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