9.2
general documentation
cs_math_hip.h
Go to the documentation of this file.
1/*============================================================================
2 * Math functions for HIP.
3 *============================================================================*/
4
5/*
6 This file is part of code_saturne, a general-purpose CFD tool.
7
8 Copyright (C) 1998-2026 EDF S.A.
9
10 This program is free software; you can redistribute it and/or modify it under
11 the terms of the GNU General Public License as published by the Free Software
12 Foundation; either version 2 of the License, or (at your option) any later
13 version.
14
15 This program is distributed in the hope that it will be useful, but WITHOUT
16 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
17 FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
18 details.
19
20 You should have received a copy of the GNU General Public License along with
21 this program; if not, write to the Free Software Foundation, Inc., 51 Franklin
22 Street, Fifth Floor, Boston, MA 02110-1301, USA.
23*/
24
25/*----------------------------------------------------------------------------*/
26
27#pragma once
28
29#include "base/cs_defs.h"
30
31/*----------------------------------------------------------------------------
32 * Standard C/C++/HIP library headers
33 *----------------------------------------------------------------------------*/
34
35#include <float.h>
36#include "hip/hip_runtime.h"
37#include <hip/hip_runtime_api.h>
38
39/*----------------------------------------------------------------------------
40 * Local headers
41 *----------------------------------------------------------------------------*/
42
43#include "base/cs_base_hip.h"
44
45/*=============================================================================
46 * Semi-private inline functions
47 *============================================================================*/
48
49/*----------------------------------------------------------------------------*/
63/*----------------------------------------------------------------------------*/
64
65template <class V>
66__device__ cs_hip_mask_t
67_conflict_mask(cs_hip_mask_t mask,
68 V v) noexcept
69{
70 // It seems that although the dunder warp functions are available for
71 // ROCm 6.2, they may be disabled...
72 // Documentation indicates that it is activated by default on ROCm 7.0
73#if (HIP_VERSION >= 60200000 && defined(HIP_ENABLE_WARP_SYNC_BUILTINS)) \
74 || (HIP_VERSION >= 70000000 && !defined(HIP_DISABLE_WARP_SYNC_BUILTINS))
75 return __match_any_sync(mask, v);
76#else
77 cs_hip_mask_t lanemask_eq = 1u << (threadIdx.x % CS_HIP_WARP_SIZE);
78
79 if (!(mask & lanemask_eq))
80 return 0;
81
82 cs_hip_mask_t ref, ballot;
83 int leader;
84
85 goto entry;
86
87loop:
88 mask &= ~ballot;
89
90entry:
91 // Handle different warp sizes
92#if CS_HIP_WARP_SIZE == 64
93// ffsll seems to have problems distinguishing between long long int and
94// long long unsigned...
95// leader = __ffsll(mask) - 1;
96 leader = ( mask == 0 ? -1 : __builtin_ctzll(mask) );
97#else
98 leader = __ffs(mask) - 1;
99#endif
100
101 ref = __shfl(v, leader);
102 ballot = __ballot(v == ref);
103
104 if (!(ballot & lanemask_eq))
105 goto loop;
106
107 return ballot;
108#endif
109}
110
111/*----------------------------------------------------------------------------*/
120/*----------------------------------------------------------------------------*/
121
122template <class T>
123__device__ bool
124_reduce_add(cs_hip_mask_t mask,
125 cs_hip_mask_t peers,
126 T &v) noexcept
127{
128 int laneid = __lane_id(); // Ensure portability depending on warpSize for AMD
129 cs_hip_mask_t lanemask_lt = (1u << laneid) - 1;
130 cs_hip_mask_t lanemask_gt = -2u << laneid;
131
132#if CS_HIP_WARP_SIZE == 64
133 int rank = __popcll(peers & lanemask_lt);
134#else
135 int rank = __popc(peers & lanemask_lt);
136#endif
137
138 bool is_leader = (rank == 0);
139
140 peers &= lanemask_gt;
141
142 while (__ballot(peers != 0)) {
143#if CS_HIP_WARP_SIZE == 64
144 int next = ( peers == 0 ? -1 : __builtin_ctzll(peers) ) + 1;
145// ffsll seems to have problems distinguishing between long long int and
146// long long unsigned...
147// int next = __ffsll(peers); // 1-based index
148#else
149 int next = __ffs(peers); // 1-based index
150#endif
151
152 auto tmp = v.shuffle(mask, next - 1);
153 if (next) {
154 v.add(tmp);
155 }
156
157 peers &= __ballot(!(rank & 1));
158 rank >>= 1;
159 }
160
161 return is_leader;
162}
163
164/*----------------------------------------------------------------------------*/
169/*----------------------------------------------------------------------------*/
170
171template <class T, size_t...>
173
174private:
175 T value = {};
176
177public:
178 using inner_type = T;
179
180public:
181
182 // __device__
183 assembled_value() noexcept = default;
184
185 __device__
186 assembled_value(T val) noexcept : value(val)
187 {
188 }
189
190 __device__ void
191 add(const assembled_value &restrict other) restrict noexcept
192 {
193 value += other.value;
194 }
195
196 __device__ void
198 {
199 atomicAdd(&value, other.value);
200 }
201
202 __device__ assembled_value
204 {
205 assembled_value previous = *this;
206 *this = other;
207 return previous;
208 }
209
210 __device__ assembled_value
212 {
213 return assembled_value(atomicExch(&value, other.value));
214 }
215
216 __device__ assembled_value
217 shuffle(cs_hip_mask_t mask, unsigned laneid) const noexcept
218 {
219#if HIP_VERSION >= 60200000 && defined(HIP_ENABLE_WARP_SYNC_BUILTINS)
220 return assembled_value(__shfl_sync(mask, value, laneid));
221#else
222 CS_UNUSED(mask);
223 return assembled_value(__shfl(value, laneid));
224#endif
225 }
226
227 __device__ cs_hip_mask_t
228 conflict_mask(cs_hip_mask_t mask) const noexcept
229 {
230 return _conflict_mask(mask, (uintptr_t)this);
231 }
232
233 __device__ bool
234 reduce_add(cs_hip_mask_t mask, cs_hip_mask_t peers) noexcept
235 {
236 return _reduce_add(mask, peers, *this);
237 }
238
239 __device__ void
240 conflict_free_add(cs_hip_mask_t mask, assembled_value other) noexcept
241 {
242 cs_hip_mask_t peers = conflict_mask(mask);
243 if (other.reduce_add(mask, peers)) {
244 atomic_add(other);
245 }
246 }
247
248 __device__ inner_type &
249 operator*() noexcept
250 {
251 return value;
252 }
253
254 __device__ inner_type const &
255 operator*() const noexcept
256 {
257 return value;
258 }
259
260 __device__ inner_type *
261 operator->() noexcept
262 {
263 return &value;
264 }
265
266 __device__ inner_type const *
267 operator->() const noexcept
268 {
269 return &value;
270 }
271
272 __device__ inner_type &
273 get() noexcept
274 {
275 return value;
276 }
277
278 __device__ inner_type const &
279 get() const noexcept
280 {
281 return value;
282 }
283
284 static __device__ assembled_value &
285 ref(inner_type &r) noexcept
286 {
287 return reinterpret_cast<assembled_value &>(r);
288 }
289
290 static __device__ assembled_value const &
291 ref(inner_type const &r) noexcept
292 {
293 return reinterpret_cast<assembled_value const &>(r);
294 }
295};
296
297template <class T, size_t Head, size_t... Tail>
298class assembled_value<T, Head, Tail...> {
299
300private:
301 assembled_value<T, Tail...> data[Head];
302
303public:
304 using inner_type = typename assembled_value<T, Tail...>::inner_type[Head];
305
306public:
307 // __device__ assembled_value() noexcept = default;
308
309 __device__ void
310 add(const assembled_value &restrict other) restrict noexcept
311 {
312 for (size_t i = 0; i < Head; ++i) {
313 data[i].add(other.data[i]);
314 }
315 }
316
317 __device__ void
319 {
320 for (size_t i = 0; i < Head; ++i) {
321 data[i].atomic_add(other.data[i]);
322 }
323 }
324
325 __device__ assembled_value
327 {
328 assembled_value previous;
329 for (size_t i = 0; i < Head; ++i) {
330 previous.data[i] = data[i].exchange(other.data[i]);
331 }
332 return previous;
333 }
334
335 __device__ assembled_value
337 {
338 assembled_value previous;
339 for (size_t i = 0; i < Head; ++i) {
340 previous.data[i] = data[i].atomic_exchange(other.data[i]);
341 }
342 return previous;
343 }
344
345 __device__ assembled_value
346 shuffle(cs_hip_mask_t mask, unsigned laneid) const noexcept
347 {
348 assembled_value shuffled;
349 for (size_t i = 0; i < Head; ++i) {
350 shuffled.data[i] = data[i].shuffle(mask, laneid);
351 }
352 return shuffled;
353 }
354
355 __device__ cs_hip_mask_t
356 conflict_mask(cs_hip_mask_t mask) const noexcept
357 {
358 return _conflict_mask(mask, (uintptr_t)this);
359 }
360
361 __device__ bool
362 reduce_add(cs_hip_mask_t mask, cs_hip_mask_t peers) noexcept
363 {
364 return _reduce_add(mask, peers, *this);
365 }
366
367 __device__ void
368 conflict_free_add(cs_hip_mask_t mask, assembled_value other) noexcept
369 {
370 cs_hip_mask_t peers = conflict_mask(mask);
371 if (other.reduce_add(mask, peers)) {
372 atomic_add(other);
373 }
374 }
375
376 __device__ assembled_value<T, Tail...> &
377 operator[](size_t i) noexcept
378 {
379 return data[i];
380 }
381
382 __device__ assembled_value<T, Tail...> const &
383 operator[](size_t i) const noexcept
384 {
385 return data[i];
386 }
387
388 __device__ inner_type &
389 get() noexcept
390 {
391 return reinterpret_cast<inner_type &>(*this);
392 }
393
394 __device__ inner_type const &
395 get() const noexcept
396 {
397 return reinterpret_cast<inner_type const &>(*this);
398 }
399
400 static __device__ assembled_value &
401 ref(inner_type &r) noexcept
402 {
403 return reinterpret_cast<assembled_value &>(r);
404 }
405
406 static __device__ assembled_value const &
407 ref(inner_type const &r) noexcept
408 {
409 return reinterpret_cast<assembled_value const &>(r);
410 }
411};
412
413/*=============================================================================
414 * Device function definitions
415 *============================================================================*/
416
417/*----------------------------------------------------------------------------*/
426/*----------------------------------------------------------------------------*/
427
428template <typename T>
429__device__ static void
431 T out[3])
432{
433 T norm = sqrt( in[0]*in[0]
434 + in[1]*in[1]
435 + in[2]*in[2]);
436
437 T inverse_norm = 1. / norm;
438
439 out[0] = inverse_norm * in[0];
440 out[1] = inverse_norm * in[1];
441 out[2] = inverse_norm * in[2];
442}
443
444/*----------------------------------------------------------------------------*/
452/*----------------------------------------------------------------------------*/
453
454template <typename T>
455__device__ static T
457{
458 T norm = in[0]*in[0] + in[1]*in[1] + in[2]*in[2];
459 return norm;
460}
461
462/*----------------------------------------------------------------------------*/
__device__ cs_hip_mask_t conflict_mask(cs_hip_mask_t mask) const noexcept
Definition: cs_math_hip.h:356
__device__ bool reduce_add(cs_hip_mask_t mask, cs_hip_mask_t peers) noexcept
Definition: cs_math_hip.h:362
static __device__ assembled_value & ref(inner_type &r) noexcept
Definition: cs_math_hip.h:401
__device__ assembled_value exchange(const assembled_value &restrict other) restrict noexcept
Definition: cs_math_hip.h:326
__device__ assembled_value< T, Tail... > & operator[](size_t i) noexcept
Definition: cs_math_hip.h:377
__device__ void conflict_free_add(cs_hip_mask_t mask, assembled_value other) noexcept
Definition: cs_math_hip.h:368
static __device__ assembled_value const & ref(inner_type const &r) noexcept
Definition: cs_math_hip.h:407
__device__ void atomic_add(const assembled_value &restrict other) restrict noexcept
Definition: cs_math_hip.h:318
typename assembled_value< T, Tail... >::inner_type[Head] inner_type
Definition: cs_math_hip.h:304
__device__ inner_type const & get() const noexcept
Definition: cs_math_hip.h:395
__device__ assembled_value atomic_exchange(const assembled_value &restrict other) restrict noexcept
Definition: cs_math_hip.h:336
__device__ assembled_value< T, Tail... > const & operator[](size_t i) const noexcept
Definition: cs_math_hip.h:383
__device__ inner_type & get() noexcept
Definition: cs_math_hip.h:389
__device__ assembled_value shuffle(cs_hip_mask_t mask, unsigned laneid) const noexcept
Definition: cs_math_hip.h:346
__device__ void add(const assembled_value &restrict other) restrict noexcept
Definition: cs_math_hip.h:310
Class for value assembly using warp-based partial summation to reduce conflict occurences in downstre...
Definition: cs_math_hip.h:172
__device__ cs_hip_mask_t conflict_mask(cs_hip_mask_t mask) const noexcept
Definition: cs_math_hip.h:228
__device__ bool reduce_add(cs_hip_mask_t mask, cs_hip_mask_t peers) noexcept
Definition: cs_math_hip.h:234
static __device__ assembled_value & ref(inner_type &r) noexcept
Definition: cs_math_hip.h:285
__device__ assembled_value exchange(const assembled_value &restrict other) restrict noexcept
Definition: cs_math_hip.h:203
__device__ inner_type * operator->() noexcept
Definition: cs_math_hip.h:261
T inner_type
Definition: cs_math_hip.h:178
assembled_value() noexcept=default
__device__ void conflict_free_add(cs_hip_mask_t mask, assembled_value other) noexcept
Definition: cs_math_hip.h:240
__device__ inner_type const & operator*() const noexcept
Definition: cs_math_hip.h:255
__device__ inner_type const * operator->() const noexcept
Definition: cs_math_hip.h:267
static __device__ assembled_value const & ref(inner_type const &r) noexcept
Definition: cs_math_hip.h:291
__device__ void atomic_add(const assembled_value &restrict other) restrict noexcept
Definition: cs_math_hip.h:197
__device__ inner_type const & get() const noexcept
Definition: cs_math_hip.h:279
__device__ assembled_value atomic_exchange(const assembled_value &restrict other) restrict noexcept
Definition: cs_math_hip.h:211
__device__ inner_type & operator*() noexcept
Definition: cs_math_hip.h:249
__device__ inner_type & get() noexcept
Definition: cs_math_hip.h:273
__device__ assembled_value shuffle(cs_hip_mask_t mask, unsigned laneid) const noexcept
Definition: cs_math_hip.h:217
__device__ void add(const assembled_value &restrict other) restrict noexcept
Definition: cs_math_hip.h:191
#define restrict
Definition: cs_defs.h:148
#define CS_UNUSED(x)
Definition: cs_defs.h:518
static __device__ T cs_math_3_square_norm_hip(const T in[3])
Compute the square norm of a vector of 3 real values.
Definition: cs_math_hip.h:456
__device__ cs_hip_mask_t _conflict_mask(cs_hip_mask_t mask, V v) noexcept
Return the bit mask of the threads that have the same value v.
Definition: cs_math_hip.h:67
__device__ bool _reduce_add(cs_hip_mask_t mask, cs_hip_mask_t peers, T &v) noexcept
Reduction step for conflict-free addition.
Definition: cs_math_hip.h:124
static __device__ void cs_math_3_normalize_hip(const T in[3], T out[3])
Normalise a vector of 3 real values.
Definition: cs_math_hip.h:430