DDC 0.10.0
Loading...
Searching...
No Matches
discrete_space.hpp
1// Copyright (C) The DDC development team, see COPYRIGHT.md file
2//
3// SPDX-License-Identifier: MIT
4
5#pragma once
6
7#include <cassert>
8#include <cstddef>
9#include <functional>
10#include <map>
11#include <optional>
12#include <ostream>
13#include <stdexcept>
14#include <string>
15#include <tuple>
16#include <type_traits>
17#include <typeinfo>
18#include <utility>
19
20#include <Kokkos_Core.hpp>
21
22#include "detail/dual_discretization.hpp"
23#include "detail/macros.hpp"
24
25#if defined(KOKKOS_ENABLE_CUDA)
26# include <sstream>
27
28# include <cuda.h>
29#elif defined(KOKKOS_ENABLE_HIP)
30# include <sstream>
31
32# include <hip/hip_runtime.h>
33#endif
34
35#if defined(KOKKOS_ENABLE_CUDA) || defined(KOKKOS_ENABLE_HIP)
36# define DDC_DETAIL_DEVICE_THROW_ON_ERROR(val)
37 ddc::detail::device_throw_on_error((val), #val, __FILE__, __LINE__)
38#endif
39
40namespace ddc {
41
42namespace detail {
43
44#if defined(KOKKOS_ENABLE_CUDA)
45inline void device_throw_on_error(
46 cudaError_t const err,
47 const char* const func,
48 const char* const file,
49 const int line)
50{
51 if (err != cudaSuccess) {
52 std::stringstream ss;
53 ss << "CUDA Runtime Error at: " << file << ":" << line << "\n";
54 ss << cudaGetErrorString(err) << " " << func << "\n";
55 throw std::runtime_error(ss.str());
56 }
57}
58#elif defined(KOKKOS_ENABLE_HIP)
59inline void device_throw_on_error(
60 hipError_t const err,
61 const char* const func,
62 const char* const file,
63 const int line)
64{
65 if (err != hipSuccess) {
66 std::stringstream ss;
67 ss << "HIP Runtime Error at: " << file << ":" << line << "\n";
68 ss << hipGetErrorString(err) << " " << func << "\n";
69 throw std::runtime_error(ss.str());
70 }
71}
72#endif
73
74template <class DDim, class MemorySpace>
75using ddim_impl_t = typename DDim::template Impl<DDim, MemorySpace>;
76
77template <class T>
78class GpuProxy
79{
80 // Here are some reasonable concepts that T should satisfy to avoid undefined behaviors:
81 // - copy-constructible: objects may be memcopied to the device,
82 // - standard layout: objects will be ensured to have the same, standard, representation on the host and the device,
83 // - trivially destructible: the destructor of objects located on a device may not be called.
84 // static_assert(std::is_standard_layout_v<T>, "Not standard layout");
85 // static_assert(std::is_trivially_destructible_v<T>, "Not trivially destructible");
86 // static_assert(std::is_trivially_copy_constructible_v<T>, "Not trivially copy-constructible");
87 // Currently not trivially destructible because for example of the Kokkos::View (mostly reference-counting)
88 // Currently not trivially copy-constructible because of discrete spaces that have deleted copy-constructors and Kokkos::View (mostly reference-counting)
89
90private:
91 alignas(T) Kokkos::Array<std::byte, sizeof(T)> m_data;
92
93public:
94 KOKKOS_FUNCTION
95 T* operator->()
96 {
97 return reinterpret_cast<T*>(m_data.data());
98 }
99
100 KOKKOS_FUNCTION
101 T& operator*()
102 {
103 return *reinterpret_cast<T*>(m_data.data());
104 }
105
106 KOKKOS_FUNCTION
107 T* data()
108 {
109 return reinterpret_cast<T*>(m_data.data());
110 }
111};
112
113// Global CPU variable storing resetters. Required to correctly free data.
114inline std::optional<std::map<std::string, std::function<void()>>> g_discretization_store;
115
116// Global CPU variable owning discrete spaces data for CPU and GPU
117template <class DDim>
118inline std::optional<DualDiscretization<DDim>> g_discrete_space_dual;
119
120#if defined(KOKKOS_ENABLE_CUDA)
121// Global GPU variable viewing data owned by the CPU
122template <class DDim>
123__constant__ GpuProxy<ddim_impl_t<DDim, GlobalVariableDeviceSpace>> g_discrete_space_device;
124#elif defined(KOKKOS_ENABLE_HIP)
125// Global GPU variable viewing data owned by the CPU
126// WARNING: do not put the `inline` keyword, seems to fail on MI100 rocm/4.5.0
127template <class DDim>
128__constant__ GpuProxy<ddim_impl_t<DDim, GlobalVariableDeviceSpace>> g_discrete_space_device;
129#elif defined(KOKKOS_ENABLE_SYCL)
130// Global GPU variable viewing data owned by the CPU
131template <class DDim>
132SYCL_EXTERNAL inline sycl::ext::oneapi::experimental::device_global<
133 GpuProxy<ddim_impl_t<DDim, GlobalVariableDeviceSpace>>>
134 g_discrete_space_device;
135#endif
136
137inline void display_discretization_store(std::ostream& os)
138{
139 if (g_discretization_store) {
140 os << "The host discretization store is initialized:\n";
141 for (auto const& [key, value] : *g_discretization_store) {
142 os << " - " << key << "\n";
143 }
144 } else {
145 os << "The host discretization store is not initialized:\n";
146 }
147}
148
149template <class Tuple, std::size_t... Ids>
150auto extract_after(Tuple&& t, std::index_sequence<Ids...>)
151{
152 return std::make_tuple(std::move(std::get<Ids + 1>(t))...);
153}
154
155} // namespace detail
156
157/** Initialize (emplace) a global singleton discrete space
158 *
159 * @param args the constructor arguments
160 */
161template <class DDim, class... Args>
162void init_discrete_space(Args&&... args)
163{
164 static_assert(
165 !std::is_same_v<DDim, typename DDim::discrete_dimension_type>,
166 "Discrete dimensions should inherit from the discretization, not use an alias");
167 if (detail::g_discrete_space_dual<DDim>) {
168 throw std::runtime_error("Discrete space function already initialized.");
169 }
170 detail::g_discrete_space_dual<DDim>.emplace(std::forward<Args>(args)...);
171 detail::g_discretization_store->emplace(typeid(DDim).name(), []() {
172 detail::g_discrete_space_dual<DDim>.reset();
173 });
174#if defined(KOKKOS_ENABLE_CUDA)
175 DDC_DETAIL_DEVICE_THROW_ON_ERROR(cudaMemcpyToSymbol(
176 detail::g_discrete_space_device<DDim>,
177 &detail::g_discrete_space_dual<DDim>->get_device(),
178 sizeof(detail::g_discrete_space_dual<DDim>->get_device())));
179#elif defined(KOKKOS_ENABLE_HIP)
180 DDC_DETAIL_DEVICE_THROW_ON_ERROR(hipMemcpyToSymbol(
181 detail::g_discrete_space_device<DDim>,
182 &detail::g_discrete_space_dual<DDim>->get_device(),
183 sizeof(detail::g_discrete_space_dual<DDim>->get_device())));
184#elif defined(KOKKOS_ENABLE_SYCL)
185 Kokkos::DefaultExecutionSpace exec;
186 sycl::queue q = exec.sycl_queue();
187 q.memcpy(detail::g_discrete_space_device<DDim>,
188 &detail::g_discrete_space_dual<DDim>->get_device())
189 .wait();
190#endif
191}
192
193/** Move construct a global singleton discrete space and pass through the other argument
194 *
195 * @param a - the discrete space to move at index 0
196 * - the arguments to pass through at index 1
197 */
198template <class DDim, class DDimImpl, class Arg0>
199Arg0 init_discrete_space(std::tuple<DDimImpl, Arg0>&& a)
200{
201 init_discrete_space<DDim>(std::move(std::get<0>(a)));
202 return std::get<1>(a);
203}
204
205/** Move construct a global singleton discrete space and pass through remaining arguments
206 *
207 * @param a - the discrete space to move at index 0
208 * - the (2+) arguments to pass through in other indices
209 */
210template <class DDim, class DDimImpl, class Arg0, class Arg1, class... Args>
211std::tuple<Arg0, Arg1, Args...> init_discrete_space(std::tuple<DDimImpl, Arg0, Arg1, Args...>&& a)
212{
213 init_discrete_space<DDim>(std::move(std::get<0>(a)));
214 return detail::extract_after(std::move(a), std::index_sequence_for<Arg0, Arg1, Args...>());
215}
216
217/**
218 * @tparam DDim a discrete dimension
219 * @return a boolean indicating whether DDim is initialized.
220 * This function indicates whether a dimension is initialized.
221 */
222template <class DDim>
223bool is_discrete_space_initialized() noexcept
224{
225 return detail::g_discrete_space_dual<DDim>.has_value();
226}
227
228/**
229 * @tparam DDim a discrete dimension
230 * @return the discrete space instance associated with `DDim`.
231 * This function must be called from a `KOKKOS_FUNCTION`.
232 * Call `ddc::host_discrete_space` for a host-only function instead.
233 */
234template <class DDim, class MemorySpace = DDC_CURRENT_KOKKOS_SPACE>
235KOKKOS_FUNCTION detail::ddim_impl_t<DDim, MemorySpace> const& discrete_space()
236{
237 // This function requires that `ddc::init_discrete_space<DDim>(...);` be called first
238 if constexpr (std::is_same_v<MemorySpace, Kokkos::HostSpace>) {
239 KOKKOS_ASSERT(is_discrete_space_initialized<DDim>())
240 return detail::g_discrete_space_dual<DDim>->get_host();
241 }
242#if defined(KOKKOS_ENABLE_CUDA) || defined(KOKKOS_ENABLE_HIP)
243 else if constexpr (std::is_same_v<MemorySpace, detail::GlobalVariableDeviceSpace>) {
244 return *detail::g_discrete_space_device<DDim>;
245 }
246#elif defined(KOKKOS_ENABLE_SYCL)
247 else if constexpr (std::is_same_v<MemorySpace, detail::GlobalVariableDeviceSpace>) {
248 return *detail::g_discrete_space_device<DDim>.get();
249 }
250#endif
251 else {
252 static_assert(std::is_same_v<MemorySpace, MemorySpace>, "Memory space not handled");
253 }
254}
255
256template <class DDim>
257detail::ddim_impl_t<DDim, Kokkos::HostSpace> const& host_discrete_space()
258{
259 // This function requires that `ddc::init_discrete_space<DDim>(...);` be called first
260 assert(is_discrete_space_initialized<DDim>());
261 return detail::g_discrete_space_dual<DDim>->get_host();
262}
263
264} // namespace ddc
265
266#if defined(KOKKOS_ENABLE_CUDA) || defined(KOKKOS_ENABLE_HIP)
267# undef DDC_DETAIL_DEVICE_THROW_ON_ERROR
268#endif
The top-level namespace of DDC.
bool is_discrete_space_initialized() noexcept
void init_discrete_space(Args &&... args)
Initialize (emplace) a global singleton discrete space.
detail::ddim_impl_t< DDim, Kokkos::HostSpace > const & host_discrete_space()
KOKKOS_FUNCTION detail::ddim_impl_t< DDim, MemorySpace > const & discrete_space()
Arg0 init_discrete_space(std::tuple< DDimImpl, Arg0 > &&a)
Move construct a global singleton discrete space and pass through the other argument.
std::tuple< Arg0, Arg1, Args... > init_discrete_space(std::tuple< DDimImpl, Arg0, Arg1, Args... > &&a)
Move construct a global singleton discrete space and pass through remaining arguments.