NeoN
A framework for CFD software
Loading...
Searching...
No Matches
parallelAlgorithms.hpp
Go to the documentation of this file.
1// SPDX-FileCopyrightText: 2023 - 2026 NeoN authors
2//
3// SPDX-License-Identifier: MIT
4
5#pragma once
6
7#include <Kokkos_Core.hpp>
8#include <type_traits>
9
10#include "NeoN/core/logging.hpp"
13
14#ifdef NN_WITH_KOKKOS
15#define NEON_LAMBDA KOKKOS_LAMBDA
16#define NEON_INLINE_FUNCTION KOKKOS_INLINE_FUNCTION
17namespace NeoN
18{
19// just pull Kokkos::atomic_* functions into NeoN namespace
20using Kokkos::atomic_add;
21using Kokkos::atomic_sub;
22}
23#else
24#define NEON_LAMBDA [&]
25namespace NeoN
26{
27// using atomic_add = [](auto& a, auto b){a+b;};
28// using atomic_sub = [](auto& a, auto b){a-b;};
29}
30#endif
31
32namespace NeoN
33{
34
35
36template<typename ValueType>
37class Vector;
38
39
40// Concept to check if a callable is compatible with void(const size_t)
41template<typename Kernel>
42concept parallelForKernel = requires(Kernel t, size_t i) {
43 {
44 t(i)
45 } -> std::same_as<void>;
46};
47
48
49/* @brief calls fence if a logger is set */
50template<typename ExecutorType>
51void fenceIfLogger(const ExecutorType& exec)
52{
53 auto logger = getLogger(exec);
54 if (logger != nullptr)
55 {
56 fence(exec);
57 }
58}
59
60/* @brief execute parallelFor with concrete executor */
61template<typename ExecutorType, parallelForKernel Kernel>
63 const ExecutorType&, std::pair<localIdx, localIdx> range, const Kernel& kernel, std::string name
64)
65{
66 auto [start, end] = range;
67
68 if constexpr (std::is_same<std::remove_reference_t<ExecutorType>, SerialExecutor>::value)
69 {
70 for (localIdx i = start; i < end; i++)
71 {
72 kernel(i);
73 }
74 }
75 else
76 {
77 using runOn = typename ExecutorType::exec;
78 // Pass `kernel` DIRECTLY to Kokkos (do not wrap it in another NEON_LAMBDA). The wrapper
79 // makes `kernel`'s type only ever HOST-copied (into the wrapper's closure) and never the
80 // type actually device-launched, so nvcc emits no — or, under -O3, a NULL — host copy
81 // trampoline (__nv_hdl_wrapper_t::do_copy) for it; the host-side copy then jumps to 0x0
82 // and SIGSEGVs. Launching `kernel` directly makes it the device-launched type, forcing
83 // nvcc to emit a correct trampoline. See neon-nvcc-extended-lambda-trampoline.
84 Kokkos::parallel_for(
85 name, Kokkos::RangePolicy<runOn, Kokkos::IndexType<localIdx>>(start, end), kernel
86 );
87 }
88}
89
90
91/* @brief dispatch parallelFor based on executor variant type */
92template<parallelForKernel Kernel>
94 const NeoN::Executor& exec,
95 std::pair<localIdx, localIdx> range,
96 const Kernel& kernel,
97 std::string name = "parallelFor"
98)
99{
100 std::visit([&](const auto& e) { parallelFor(e, range, kernel, name); }, exec);
101}
102
103// Concept to check if a callable is compatible with ValueType(const size_t)
104template<typename Kernel, typename ValueType>
105concept parallelForContainerKernel = requires(Kernel t, ValueType val, size_t i) {
106 {
107 t(i)
108 } -> std::same_as<ValueType>;
109};
110
111template<
112 typename Executor,
113 template<typename>
114 class ContType,
115 typename ValueType,
118 const Executor&,
119 ContType<ValueType>& container,
120 const Kernel& kernel,
121 std::string name = "parallelFor"
122)
123{
124 auto view = container.view();
125 if constexpr (std::is_same<std::remove_reference_t<Executor>, SerialExecutor>::value)
126 {
127 for (localIdx i = 0; i < view.size(); i++)
128 {
129 view[i] = kernel(i);
130 }
131 }
132 else
133 {
134 using runOn = typename Executor::exec;
135 Kokkos::parallel_for(
136 name,
137 Kokkos::RangePolicy<runOn>(0, view.size()),
138 NEON_LAMBDA(const localIdx i) { view[i] = kernel(i); }
139 );
140 }
141}
142
143template<
144 template<typename>
145 class ContType,
146 typename ValueType,
147 parallelForContainerKernel<ValueType> Kernel>
148void parallelFor(ContType<ValueType>& cont, const Kernel& kernel, std::string name = "parallelFor")
149{
150 std::visit([&](const auto& e) { parallelFor(e, cont, kernel, name); }, cont.exec());
151}
152
153template<typename Executor, typename Kernel, typename T>
155 [[maybe_unused]] const Executor& exec,
156 std::pair<localIdx, localIdx> range,
157 const Kernel& kernel,
158 T& value
159)
160{
161 auto [start, end] = range;
162 if constexpr (std::is_same<std::remove_reference_t<Executor>, SerialExecutor>::value)
163 {
164 for (localIdx i = start; i < end; i++)
165 {
166 if constexpr (Kokkos::is_reducer<T>::value)
167 {
168 kernel(i, value.reference());
169 }
170 else
171 {
172 kernel(i, value);
173 }
174 }
175 }
176 else
177 {
178 using runOn = typename Executor::exec;
179 Kokkos::parallel_reduce(
180 "parallelReduce", Kokkos::RangePolicy<runOn>(start, end), kernel, value
181 );
182 }
183}
184
185template<typename Kernel, typename T>
187 const NeoN::Executor& exec, std::pair<localIdx, localIdx> range, const Kernel& kernel, T& value
188)
189{
190 std::visit([&](const auto& e) { parallelReduce(e, range, kernel, value); }, exec);
191}
192
193
194template<typename Executor, typename ValueType, typename Kernel, typename T>
196 [[maybe_unused]] const Executor& exec, Vector<ValueType>& field, const Kernel& kernel, T& value
197)
198{
199 if constexpr (std::is_same<std::remove_reference_t<Executor>, SerialExecutor>::value)
200 {
201 localIdx fieldSize = field.size();
202 for (localIdx i = 0; i < fieldSize; i++)
203 {
204 if constexpr (Kokkos::is_reducer<T>::value)
205 {
206 kernel(i, value.reference());
207 }
208 else
209 {
210 kernel(i, value);
211 }
212 }
213 }
214 else
215 {
216 using runOn = typename Executor::exec;
217 Kokkos::parallel_reduce(
218 "parallelReduce", Kokkos::RangePolicy<runOn>(0, field.size()), kernel, value
219 );
220 }
221}
222
223template<typename ValueType, typename Kernel, typename T>
224void parallelReduce(Vector<ValueType>& field, const Kernel& kernel, T& value)
225{
226 std::visit([&](const auto& e) { parallelReduce(e, field, kernel, value); }, field.exec());
227}
228
229template<typename Executor, typename Kernel>
231 [[maybe_unused]] const Executor& exec, std::pair<localIdx, localIdx> range, const Kernel& kernel
232)
233{
234 auto [start, end] = range;
235 using runOn = typename Executor::exec;
236 Kokkos::parallel_scan("parallelScan", Kokkos::RangePolicy<runOn>(start, end), kernel);
237}
238
239template<typename Kernel>
241 const NeoN::Executor& exec, std::pair<localIdx, localIdx> range, const Kernel& kernel
242)
243{
244 std::visit([&](const auto& e) { parallelScan(e, range, kernel); }, exec);
245}
246
247template<typename Executor, typename Kernel, typename ReturnType>
249 [[maybe_unused]] const Executor& exec,
250 std::pair<localIdx, localIdx> range,
251 const Kernel& kernel,
252 ReturnType& returnValue
253)
254{
255 auto [start, end] = range;
256 using runOn = typename Executor::exec;
257 Kokkos::parallel_scan(
258 "parallelScan", Kokkos::RangePolicy<runOn>(start, end), kernel, returnValue
259 );
260}
261
262template<typename Kernel, typename ReturnType>
264 const NeoN::Executor& exec,
265 std::pair<localIdx, localIdx> range,
266 const Kernel& kernel,
267 ReturnType& returnValue
268)
269{
270 std::visit([&](const auto& e) { parallelScan(e, range, kernel, returnValue); }, exec);
271}
272
273} // namespace NeoN
Reference executor for serial CPU execution.
A class to contain the data and executors for a field and define some basic operations.
Definition vector.hpp:27
localIdx size() const
Gets the size of the field.
Definition vector.hpp:268
const Executor & exec() const
Gets the executor associated with the field.
Definition vector.hpp:262
Integer types used throughout NeoN.
Definition array.hpp:18
void fenceIfLogger(const ExecutorType &exec)
void fence(const Executor &exec)
Definition executor.hpp:23
std::shared_ptr< const Logging::BaseLogger > getLogger(const Executor &exec)
Definition executor.hpp:71
int32_t localIdx
Definition label.hpp:50
void parallelScan(const Executor &exec, std::pair< localIdx, localIdx > range, const Kernel &kernel)
std::variant< SerialExecutor, CPUExecutor, GPUExecutor > Executor
Definition executor.hpp:20
void parallelFor(const ExecutorType &, std::pair< localIdx, localIdx > range, const Kernel &kernel, std::string name)
const std::string & name(const NeoN::Document &doc)
Retrieves the name of a Document.
void parallelReduce(const Executor &exec, std::pair< localIdx, localIdx > range, const Kernel &kernel, T &value)
#define NEON_LAMBDA