Realm
A distributed, event-based tasking library
Loading...
Searching...
No Matches
redop.h
Go to the documentation of this file.
1/*
2 * Copyright 2025 Stanford University, NVIDIA Corporation
3 * SPDX-License-Identifier: Apache-2.0
4 *
5 * Licensed under the Apache License, Version 2.0 (the "License");
6 * you may not use this file except in compliance with the License.
7 * You may obtain a copy of the License at
8 *
9 * http://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
16 */
17
18// reduction ops for Realm
19
20#ifndef REALM_REDOP_H
21#define REALM_REDOP_H
22
23#include "realm/realm_config.h"
24
25#ifdef REALM_USE_CUDA
27#endif
28
29#ifdef REALM_USE_HIP
30#include "realm/hip/hip_redop.h"
31#endif
32
33#include <cstddef>
34#include <type_traits>
35
36namespace Realm {
37
38 // a reduction op needs to look like this
39#ifdef NOT_REALLY_CODE
40 class MyReductionOp {
41 public:
42 typedef int LHS;
43 typedef int RHS;
44
45 void apply(LHS &lhs, RHS rhs) const;
46
47 // both of these are optional
48 static const RHS identity;
49 void fold(RHS &rhs1, RHS rhs2) const;
50 };
51#endif
52
53 template <typename REDOP>
54 struct ReductionOp;
55
57 size_t sizeof_this; // includes any identity val or user data after struct
58 size_t sizeof_lhs;
59 size_t sizeof_rhs;
60 size_t sizeof_userdata; // extra data supplied to apply/fold
61 void *identity; // if non-null, points into same object
62 void *userdata; // if non-null, points into same object
63
64 // CPU apply/fold functions - tolerate strided src/dst
65 void (*cpu_apply_excl_fn)(void *lhs_ptr, size_t lhs_stride, const void *rhs_ptr,
66 size_t rhs_stride, size_t count, const void *userdata);
67 void (*cpu_apply_nonexcl_fn)(void *lhs_ptr, size_t lhs_stride, const void *rhs_ptr,
68 size_t rhs_stride, size_t count, const void *userdata);
69 void (*cpu_fold_excl_fn)(void *rhs1_ptr, size_t rhs1_stride, const void *rhs2_ptr,
70 size_t rhs2_stride, size_t count, const void *userdata);
71 void (*cpu_fold_nonexcl_fn)(void *rhs1_ptr, size_t rhs1_stride, const void *rhs2_ptr,
72 size_t rhs2_stride, size_t count, const void *userdata);
73
74#ifdef REALM_USE_CUDA
75 // CUDA kernels for apply/fold - these are not actually the functions,
76 // but just information (e.g. host wrapper fnptr) that can be used
77 // to look up the actual kernels
78 void *cuda_apply_excl_fn, *cuda_apply_nonexcl_fn;
79 void *cuda_fold_excl_fn, *cuda_fold_nonexcl_fn;
80 void *cuda_apply_excl_fn_advanced, *cuda_apply_nonexcl_fn_advanced;
81 void *cuda_fold_excl_fn_advanced, *cuda_fold_nonexcl_fn_advanced;
82 void *cuda_apply_excl_fn_transpose, *cuda_apply_nonexcl_fn_transpose;
83 void *cuda_fold_excl_fn_transpose, *cuda_fold_nonexcl_fn_transpose;
84 // These function pointers make the connection to the app's runtime
85 // instance in order to properly translate and capture the correct
86 // function to launch.
87 // The runtime function pointer to launch these shadow symbols
88 void *cudaLaunchKernel_fn;
89 // The runtime function pointer to translate the host shadow symbol to a driver
90 // function
91 void *cudaGetFuncBySymbol_fn;
92#endif
93#ifdef REALM_USE_HIP
94 // HIP kernels for apply/fold - these are not actually the functions,
95 // but just information (e.g. host wrapper fnptr) that can be used
96 // to look up the actual kernels
97 void *hip_apply_excl_fn, *hip_apply_nonexcl_fn;
98 void *hip_fold_excl_fn, *hip_fold_nonexcl_fn;
99 void *hip_apply_excl_fn_advanced, *hip_apply_nonexcl_fn_advanced;
100 void *hip_fold_excl_fn_advanced, *hip_fold_nonexcl_fn_advanced;
101 void *hip_apply_excl_fn_transpose, *hip_apply_nonexcl_fn_transpose;
102 void *hip_fold_excl_fn_transpose, *hip_fold_nonexcl_fn_transpose;
103#endif
104
107 , sizeof_lhs(0)
108 , sizeof_rhs(0)
109 , sizeof_userdata(0)
110 , identity(0)
111 , userdata(0)
116#ifdef REALM_USE_CUDA
117 , cuda_apply_excl_fn(0)
118 , cuda_apply_nonexcl_fn(0)
119 , cuda_fold_excl_fn(0)
120 , cuda_fold_nonexcl_fn(0)
121 , cuda_apply_excl_fn_advanced(0)
122 , cuda_apply_nonexcl_fn_advanced(0)
123 , cuda_fold_excl_fn_advanced(0)
124 , cuda_fold_nonexcl_fn_advanced(0)
125 , cuda_apply_excl_fn_transpose(0)
126 , cuda_apply_nonexcl_fn_transpose(0)
127 , cuda_fold_excl_fn_transpose(0)
128 , cuda_fold_nonexcl_fn_transpose(0)
129 , cudaLaunchKernel_fn(0)
130 , cudaGetFuncBySymbol_fn(0)
131#endif
132#ifdef REALM_USE_HIP
133 , hip_apply_excl_fn(0)
134 , hip_apply_nonexcl_fn(0)
135 , hip_fold_excl_fn(0)
136 , hip_fold_nonexcl_fn(0)
137 , hip_apply_excl_fn_advanced(0)
138 , hip_apply_nonexcl_fn_advanced(0)
139 , hip_fold_excl_fn_advanced(0)
140 , hip_fold_nonexcl_fn_advanced(0)
141 , hip_apply_excl_fn_transpose(0)
142 , hip_apply_nonexcl_fn_transpose(0)
143 , hip_fold_excl_fn_transpose(0)
144 , hip_fold_nonexcl_fn_transpose(0)
145#endif
146 {}
147
148 template <class REDOP>
150 {
151 // reduction ops are allowed to use helper constructors, but are
152 // type-erased inside of realm, so must be trivially copyable and
153 // trivially destructible (we will use malloc/memcpy/free instead
154 // of new/delete)
155 // FIXME:
156 // TODO:
157 // Re-enable when the examples used in legion CI are fixed.
158#if 0
159 static_assert(std::is_trivially_copyable<ReductionOp<REDOP>>::value &&
160 std::is_trivially_destructible<ReductionOp<REDOP>>::value,
161 "ReductionOp<REDOP> must be trivially copyable/destructible");
162#endif
163 void *ptr = malloc(sizeof(ReductionOp<REDOP>));
164 if(ptr) {
165 ReductionOpUntyped *redop = new(ptr) ReductionOp<REDOP>;
166 return redop;
167 } else
168 return nullptr;
169 }
170
172 };
173
174 namespace ReductionKernels {
175 template <typename REDOP, bool EXCL>
176 void cpu_apply_wrapper(void *lhs_ptr, size_t lhs_stride, const void *rhs_ptr,
177 size_t rhs_stride, size_t count, const void *userdata)
178 {
179 const REDOP *redop = static_cast<const REDOP *>(userdata);
180 for(size_t i = 0; i < count; i++) {
181 redop->template apply<EXCL>(*static_cast<typename REDOP::LHS *>(lhs_ptr),
182 *static_cast<const typename REDOP::RHS *>(rhs_ptr));
183 lhs_ptr = static_cast<char *>(lhs_ptr) + lhs_stride;
184 rhs_ptr = static_cast<const char *>(rhs_ptr) + rhs_stride;
185 }
186 }
187
188 template <typename REDOP, bool EXCL>
189 void cpu_fold_wrapper(void *rhs1_ptr, size_t rhs1_stride, const void *rhs2_ptr,
190 size_t rhs2_stride, size_t count, const void *userdata)
191 {
192 const REDOP *redop = static_cast<const REDOP *>(userdata);
193 for(size_t i = 0; i < count; i++) {
194 redop->template fold<EXCL>(*static_cast<typename REDOP::RHS *>(rhs1_ptr),
195 *static_cast<const typename REDOP::RHS *>(rhs2_ptr));
196 rhs1_ptr = static_cast<char *>(rhs1_ptr) + rhs1_stride;
197 rhs2_ptr = static_cast<const char *>(rhs2_ptr) + rhs2_stride;
198 }
199 }
200 }; // namespace ReductionKernels
201
202#if defined(REALM_USE_CUDA) && defined(__CUDACC__)
203 // with a cuda-capable compiler, we'll automatically add cuda reduction
204 // kernels if the REDOP class defines has_cuda_reductions AND it's true
205 // this requires a bunch of SFINAE template-fu
206 template <typename T>
207 struct HasHasCudaReductions {
208 struct YES {
209 char dummy[1];
210 };
211 struct NO {
212 char dummy[2];
213 };
214 struct AltnerativeDefinition {
215 static const bool has_cuda_reductions = false;
216 };
217 template <typename T2>
218 struct Combined : public T2, public AltnerativeDefinition {};
219 template <typename T2, T2>
220 struct CheckAmbiguous {};
221 template <typename T2>
222 static NO
223 has_member(CheckAmbiguous<const bool *, &Combined<T2>::has_cuda_reductions> *);
224 template <typename T2>
225 static YES has_member(...);
226 const static bool value = sizeof(has_member<T>(0)) == sizeof(YES);
227 };
228
229 template <typename T, bool OK>
230 struct MaybeAddCudaReductions;
231 template <typename T>
232 struct MaybeAddCudaReductions<T, false> {
233 static void if_member_exists(ReductionOpUntyped *redop){};
234 static void if_member_is_true(ReductionOpUntyped *redop){};
235 };
236 template <typename T>
237 struct MaybeAddCudaReductions<T, true> {
238 static void if_member_exists(ReductionOpUntyped *redop)
239 {
240 MaybeAddCudaReductions<T, T::has_cuda_reductions>::if_member_is_true(redop);
241 }
242 static void if_member_is_true(ReductionOpUntyped *redop)
243 {
244 Cuda::add_cuda_redop_kernels<T>(redop);
245 }
246 };
247#endif
248
249#if defined(REALM_USE_HIP) && (defined(__CUDACC__) || defined(__HIPCC__))
250 // with a hip-capable compiler, we'll automatically add hip reduction
251 // kernels if the REDOP class defines has_hip_reductions AND it's true
252 // this requires a bunch of SFINAE template-fu
253 template <typename T>
254 struct HasHasHipReductions {
255 struct YES {
256 char dummy[1];
257 };
258 struct NO {
259 char dummy[2];
260 };
261 struct AltnerativeDefinition {
262 static const bool has_hip_reductions = false;
263 };
264 template <typename T2>
265 struct Combined : public T2, public AltnerativeDefinition {};
266 template <typename T2, T2>
267 struct CheckAmbiguous {};
268 template <typename T2>
269 static NO
270 has_member(CheckAmbiguous<const bool *, &Combined<T2>::has_hip_reductions> *);
271 template <typename T2>
272 static YES has_member(...);
273 const static bool value = sizeof(has_member<T>(0)) == sizeof(YES);
274 };
275
276 template <typename T, bool OK>
277 struct MaybeAddHipReductions;
278 template <typename T>
279 struct MaybeAddHipReductions<T, false> {
280 static void if_member_exists(ReductionOpUntyped *redop){};
281 static void if_member_is_true(ReductionOpUntyped *redop){};
282 };
283 template <typename T>
284 struct MaybeAddHipReductions<T, true> {
285 static void if_member_exists(ReductionOpUntyped *redop)
286 {
287 MaybeAddHipReductions<T, T::has_hip_reductions>::if_member_is_true(redop);
288 }
289 static void if_member_is_true(ReductionOpUntyped *redop)
290 {
291 Hip::add_hip_redop_kernels<T>(redop);
292 }
293 };
294#endif
295
296 template <typename REDOP>
298 // tacked on to end of ReductionOpUntyped struct
299 typename REDOP::RHS identity_val;
301
303 : identity_val(REDOP::identity)
304 , userdata_val()
305 {
307 sizeof_lhs = sizeof(typename REDOP::LHS);
308 sizeof_rhs = sizeof(typename REDOP::RHS);
309 sizeof_userdata = sizeof(REDOP);
312 cpu_apply_excl_fn = &ReductionKernels::cpu_apply_wrapper<REDOP, true>;
313 cpu_apply_nonexcl_fn = &ReductionKernels::cpu_apply_wrapper<REDOP, false>;
314 cpu_fold_excl_fn = &ReductionKernels::cpu_fold_wrapper<REDOP, true>;
315 cpu_fold_nonexcl_fn = &ReductionKernels::cpu_fold_wrapper<REDOP, false>;
316#if defined(REALM_USE_CUDA) && defined(__CUDACC__)
317 // if REDOP defines/sets 'has_cuda_reductions' to true, try to
318 // automatically build wrappers for apply_cuda<> and fold_cuda<>
319 MaybeAddCudaReductions<REDOP, HasHasCudaReductions<REDOP>::value>::if_member_exists(
320 this);
321#endif
322#if defined(REALM_USE_HIP) && (defined(__CUDACC__) || defined(__HIPCC__))
323 // if REDOP defines/sets 'has_hip_reductions' to true, try to
324 // automatically build wrappers for apply_hip<> and fold_hip<>
325 MaybeAddHipReductions<REDOP, HasHasHipReductions<REDOP>::value>::if_member_exists(
326 this);
327#endif
328 }
329
330 protected:
331 };
332
333}; // namespace Realm
334
335 // include "redop.inl"
336
337#endif // ifndef REALM_REDOP_H
Realm::ReductionOp< REDOP > ReductionOp
Definition prealm.h:88
void cpu_apply_wrapper(void *lhs_ptr, size_t lhs_stride, const void *rhs_ptr, size_t rhs_stride, size_t count, const void *userdata)
Definition redop.h:176
void cpu_fold_wrapper(void *rhs1_ptr, size_t rhs1_stride, const void *rhs2_ptr, size_t rhs2_stride, size_t count, const void *userdata)
Definition redop.h:189
Definition activemsg.h:42
Definition redop.h:56
size_t sizeof_lhs
Definition redop.h:58
static ReductionOpUntyped * create_reduction_op(void)
Definition redop.h:149
void(* cpu_apply_nonexcl_fn)(void *lhs_ptr, size_t lhs_stride, const void *rhs_ptr, size_t rhs_stride, size_t count, const void *userdata)
Definition redop.h:67
size_t sizeof_this
Definition redop.h:57
void(* cpu_fold_nonexcl_fn)(void *rhs1_ptr, size_t rhs1_stride, const void *rhs2_ptr, size_t rhs2_stride, size_t count, const void *userdata)
Definition redop.h:71
ReductionOpUntyped()
Definition redop.h:105
static ReductionOpUntyped * clone_reduction_op(const ReductionOpUntyped *redop)
void * identity
Definition redop.h:61
void * userdata
Definition redop.h:62
size_t sizeof_rhs
Definition redop.h:59
size_t sizeof_userdata
Definition redop.h:60
void(* cpu_fold_excl_fn)(void *rhs1_ptr, size_t rhs1_stride, const void *rhs2_ptr, size_t rhs2_stride, size_t count, const void *userdata)
Definition redop.h:69
void(* cpu_apply_excl_fn)(void *lhs_ptr, size_t lhs_stride, const void *rhs_ptr, size_t rhs_stride, size_t count, const void *userdata)
Definition redop.h:65
Definition redop.h:297
ReductionOp()
Definition redop.h:302
REDOP userdata_val
Definition redop.h:300
REDOP::RHS identity_val
Definition redop.h:299