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