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