Block-Structured AMR Software Framework
Loading...
Searching...
No Matches
AMReX_Scan.H
Go to the documentation of this file.
1#ifndef AMREX_SCAN_H_
2#define AMREX_SCAN_H_
3#include <AMReX_Config.H>
4
5#include <AMReX_Extension.H>
6#include <AMReX_Gpu.H>
7#include <AMReX_Arena.H>
8
9#if defined(AMREX_USE_CUDA)
10# include <cub/cub.cuh>
11# ifdef AMREX_CUDA_CCCL_VER_GE_2_8
12# include <thrust/iterator/transform_iterator.h>
13# endif
14#elif defined(AMREX_USE_HIP)
15# include <rocprim/rocprim.hpp>
16#elif defined(AMREX_USE_SYCL) && defined(AMREX_USE_ONEDPL)
17# include <oneapi/dpl/execution>
18# include <oneapi/dpl/numeric>
19#endif
20
21#include <concepts>
22#include <cstdint>
23#include <iterator>
24#include <numeric>
25#include <type_traits>
26
27namespace amrex {
28namespace Scan {
29
30struct RetSum {
31 bool flag = true;
32 explicit operator bool() const noexcept { return flag; }
33};
34static constexpr RetSum retSum{true};
35static constexpr RetSum noRetSum{false};
36
37namespace Type {
38 static constexpr struct Inclusive {} inclusive{};
39 static constexpr struct Exclusive {} exclusive{};
40}
41
42#if defined(AMREX_USE_GPU)
43
45namespace detail {
46
47template <typename T>
48struct STVA
49{
50 char status;
51 T value;
52};
53
54template <typename T, bool SINGLE_WORD> struct BlockStatus {};
55
56template <typename T>
57struct BlockStatus<T, true>
58{
59 template<typename U>
60 union Data {
61 STVA<U> s;
62 uint64_t i;
63 void operator=(Data<U> const&) = delete;
64 void operator=(Data<U> &&) = delete;
65 };
66 Data<T> d;
67
69 void write (char a_status, T a_value) {
70#if defined(AMREX_USE_CUDA)
71 volatile uint64_t tmp;
72 reinterpret_cast<STVA<T> volatile&>(tmp).status = a_status;
73 reinterpret_cast<STVA<T> volatile&>(tmp).value = a_value;
74 reinterpret_cast<uint64_t&>(d.s) = tmp;
75#else
76 Data<T> tmp;
77 tmp.s = {a_status, a_value};
78 static_assert(sizeof(unsigned long long) == sizeof(uint64_t),
79 "HIP/SYCL: unsigned long long must be 64 bits");
80 Gpu::Atomic::Exch(reinterpret_cast<unsigned long long*>(&d),
81 reinterpret_cast<unsigned long long&>(tmp));
82#endif
83 }
84
86 T get_aggregate() const { return d.s.value; }
87
89 STVA<T> read () volatile {
90#if defined(AMREX_USE_CUDA)
91 volatile uint64_t tmp = reinterpret_cast<uint64_t volatile&>(d);
92 return {reinterpret_cast<STVA<T> volatile&>(tmp).status,
93 reinterpret_cast<STVA<T> volatile&>(tmp).value };
94#else
95 static_assert(sizeof(unsigned long long) == sizeof(uint64_t),
96 "HIP/SYCL: unsigned long long must be 64 bits");
97 unsigned long long tmp = Gpu::Atomic::Add
98 (reinterpret_cast<unsigned long long*>(const_cast<Data<T>*>(&d)), 0ull);
99 return (*reinterpret_cast<Data<T>*>(&tmp)).s;
100#endif
101 }
102
104 void set_status (char a_status) { d.s.status = a_status; }
105
107 STVA<T> wait () volatile {
108 STVA<T> r;
109 do {
110#if defined(AMREX_USE_SYCL)
111 sycl::atomic_fence(sycl::memory_order::acq_rel, sycl::memory_scope::work_group);
112#else
113 __threadfence_block();
114#endif
115 r = read();
116 } while (r.status == 'x');
117 return r;
118 }
119};
120
121template <typename T>
122struct BlockStatus<T, false>
123{
124 T aggregate;
125 T inclusive;
126 char status;
127
129 void write (char a_status, T a_value) {
130 if (a_status == 'a') {
131 aggregate = a_value;
132 } else {
133 inclusive = a_value;
134 }
135#if defined(AMREX_USE_SYCL)
136 sycl::atomic_fence(sycl::memory_order::acq_rel, sycl::memory_scope::device);
137#else
138 __threadfence();
139#endif
140 status = a_status;
141 }
142
144 T get_aggregate() const { return aggregate; }
145
147 STVA<T> read () volatile {
148#if defined(AMREX_USE_SYCL)
149 constexpr auto mo = sycl::memory_order::relaxed;
150 constexpr auto ms = sycl::memory_scope::device;
151 constexpr auto as = sycl::access::address_space::global_space;
152#endif
153 if (status == 'x') {
154 return {'x', 0};
155 } else if (status == 'a') {
156#if defined(AMREX_USE_SYCL)
157 sycl::atomic_ref<T,mo,ms,as> ar{const_cast<T&>(aggregate)};
158 return {'a', ar.load()};
159#else
160 return {'a', aggregate};
161#endif
162 } else {
163#if defined(AMREX_USE_SYCL)
164 sycl::atomic_ref<T,mo,ms,as> ar{const_cast<T&>(inclusive)};
165 return {'p', ar.load()};
166#else
167 return {'p', inclusive};
168#endif
169 }
170 }
171
173 void set_status (char a_status) { status = a_status; }
174
176 STVA<T> wait () volatile {
177 STVA<T> r;
178 do {
179 r = read();
180#if defined(AMREX_USE_SYCL)
181 sycl::atomic_fence(sycl::memory_order::acq_rel, sycl::memory_scope::device);
182#else
183 __threadfence();
184#endif
185 } while (r.status == 'x');
186 return r;
187 }
188};
189
190}
192
193#if defined(AMREX_USE_SYCL)
194
195#ifndef AMREX_SYCL_NO_MULTIPASS_SCAN
196template <typename T, std::integral N, typename FIN, typename FOUT, typename TYPE>
197T PrefixSum_mp (N n, FIN const& fin, FOUT const& fout, TYPE, RetSum a_ret_sum)
198{
199 if (n <= 0) { return 0; }
200 constexpr int nwarps_per_block = 8;
201 constexpr int nthreads = nwarps_per_block*Gpu::Device::warp_size;
202 constexpr int nchunks = 12;
203 constexpr int nelms_per_block = nthreads * nchunks;
204 AMREX_ALWAYS_ASSERT(static_cast<Long>(n) < static_cast<Long>(
205 std::numeric_limits<int>::max())*nelms_per_block);
206 int nblocks = (static_cast<Long>(n) + nelms_per_block - 1) / nelms_per_block;
207 std::size_t sm = sizeof(T) * (Gpu::Device::warp_size + nwarps_per_block);
208 auto stream = Gpu::gpuStream();
209
210 std::size_t nbytes_blockresult = Arena::align(sizeof(T)*n);
211 std::size_t nbytes_blocksum = Arena::align(sizeof(T)*nblocks);
212 std::size_t nbytes_totalsum = Arena::align(sizeof(T));
213 auto dp = (char*)(The_Arena()->alloc(nbytes_blockresult
214 + nbytes_blocksum
215 + nbytes_totalsum));
216 T* blockresult_p = (T*)dp;
217 T* blocksum_p = (T*)(dp + nbytes_blockresult);
218 T* totalsum_p = (T*)(dp + nbytes_blockresult + nbytes_blocksum);
219
220 amrex::launch<nthreads>(nblocks, sm, stream,
221 [=] AMREX_GPU_DEVICE (Gpu::Handler const& gh) noexcept
222 {
223 sycl::sub_group const& sg = gh.item->get_sub_group();
224 int lane = sg.get_local_id()[0];
225 int warp = sg.get_group_id()[0];
226 int nwarps = sg.get_group_range()[0];
227
228 int threadIdxx = gh.item->get_local_id(0);
229 int blockIdxx = gh.item->get_group_linear_id();
230 int blockDimx = gh.item->get_local_range(0);
231
232 T* shared = (T*)(gh.local);
233 T* shared2 = shared + Gpu::Device::warp_size;
234
235 // Each block processes [ibegin,iend).
236 N ibegin = static_cast<N>(nelms_per_block) * blockIdxx;
237 N iend = static_cast<N>(amrex::min(Long(ibegin)+nelms_per_block, Long(n)));
238
239 // Each block is responsible for nchunks chunks of data,
240 // where each chunk has blockDim.x elements, one for each
241 // thread in the block.
242 T sum_prev_chunk = 0; // inclusive sum from previous chunks.
243 for (int ichunk = 0; ichunk < nchunks; ++ichunk) {
244 Long offset = Long(ibegin) + ichunk*blockDimx;
245 if (offset >= Long(iend)) { break; }
246
247 offset += threadIdxx;
248 T x0 = (offset < Long(iend)) ? fin(N(offset)) : 0;
249 T x = x0;
250 // Scan within a warp
251 for (int i = 1; i <= Gpu::Device::warp_size; i *= 2) {
252 T s = sycl::shift_group_right(sg, x, i);
253 if (lane >= i) { x += s; }
254 }
255
256 // x now holds the inclusive sum within the warp. The
257 // last thread in each warp holds the inclusive sum of
258 // this warp. We will store it in shared memory.
259 if (lane == Gpu::Device::warp_size - 1) {
260 shared[warp] = x;
261 }
262
263 gh.item->barrier(sycl::access::fence_space::local_space);
264
265 // The first warp will do scan on the warp sums for the
266 // whole block.
267 if (warp == 0) {
268 T y = (lane < nwarps) ? shared[lane] : 0;
269 for (int i = 1; i <= Gpu::Device::warp_size; i *= 2) {
270 T s = sycl::shift_group_right(sg, y, i);
271 if (lane >= i) { y += s; }
272 }
273
274 if (lane < nwarps) { shared2[lane] = y; }
275 }
276
277 gh.item->barrier(sycl::access::fence_space::local_space);
278
279 // shared[0:nwarps) holds the inclusive sum of warp sums.
280
281 // Also note x still holds the inclusive sum within the
282 // warp. Given these two, we can compute the inclusive
283 // sum within this chunk.
284 T sum_prev_warp = (warp == 0) ? 0 : shared2[warp-1];
285 T tmp_out = sum_prev_warp + sum_prev_chunk +
286 (std::is_same_v<std::decay_t<TYPE>,Type::Inclusive> ? x : x-x0);
287 sum_prev_chunk += shared2[nwarps-1];
288
289 if (offset < Long(iend)) {
290 blockresult_p[offset] = tmp_out;
291 }
292 }
293
294 // sum_prev_chunk now holds the sum of the whole block.
295 if (threadIdxx == 0) {
296 blocksum_p[blockIdxx] = sum_prev_chunk;
297 }
298 });
299
300 amrex::launch<nthreads>(1, sm, stream,
301 [=] AMREX_GPU_DEVICE (Gpu::Handler const& gh) noexcept
302 {
303 sycl::sub_group const& sg = gh.item->get_sub_group();
304 int lane = sg.get_local_id()[0];
305 int warp = sg.get_group_id()[0];
306 int nwarps = sg.get_group_range()[0];
307
308 int threadIdxx = gh.item->get_local_id(0);
309 int blockDimx = gh.item->get_local_range(0);
310
311 T* shared = (T*)(gh.local);
312 T* shared2 = shared + Gpu::Device::warp_size;
313
314 T sum_prev_chunk = 0;
315 for (int offset = threadIdxx; offset - threadIdxx < nblocks; offset += blockDimx) {
316 T x = (offset < nblocks) ? blocksum_p[offset] : 0;
317 // Scan within a warp
318 for (int i = 1; i <= Gpu::Device::warp_size; i *= 2) {
319 T s = sycl::shift_group_right(sg, x, i);
320 if (lane >= i) { x += s; }
321 }
322
323 // x now holds the inclusive sum within the warp. The
324 // last thread in each warp holds the inclusive sum of
325 // this warp. We will store it in shared memory.
326 if (lane == Gpu::Device::warp_size - 1) {
327 shared[warp] = x;
328 }
329
330 gh.item->barrier(sycl::access::fence_space::local_space);
331
332 // The first warp will do scan on the warp sums for the
333 // whole block.
334 if (warp == 0) {
335 T y = (lane < nwarps) ? shared[lane] : 0;
336 for (int i = 1; i <= Gpu::Device::warp_size; i *= 2) {
337 T s = sycl::shift_group_right(sg, y, i);
338 if (lane >= i) { y += s; }
339 }
340
341 if (lane < nwarps) { shared2[lane] = y; }
342 }
343
344 gh.item->barrier(sycl::access::fence_space::local_space);
345
346 // shared[0:nwarps) holds the inclusive sum of warp sums.
347
348 // Also note x still holds the inclusive sum within the
349 // warp. Given these two, we can compute the inclusive
350 // sum within this chunk.
351 T sum_prev_warp = (warp == 0) ? 0 : shared2[warp-1];
352 T tmp_out = sum_prev_warp + sum_prev_chunk + x;
353 sum_prev_chunk += shared2[nwarps-1];
354
355 if (offset < nblocks) {
356 blocksum_p[offset] = tmp_out;
357 }
358 }
359
360 // sum_prev_chunk now holds the total sum.
361 if (threadIdxx == 0) {
362 *totalsum_p = sum_prev_chunk;
363 }
364 });
365
366 amrex::launch<nthreads>(nblocks, 0, stream,
367 [=] AMREX_GPU_DEVICE (Gpu::Handler const& gh) noexcept
368 {
369 int threadIdxx = gh.item->get_local_id(0);
370 int blockIdxx = gh.item->get_group_linear_id();
371 int blockDimx = gh.item->get_local_range(0);
372
373 // Each block processes [ibegin,iend).
374 N ibegin = static_cast<N>(nelms_per_block) * blockIdxx;
375 N iend = static_cast<N>(amrex::min(Long(ibegin)+nelms_per_block, Long(n)));
376 T prev_sum = (blockIdxx == 0) ? 0 : blocksum_p[blockIdxx-1];
377 int nelms_this_block = static_cast<int>(iend-ibegin);
378 for (int i = threadIdxx; i < nelms_this_block; i += blockDimx) {
379 N offset = ibegin + i;
380 fout(offset, prev_sum + blockresult_p[offset]);
381 }
382 });
383
384 T totalsum = 0;
385 if (a_ret_sum) {
386 Gpu::dtoh_memcpy_async(&totalsum, totalsum_p, sizeof(T));
387
389 The_Arena()->free(dp);
390
392 } else {
394 }
395
396 return totalsum;
397}
398#endif
399
400template <typename T, std::integral N, typename FIN, typename FOUT, typename TYPE>
401requires (std::same_as<std::decay_t<TYPE>,Type::Inclusive> ||
402 std::same_as<std::decay_t<TYPE>,Type::Exclusive>)
403T PrefixSum (N n, FIN && fin, FOUT && fout, TYPE type, RetSum a_ret_sum = retSum)
404{
405 if (n <= 0) { return 0; }
406 constexpr int nwarps_per_block = 8;
407 constexpr int nthreads = nwarps_per_block*Gpu::Device::warp_size;
408 constexpr int nchunks = 12;
409 constexpr int nelms_per_block = nthreads * nchunks;
410 AMREX_ALWAYS_ASSERT(static_cast<Long>(n) < static_cast<Long>(
411 std::numeric_limits<int>::max())*nelms_per_block);
412 int nblocks = (static_cast<Long>(n) + nelms_per_block - 1) / nelms_per_block;
413
414#ifndef AMREX_SYCL_NO_MULTIPASS_SCAN
415 if (nblocks > 1) {
416 return PrefixSum_mp<T>(n, std::forward<FIN>(fin), std::forward<FOUT>(fout), type, a_ret_sum);
417 }
418#else
420#endif
421
422 std::size_t sm = sizeof(T) * (Gpu::Device::warp_size + nwarps_per_block) + sizeof(int);
423 auto stream = Gpu::gpuStream();
424
425 using BlockStatusT = std::conditional_t<sizeof(detail::STVA<T>) <= 8,
426 detail::BlockStatus<T,true>, detail::BlockStatus<T,false> >;
427
428 std::size_t nbytes_blockstatus = Arena::align(sizeof(BlockStatusT)*nblocks);
429 std::size_t nbytes_blockid = Arena::align(sizeof(unsigned int));
430 std::size_t nbytes_totalsum = Arena::align(sizeof(T));
431 auto dp = (char*)(The_Arena()->alloc( nbytes_blockstatus
432 + nbytes_blockid
433 + nbytes_totalsum));
434 BlockStatusT* AMREX_RESTRICT block_status_p = (BlockStatusT*)dp;
435 unsigned int* AMREX_RESTRICT virtual_block_id_p = (unsigned int*)(dp + nbytes_blockstatus);
436 T* AMREX_RESTRICT totalsum_p = (T*)(dp + nbytes_blockstatus + nbytes_blockid);
437
438 amrex::ParallelFor(nblocks, [=] AMREX_GPU_DEVICE (int i) noexcept {
439 BlockStatusT& block_status = block_status_p[i];
440 block_status.set_status('x');
441 if (i == 0) {
442 *virtual_block_id_p = 0;
443 *totalsum_p = 0;
444 }
445 });
446
447 amrex::launch<nthreads>(nblocks, sm, stream,
448 [=] AMREX_GPU_DEVICE (Gpu::Handler const& gh) noexcept
449 {
450 sycl::sub_group const& sg = gh.item->get_sub_group();
451 int lane = sg.get_local_id()[0];
452 int warp = sg.get_group_id()[0];
453 int nwarps = sg.get_group_range()[0];
454
455 int threadIdxx = gh.item->get_local_id(0);
456 int blockDimx = gh.item->get_local_range(0);
457 int gridDimx = gh.item->get_group_range(0);
458
459 T* shared = (T*)(gh.local);
460 T* shared2 = shared + Gpu::Device::warp_size;
461
462 // First of all, get block virtual id. We must do this to
463 // avoid deadlock because blocks may be launched in any order.
464 // Anywhere in this function, we should not use blockIdx.
465 int virtual_block_id = 0;
466 if (gridDimx > 1) {
467 int& virtual_block_id_shared = *((int*)(shared2+nwarps));
468 if (threadIdxx == 0) {
469 unsigned int bid = Gpu::Atomic::Add(virtual_block_id_p, 1u);
470 virtual_block_id_shared = bid;
471 }
472 gh.item->barrier(sycl::access::fence_space::local_space);
473 virtual_block_id = virtual_block_id_shared;
474 }
475
476 // Each block processes [ibegin,iend).
477 N ibegin = static_cast<N>(nelms_per_block) * virtual_block_id;
478 N iend = static_cast<N>(amrex::min(Long(ibegin)+nelms_per_block, Long(n)));
479 BlockStatusT& block_status = block_status_p[virtual_block_id];
480
481 //
482 // The overall algorithm is based on "Single-pass Parallel
483 // Prefix Scan with Decoupled Look-back" by D. Merrill &
484 // M. Garland.
485 //
486
487 // Each block is responsible for nchunks chunks of data,
488 // where each chunk has blockDim.x elements, one for each
489 // thread in the block.
490 T sum_prev_chunk = 0; // inclusive sum from previous chunks.
491 T tmp_out[nchunks]; // block-wide inclusive sum for chunks
492 for (int ichunk = 0; ichunk < nchunks; ++ichunk) {
493 Long offset = Long(ibegin) + ichunk*blockDimx;
494 if (offset >= Long(iend)) { break; }
495
496 offset += threadIdxx;
497 T x0 = (offset < Long(iend)) ? fin(N(offset)) : 0;
498 if (std::is_same_v<std::decay_t<TYPE>,Type::Exclusive> && offset == Long(n)-1) {
499 *totalsum_p += x0;
500 }
501 T x = x0;
502 // Scan within a warp
503 for (int i = 1; i <= Gpu::Device::warp_size; i *= 2) {
504 T s = sycl::shift_group_right(sg, x, i);
505 if (lane >= i) { x += s; }
506 }
507
508 // x now holds the inclusive sum within the warp. The
509 // last thread in each warp holds the inclusive sum of
510 // this warp. We will store it in shared memory.
511 if (lane == Gpu::Device::warp_size - 1) {
512 shared[warp] = x;
513 }
514
515 gh.item->barrier(sycl::access::fence_space::local_space);
516
517 // The first warp will do scan on the warp sums for the
518 // whole block.
519 if (warp == 0) {
520 T y = (lane < nwarps) ? shared[lane] : 0;
521 for (int i = 1; i <= Gpu::Device::warp_size; i *= 2) {
522 T s = sycl::shift_group_right(sg, y, i);
523 if (lane >= i) { y += s; }
524 }
525
526 if (lane < nwarps) { shared2[lane] = y; }
527 }
528
529 gh.item->barrier(sycl::access::fence_space::local_space);
530
531 // shared[0:nwarps) holds the inclusive sum of warp sums.
532
533 // Also note x still holds the inclusive sum within the
534 // warp. Given these two, we can compute the inclusive
535 // sum within this chunk.
536 T sum_prev_warp = (warp == 0) ? 0 : shared2[warp-1];
537 tmp_out[ichunk] = sum_prev_warp + sum_prev_chunk +
538 (std::is_same_v<std::decay_t<TYPE>,Type::Inclusive> ? x : x-x0);
539 sum_prev_chunk += shared2[nwarps-1];
540 }
541
542 // sum_prev_chunk now holds the sum of the whole block.
543 if (threadIdxx == 0 && gridDimx > 1) {
544 block_status.write((virtual_block_id == 0) ? 'p' : 'a',
545 sum_prev_chunk);
546 }
547
548 if (virtual_block_id == 0) {
549 for (int ichunk = 0; ichunk < nchunks; ++ichunk) {
550 Long offset = Long(ibegin) + ichunk*blockDimx + threadIdxx;
551 if (offset >= Long(iend)) { break; }
552 fout(N(offset), tmp_out[ichunk]);
553 if (offset == Long(n)-1) {
554 *totalsum_p += tmp_out[ichunk];
555 }
556 }
557 } else if (virtual_block_id > 0) {
558
559 if (warp == 0) {
560 T exclusive_prefix = 0;
561 BlockStatusT volatile* pbs = block_status_p;
562 for (int iblock0 = virtual_block_id-1; iblock0 >= 0; iblock0 -= Gpu::Device::warp_size)
563 {
564 int iblock = iblock0-lane;
565 detail::STVA<T> stva{'p', 0};
566 if (iblock >= 0) {
567 stva = pbs[iblock].wait();
568 }
569
570 T x = stva.value;
571
572 // implement our own __ballot
573 unsigned status_bf = (stva.status == 'p') ? (0x1u << lane) : 0;
574 for (int i = 1; i < Gpu::Device::warp_size; i *= 2) {
575 status_bf |= sycl::permute_group_by_xor(sg, status_bf, i);
576 }
577
578 bool stop_lookback = status_bf & 0x1u;
579 if (stop_lookback == false) {
580 if (status_bf != 0) {
581 T y = x;
582 if (lane > 0) { x = 0; }
583 unsigned int bit_mask = 0x1u;
584 for (int i = 1; i < Gpu::Device::warp_size; ++i) {
585 bit_mask <<= 1;
586 if (i == lane) { x = y; }
587 if (status_bf & bit_mask) {
588 stop_lookback = true;
589 break;
590 }
591 }
592 }
593
594 for (int i = Gpu::Device::warp_size/2; i > 0; i /= 2) {
595 x += sycl::shift_group_left(sg, x,i);
596 }
597 }
598
599 if (lane == 0) { exclusive_prefix += x; }
600 if (stop_lookback) { break; }
601 }
602
603 if (lane == 0) {
604 block_status.write('p', block_status.get_aggregate() + exclusive_prefix);
605 shared[0] = exclusive_prefix;
606 }
607 }
608
609 gh.item->barrier(sycl::access::fence_space::local_space);
610
611 T exclusive_prefix = shared[0];
612
613 for (int ichunk = 0; ichunk < nchunks; ++ichunk) {
614 Long offset = Long(ibegin) + ichunk*blockDimx + threadIdxx;
615 if (offset >= Long(iend)) { break; }
616 T t = tmp_out[ichunk] + exclusive_prefix;
617 fout(N(offset), t);
618 if (offset == Long(n)-1) {
619 *totalsum_p += t;
620 }
621 }
622 }
623 });
624
625 T totalsum = 0;
626 if (a_ret_sum) {
627 // xxxxx SYCL todo: Should test if using pinned memory and thus
628 // avoiding memcpy is faster.
629 Gpu::dtoh_memcpy_async(&totalsum, totalsum_p, sizeof(T));
630
632 The_Arena()->free(dp);
633
635 } else {
637 }
638
639 return totalsum;
640}
641
642#elif defined(AMREX_USE_HIP)
643
644template <typename T, std::integral N, typename FIN, typename FOUT, typename TYPE>
645requires (std::same_as<std::decay_t<TYPE>,Type::Inclusive> ||
646 std::same_as<std::decay_t<TYPE>,Type::Exclusive>)
647T PrefixSum (N n, FIN const& fin, FOUT const& fout, TYPE, RetSum a_ret_sum = retSum)
648{
649 if (n <= 0) { return 0; }
650 constexpr int nwarps_per_block = 4;
651 constexpr int nthreads = nwarps_per_block*Gpu::Device::warp_size; // # of threads per block
652 constexpr int nelms_per_thread = sizeof(T) >= 8 ? 8 : 16;
653 constexpr int nelms_per_block = nthreads * nelms_per_thread;
654 AMREX_ALWAYS_ASSERT(static_cast<Long>(n) < static_cast<Long>(
655 std::numeric_limits<int>::max())*nelms_per_block);
656 int nblocks = (static_cast<Long>(n) + nelms_per_block - 1) / nelms_per_block;
657 std::size_t sm = 0;
658 auto stream = Gpu::gpuStream();
659
660 using ScanTileState = rocprim::detail::lookback_scan_state<T>;
661 using OrderedBlockId = rocprim::detail::ordered_block_id<unsigned int>;
662
663#if (defined(HIP_VERSION_MAJOR) && (HIP_VERSION_MAJOR < 6)) || \
664 (defined(HIP_VERSION_MAJOR) && (HIP_VERSION_MAJOR == 6) && \
665 defined(HIP_VERSION_MINOR) && (HIP_VERSION_MINOR == 0))
666
667 std::size_t nbytes_tile_state = rocprim::detail::align_size
668 (ScanTileState::get_storage_size(nblocks));
669 std::size_t nbytes_block_id = OrderedBlockId::get_storage_size();
670
671 auto dp = (char*)(The_Arena()->alloc(nbytes_tile_state+nbytes_block_id));
672
673 ScanTileState tile_state = ScanTileState::create(dp, nblocks);
674
675#else
676
677 std::size_t nbytes_tile_state;
678 AMREX_HIP_SAFE_CALL(ScanTileState::get_storage_size(nblocks, stream, nbytes_tile_state));
679 nbytes_tile_state = rocprim::detail::align_size(nbytes_tile_state);
680
681 std::size_t nbytes_block_id = OrderedBlockId::get_storage_size();
682
683 auto dp = (char*)(The_Arena()->alloc(nbytes_tile_state+nbytes_block_id));
684
685 ScanTileState tile_state;
686 AMREX_HIP_SAFE_CALL(ScanTileState::create(tile_state, dp, nblocks, stream));
687
688#endif
689
690 auto ordered_block_id = OrderedBlockId::create
691 (reinterpret_cast<OrderedBlockId::id_type*>(dp + nbytes_tile_state));
692
693 // Init ScanTileState on device
694 amrex::launch<nthreads>((nblocks+nthreads-1)/nthreads, 0, stream, [=] AMREX_GPU_DEVICE ()
695 {
696 auto& scan_tile_state = const_cast<ScanTileState&>(tile_state);
697 auto& scan_bid = const_cast<OrderedBlockId&>(ordered_block_id);
698 const unsigned int gid = blockIdx.x*nthreads + threadIdx.x;
699 if (gid == 0) { scan_bid.reset(); }
700 scan_tile_state.initialize_prefix(gid, nblocks);
701 });
702
703 T* totalsum_p = (a_ret_sum) ? (T*)(The_Pinned_Arena()->alloc(sizeof(T))) : nullptr;
704
705 amrex::launch_global<nthreads> <<<nblocks, nthreads, sm, stream>>> (
706 [=] AMREX_GPU_DEVICE () noexcept
707 {
708 using BlockLoad = rocprim::block_load<T, nthreads, nelms_per_thread,
709 rocprim::block_load_method::block_load_transpose>;
710 using BlockScan = rocprim::block_scan<T, nthreads,
711 rocprim::block_scan_algorithm::using_warp_scan>;
712 using BlockExchange = rocprim::block_exchange<T, nthreads, nelms_per_thread>;
713 using LookbackScanPrefixOp = rocprim::detail::lookback_scan_prefix_op
714 <T, rocprim::plus<T>, ScanTileState>;
715
716 __shared__ struct TempStorage {
717 typename OrderedBlockId::storage_type ordered_bid;
718 union {
719 typename BlockLoad::storage_type load;
720 typename BlockExchange::storage_type exchange;
721 typename BlockScan::storage_type scan;
722 };
723 } temp_storage;
724
725 // Lambda captured tile_state is const. We have to cast the const away.
726 auto& scan_tile_state = const_cast<ScanTileState&>(tile_state);
727 auto& scan_bid = const_cast<OrderedBlockId&>(ordered_block_id);
728
729 auto const virtual_block_id = scan_bid.get(threadIdx.x, temp_storage.ordered_bid);
730
731 // Each block processes [ibegin,iend).
732 N ibegin = static_cast<N>(nelms_per_block) * virtual_block_id;
733 N iend = static_cast<N>(amrex::min(Long(ibegin)+nelms_per_block, Long(n)));
734
735 auto input_begin = rocprim::make_transform_iterator(
736 rocprim::make_counting_iterator(N(0)),
737 [&] (N i) -> T { return fin(i+ibegin); });
738
739 T data[nelms_per_thread];
740 if (static_cast<int>(iend-ibegin) == nelms_per_block) {
741 BlockLoad().load(input_begin, data, temp_storage.load);
742 } else {
743 // padding with 0
744 BlockLoad().load(input_begin, data, iend-ibegin, 0, temp_storage.load);
745 }
746
747 __syncthreads();
748
749 constexpr bool is_exclusive = std::is_same_v<std::decay_t<TYPE>,Type::Exclusive>;
750
751 if (virtual_block_id == 0) {
752 T block_agg;
753 AMREX_IF_CONSTEXPR(is_exclusive) {
754 BlockScan().exclusive_scan(data, data, T{0}, block_agg, temp_storage.scan);
755 } else {
756 BlockScan().inclusive_scan(data, data, block_agg, temp_storage.scan);
757 }
758 if (threadIdx.x == 0) {
759 if (nblocks > 1) {
760 scan_tile_state.set_complete(0, block_agg);
761 } else if (nblocks == 1 && totalsum_p) {
762 *totalsum_p = block_agg;
763 }
764 }
765 } else {
766 T last = data[nelms_per_thread-1]; // Need this for the total sum in exclusive case
767
768 LookbackScanPrefixOp prefix_op(virtual_block_id, rocprim::plus<T>(), scan_tile_state);
769 AMREX_IF_CONSTEXPR(is_exclusive) {
770 BlockScan().exclusive_scan(data, data, temp_storage.scan, prefix_op,
771 rocprim::plus<T>());
772 } else {
773 BlockScan().inclusive_scan(data, data, temp_storage.scan, prefix_op,
774 rocprim::plus<T>());
775 }
776 if (totalsum_p) {
777 if (iend == n && threadIdx.x == nthreads-1) { // last thread of last block
778 T tsum = data[nelms_per_thread-1];
779 AMREX_IF_CONSTEXPR(is_exclusive) { tsum += last; }
780 *totalsum_p = tsum;
781 }
782 }
783 }
784
785 __syncthreads();
786
787 BlockExchange().blocked_to_striped(data, data, temp_storage.exchange);
788
789 for (int i = 0; i < nelms_per_thread; ++i) {
790 N offset = ibegin + i*nthreads + threadIdx.x;
791 if (offset < iend) { fout(offset, data[i]); }
792 }
793 });
794
795 if (totalsum_p) {
798
799 The_Arena()->free(dp);
800 } else {
802 }
803
804 T ret = (a_ret_sum) ? *totalsum_p : T(0);
805 if (totalsum_p) { The_Pinned_Arena()->free(totalsum_p); }
806
807 return ret;
808}
809
810#elif defined(AMREX_USE_CUDA)
811
812template <typename T, std::integral N, typename FIN, typename FOUT, typename TYPE>
813requires (std::same_as<std::decay_t<TYPE>,Type::Inclusive> ||
814 std::same_as<std::decay_t<TYPE>,Type::Exclusive>)
815T PrefixSum (N n, FIN const& fin, FOUT const& fout, TYPE, RetSum a_ret_sum = retSum)
816{
817 if (n <= 0) { return 0; }
818 constexpr int nwarps_per_block = 8;
819 constexpr int nthreads = nwarps_per_block*Gpu::Device::warp_size; // # of threads per block
820 constexpr int nelms_per_thread = sizeof(T) >= 8 ? 4 : 8;
821 constexpr int nelms_per_block = nthreads * nelms_per_thread;
822 AMREX_ALWAYS_ASSERT(static_cast<Long>(n) < static_cast<Long>(
823 std::numeric_limits<int>::max())*nelms_per_block);
824 int nblocks = (static_cast<Long>(n) + nelms_per_block - 1) / nelms_per_block;
825 std::size_t sm = 0;
826 auto stream = Gpu::gpuStream();
827
828 using ScanTileState = cub::ScanTileState<T>;
829 std::size_t tile_state_size = 0;
830 ScanTileState::AllocationSize(nblocks, tile_state_size);
831
832 std::size_t nbytes_tile_state = Arena::align(tile_state_size);
833 auto tile_state_p = (char*)(The_Arena()->alloc(nbytes_tile_state));
834
835 ScanTileState tile_state;
836 tile_state.Init(nblocks, tile_state_p, tile_state_size); // Init ScanTileState on host
837
838 if (nblocks > 1) {
839 // Init ScanTileState on device
840 amrex::launch<nthreads>((nblocks+nthreads-1)/nthreads, 0, stream, [=] AMREX_GPU_DEVICE ()
841 {
842 const_cast<ScanTileState&>(tile_state).InitializeStatus(nblocks);
843 });
844 }
845
846 T* totalsum_p = (a_ret_sum) ? (T*)(The_Pinned_Arena()->alloc(sizeof(T))) : nullptr;
847
848 amrex::launch_global<nthreads> <<<nblocks, nthreads, sm, stream>>> (
849 [=] AMREX_GPU_DEVICE () noexcept
850 {
851 using BlockLoad = cub::BlockLoad<T, nthreads, nelms_per_thread, cub::BLOCK_LOAD_WARP_TRANSPOSE>;
852 using BlockScan = cub::BlockScan<T, nthreads, cub::BLOCK_SCAN_WARP_SCANS>;
853 using BlockExchange = cub::BlockExchange<T, nthreads, nelms_per_thread>;
854
855#ifdef AMREX_CUDA_CCCL_VER_GE_2_8
856 using Sum = cuda::std::plus<T>;
857#else
858 using Sum = cub::Sum;
859#endif
860 using TilePrefixCallbackOp = cub::TilePrefixCallbackOp<T, Sum, ScanTileState>;
861
862 __shared__ union TempStorage
863 {
864 typename BlockLoad::TempStorage load;
865 typename BlockExchange::TempStorage exchange;
866 struct ScanStorage {
867 typename BlockScan::TempStorage scan;
868 typename TilePrefixCallbackOp::TempStorage prefix;
869 } scan_storeage;
870 } temp_storage;
871
872 // Lambda captured tile_state is const. We have to cast the const away.
873 auto& scan_tile_state = const_cast<ScanTileState&>(tile_state);
874
875 int virtual_block_id = blockIdx.x;
876
877 // Each block processes [ibegin,iend).
878 N ibegin = static_cast<N>(nelms_per_block) * virtual_block_id;
879 N iend = static_cast<N>(amrex::min(Long(ibegin)+nelms_per_block, Long(n)));
880
881 auto input_lambda = [&] (N i) -> T { return fin(i+ibegin); };
882#ifdef AMREX_CUDA_CCCL_VER_GE_2_8
883 thrust::transform_iterator<decltype(input_lambda),thrust::counting_iterator<N> >
884 input_begin(thrust::counting_iterator<N>(0), input_lambda);
885#else
886 cub::TransformInputIterator<T,decltype(input_lambda),cub::CountingInputIterator<N> >
887 input_begin(cub::CountingInputIterator<N>(0), input_lambda);
888#endif
889
890 T data[nelms_per_thread];
891 if (static_cast<int>(iend-ibegin) == nelms_per_block) {
892 BlockLoad(temp_storage.load).Load(input_begin, data);
893 } else {
894 BlockLoad(temp_storage.load).Load(input_begin, data, iend-ibegin, 0); // padding with 0
895 }
896
897 __syncthreads();
898
899 constexpr bool is_exclusive = std::is_same_v<std::decay_t<TYPE>,Type::Exclusive>;
900
901 if (virtual_block_id == 0) {
902 T block_agg;
903 AMREX_IF_CONSTEXPR(is_exclusive) {
904 BlockScan(temp_storage.scan_storeage.scan).ExclusiveSum(data, data, block_agg);
905 } else {
906 BlockScan(temp_storage.scan_storeage.scan).InclusiveSum(data, data, block_agg);
907 }
908 if (threadIdx.x == 0) {
909 if (nblocks > 1) {
910 scan_tile_state.SetInclusive(0, block_agg);
911 } else if (nblocks == 1 && totalsum_p) {
912 *totalsum_p = block_agg;
913 }
914 }
915 } else {
916 T last = data[nelms_per_thread-1]; // Need this for the total sum in exclusive case
917
918 TilePrefixCallbackOp prefix_op(scan_tile_state, temp_storage.scan_storeage.prefix,
919 Sum{}, virtual_block_id);
920 AMREX_IF_CONSTEXPR(is_exclusive) {
921 BlockScan(temp_storage.scan_storeage.scan).ExclusiveSum(data, data, prefix_op);
922 } else {
923 BlockScan(temp_storage.scan_storeage.scan).InclusiveSum(data, data, prefix_op);
924 }
925 if (totalsum_p) {
926 if (iend == n && threadIdx.x == nthreads-1) { // last thread of last block
927 T tsum = data[nelms_per_thread-1];
928 AMREX_IF_CONSTEXPR(is_exclusive) { tsum += last; }
929 *totalsum_p = tsum;
930 }
931 }
932 }
933
934 __syncthreads();
935
936 BlockExchange(temp_storage.exchange).BlockedToStriped(data);
937
938 for (int i = 0; i < nelms_per_thread; ++i) {
939 N offset = ibegin + i*nthreads + threadIdx.x;
940 if (offset < iend) { fout(offset, data[i]); }
941 }
942 });
943
944 if (totalsum_p) {
947
948 The_Arena()->free(tile_state_p);
949 } else {
950 Gpu::freeAsync(The_Arena(), tile_state_p);
951 }
952
953 T ret = (a_ret_sum) ? *totalsum_p : T(0);
954 if (totalsum_p) { The_Pinned_Arena()->free(totalsum_p); }
955
956 return ret;
957}
958
959#endif
960
972template <std::integral N, typename T >
973T InclusiveSum (N n, T const* in, T * out, RetSum a_ret_sum = retSum)
974{
975 if (n <= 0) { return 0; }
976#if defined(AMREX_USE_CUDA)
977 void* d_temp = nullptr;
978 std::size_t temp_bytes = 0;
979 AMREX_GPU_SAFE_CALL(cub::DeviceScan::InclusiveSum(d_temp, temp_bytes, in, out, n,
980 Gpu::gpuStream()));
981 d_temp = The_Arena()->alloc(temp_bytes);
982 AMREX_GPU_SAFE_CALL(cub::DeviceScan::InclusiveSum(d_temp, temp_bytes, in, out, n,
983 Gpu::gpuStream()));
984 T totalsum = 0;
985 if (a_ret_sum) {
986 Gpu::dtoh_memcpy_async(&totalsum, out+(n-1), sizeof(T));
987 }
989 The_Arena()->free(d_temp);
991 return totalsum;
992#elif defined(AMREX_USE_HIP)
993 void* d_temp = nullptr;
994 std::size_t temp_bytes = 0;
995 AMREX_GPU_SAFE_CALL(rocprim::inclusive_scan(d_temp, temp_bytes, in, out, n,
996 rocprim::plus<T>(), Gpu::gpuStream()));
997 d_temp = The_Arena()->alloc(temp_bytes);
998 AMREX_GPU_SAFE_CALL(rocprim::inclusive_scan(d_temp, temp_bytes, in, out, n,
999 rocprim::plus<T>(), Gpu::gpuStream()));
1000 T totalsum = 0;
1001 if (a_ret_sum) {
1002 Gpu::dtoh_memcpy_async(&totalsum, out+(n-1), sizeof(T));
1003 }
1005 The_Arena()->free(d_temp);
1007 return totalsum;
1008#elif defined(AMREX_USE_SYCL) && defined(AMREX_USE_ONEDPL)
1009 auto policy = oneapi::dpl::execution::make_device_policy(Gpu::Device::streamQueue());
1010 std::inclusive_scan(policy, in, in+n, out, std::plus<T>(), T(0));
1011 T totalsum = 0;
1012 if (a_ret_sum) {
1013 Gpu::dtoh_memcpy_async(&totalsum, out+(n-1), sizeof(T));
1014 }
1017 return totalsum;
1018#else
1019 if (static_cast<Long>(n) <= static_cast<Long>(std::numeric_limits<int>::max())) {
1020 return PrefixSum<T>(static_cast<int>(n),
1021 [=] AMREX_GPU_DEVICE (int i) -> T { return in[i]; },
1022 [=] AMREX_GPU_DEVICE (int i, T const& x) { out[i] = x; },
1023 Type::inclusive, a_ret_sum);
1024 } else {
1025 return PrefixSum<T>(n,
1026 [=] AMREX_GPU_DEVICE (N i) -> T { return in[i]; },
1027 [=] AMREX_GPU_DEVICE (N i, T const& x) { out[i] = x; },
1028 Type::inclusive, a_ret_sum);
1029 }
1030#endif
1031}
1032
1044template <std::integral N, typename T >
1045T ExclusiveSum (N n, T const* in, T * out, RetSum a_ret_sum = retSum)
1046{
1047 if (n <= 0) { return 0; }
1048#if defined(AMREX_USE_CUDA)
1049 T in_last = 0;
1050 if (a_ret_sum) {
1051 Gpu::dtoh_memcpy_async(&in_last, in+(n-1), sizeof(T));
1052 }
1053 void* d_temp = nullptr;
1054 std::size_t temp_bytes = 0;
1055 AMREX_GPU_SAFE_CALL(cub::DeviceScan::ExclusiveSum(d_temp, temp_bytes, in, out, n,
1056 Gpu::gpuStream()));
1057 d_temp = The_Arena()->alloc(temp_bytes);
1058 AMREX_GPU_SAFE_CALL(cub::DeviceScan::ExclusiveSum(d_temp, temp_bytes, in, out, n,
1059 Gpu::gpuStream()));
1060 T out_last = 0;
1061 if (a_ret_sum) {
1062 Gpu::dtoh_memcpy_async(&out_last, out+(n-1), sizeof(T));
1063 }
1065 The_Arena()->free(d_temp);
1067 return in_last+out_last;
1068#elif defined(AMREX_USE_HIP)
1069 T in_last = 0;
1070 if (a_ret_sum) {
1071 Gpu::dtoh_memcpy_async(&in_last, in+(n-1), sizeof(T));
1072 }
1073 void* d_temp = nullptr;
1074 std::size_t temp_bytes = 0;
1075 AMREX_GPU_SAFE_CALL(rocprim::exclusive_scan(d_temp, temp_bytes, in, out, T{0}, n,
1076 rocprim::plus<T>(), Gpu::gpuStream()));
1077 d_temp = The_Arena()->alloc(temp_bytes);
1078 AMREX_GPU_SAFE_CALL(rocprim::exclusive_scan(d_temp, temp_bytes, in, out, T{0}, n,
1079 rocprim::plus<T>(), Gpu::gpuStream()));
1080 T out_last = 0;
1081 if (a_ret_sum) {
1082 Gpu::dtoh_memcpy_async(&out_last, out+(n-1), sizeof(T));
1083 }
1085 The_Arena()->free(d_temp);
1087 return in_last+out_last;
1088#elif defined(AMREX_USE_SYCL) && defined(AMREX_USE_ONEDPL)
1089 T in_last = 0;
1090 if (a_ret_sum) {
1091 Gpu::dtoh_memcpy_async(&in_last, in+(n-1), sizeof(T));
1092 }
1093 auto policy = oneapi::dpl::execution::make_device_policy(Gpu::Device::streamQueue());
1094 std::exclusive_scan(policy, in, in+n, out, T(0), std::plus<T>());
1095 T out_last = 0;
1096 if (a_ret_sum) {
1097 Gpu::dtoh_memcpy_async(&out_last, out+(n-1), sizeof(T));
1098 }
1101 return in_last+out_last;
1102#else
1103 if (static_cast<Long>(n) <= static_cast<Long>(std::numeric_limits<int>::max())) {
1104 return PrefixSum<T>(static_cast<int>(n),
1105 [=] AMREX_GPU_DEVICE (int i) -> T { return in[i]; },
1106 [=] AMREX_GPU_DEVICE (int i, T const& x) { out[i] = x; },
1107 Type::exclusive, a_ret_sum);
1108 } else {
1109 return PrefixSum<T>(n,
1110 [=] AMREX_GPU_DEVICE (N i) -> T { return in[i]; },
1111 [=] AMREX_GPU_DEVICE (N i, T const& x) { out[i] = x; },
1112 Type::exclusive, a_ret_sum);
1113 }
1114#endif
1115}
1116
1117#else
1118// !defined(AMREX_USE_GPU)
1119template <typename T, std::integral N, typename FIN, typename FOUT, typename TYPE>
1120requires (std::same_as<std::decay_t<TYPE>,Type::Inclusive> ||
1121 std::same_as<std::decay_t<TYPE>,Type::Exclusive>)
1122T PrefixSum (N n, FIN const& fin, FOUT const& fout, TYPE, RetSum = retSum)
1123{
1124 if (n <= 0) { return 0; }
1125 T totalsum = 0;
1126 for (N i = 0; i < n; ++i) {
1127 T x = fin(i);
1128 T y = totalsum;
1129 totalsum += x;
1130 AMREX_IF_CONSTEXPR (std::is_same_v<std::decay_t<TYPE>,Type::Inclusive>) {
1131 y += x;
1132 }
1133 fout(i, y);
1134 }
1135 return totalsum;
1136}
1137
1138// The return value is the total sum.
1139template <std::integral N, typename T >
1140T InclusiveSum (N n, T const* in, T * out, RetSum /*a_ret_sum*/ = retSum)
1141{
1142 std::inclusive_scan(in, in+n, out);
1143 return (n > 0) ? out[n-1] : T(0);
1144}
1145
1146// The return value is the total sum.
1147template <std::integral N, typename T >
1148T ExclusiveSum (N n, T const* in, T * out, RetSum /*a_ret_sum*/ = retSum)
1149{
1150 if (n <= 0) { return 0; }
1151
1152 auto in_last = in[n-1];
1153 std::exclusive_scan(in, in+n, out, T(0));
1154 return in_last + out[n-1];
1155}
1156
1157#endif
1158
1159}
1160
1161namespace Gpu
1162{
1164 template<class InIter, class OutIter>
1165 OutIter inclusive_scan (InIter begin, InIter end, OutIter result)
1166 {
1167#if defined(AMREX_USE_GPU)
1168 auto N = std::distance(begin, end);
1169 if (N <= 0) { return result; }
1170 Scan::InclusiveSum(N, &(*begin), &(*result), Scan::noRetSum);
1171 OutIter result_end = result;
1172 std::advance(result_end, N);
1173 return result_end;
1174#else
1175 return std::inclusive_scan(begin, end, result);
1176#endif
1177 }
1178
1180 template<class InIter, class OutIter>
1181 OutIter exclusive_scan (InIter begin, InIter end, OutIter result)
1182 {
1183#if defined(AMREX_USE_GPU)
1184 auto N = std::distance(begin, end);
1185 if (N <= 0) { return result; }
1186 Scan::ExclusiveSum(N, &(*begin), &(*result), Scan::noRetSum);
1187 OutIter result_end = result;
1188 std::advance(result_end, N);
1189 return result_end;
1190#else
1191 using T = typename std::iterator_traits<InIter>::value_type;
1192 return std::exclusive_scan(begin, end, result, T(0));
1193#endif
1194 }
1195
1196}}
1197
1198#endif
Memory arena base class and global arena accessors.
#define AMREX_ALWAYS_ASSERT(EX)
Definition AMReX_BLassert.H:50
Compiler- and backend-specific extension macros (e.g., restrict, SIMD, inline).
#define AMREX_FORCE_INLINE
Definition AMReX_Extension.H:124
#define AMREX_RESTRICT
Definition AMReX_Extension.H:37
#define AMREX_IF_CONSTEXPR
Definition AMReX_Extension.H:313
#define AMREX_GPU_SAFE_CALL(call)
Definition AMReX_GpuError.H:63
#define AMREX_GPU_ERROR_CHECK()
Definition AMReX_GpuError.H:151
#define AMREX_GPU_DEVICE
Definition AMReX_GpuQualifiers.H:18
Convenience header for the core AMReX GPU facilities.
Array4< int const > offset
Definition AMReX_HypreMLABecLap.cpp:1131
virtual void free(void *pt)=0
Free a previously allocated block pointed to by pt.
virtual void * alloc(std::size_t sz)=0
Allocate sz bytes from this arena.
static std::size_t align(std::size_t sz)
Return the smallest multiple of Arena::align_size that is >= sz bytes.
Definition AMReX_Arena.cpp:167
static constexpr int warp_size
Definition AMReX_GpuDevice.H:236
amrex_long Long
Definition AMReX_INT.H:30
OutIter exclusive_scan(InIter begin, InIter end, OutIter result)
Definition AMReX_Scan.H:1181
T InclusiveSum(N n, T const *in, T *out, RetSum a_ret_sum=retSum)
Inclusive sum.
Definition AMReX_Scan.H:973
OutIter inclusive_scan(InIter begin, InIter end, OutIter result)
Definition AMReX_Scan.H:1165
T ExclusiveSum(N n, T const *in, T *out, RetSum a_ret_sum=retSum)
Exclusive sum.
Definition AMReX_Scan.H:1045
Arena * The_Pinned_Arena()
Definition AMReX_Arena.cpp:855
Arena * The_Arena()
Definition AMReX_Arena.cpp:815
__host__ __device__ constexpr const T & min(const T &a, const T &b) noexcept
Definition AMReX_Algorithm.H:31
__host__ __device__ AMREX_FORCE_INLINE T Add(T *sum, T value) noexcept
Definition AMReX_GpuAtomic.H:200
void freeAsync(Arena *arena, void *mem) noexcept
Definition AMReX_GpuDevice.H:345
void streamSynchronize() noexcept
Definition AMReX_GpuDevice.H:310
void dtoh_memcpy_async(void *p_h, const void *p_d, const std::size_t sz) noexcept
Definition AMReX_GpuDevice.H:435
gpuStream_t gpuStream() noexcept
Definition AMReX_GpuDevice.H:291
static constexpr struct amrex::Scan::Type::Exclusive exclusive
static constexpr struct amrex::Scan::Type::Inclusive inclusive
static constexpr RetSum noRetSum
Definition AMReX_Scan.H:35
static constexpr RetSum retSum
Definition AMReX_Scan.H:34
T PrefixSum(N n, FIN const &fin, FOUT const &fout, TYPE, RetSum a_ret_sum=retSum)
Definition AMReX_Scan.H:815
Definition AMReX_Amr.cpp:50
__host__ __device__ void ignore_unused(const Ts &...)
No-op helper that marks variables as intentionally unused.
Definition AMReX.H:259
__host__ __device__ Dim3 begin(BoxND< dim > const &box) noexcept
Return the iterator begin coordinate of box as Dim3.
Definition AMReX_Box.H:2239
void ParallelFor(TypeList< CTOs... > ctos, std::array< int, sizeof...(CTOs)> const &runtime_options, T N, F &&f)
Definition AMReX_CTOParallelForImpl.H:202
const int[]
Definition AMReX_BLProfiler.cpp:1665
__host__ __device__ Dim3 end(BoxND< dim > const &box) noexcept
Return the iterator end coordinate of box as Dim3.
Definition AMReX_Box.H:2257
Definition AMReX_Scan.H:30
bool flag
Definition AMReX_Scan.H:31
Definition AMReX_Scan.H:39
Definition AMReX_Scan.H:38