Block-Structured AMR Software Framework
Loading...
Searching...
No Matches
AMReX_Reduce.H
Go to the documentation of this file.
1#ifndef AMREX_REDUCE_H_
2#define AMREX_REDUCE_H_
3#include <AMReX_Config.H>
4
5#include <AMReX_Concepts.H>
6#include <AMReX_Gpu.H>
7#include <AMReX_Arena.H>
8#include <AMReX_OpenMP.H>
9#include <AMReX_MFIter.H>
10#include <AMReX_TypeList.H>
11#include <AMReX_ValLocPair.H>
12
13#include <algorithm>
14#include <concepts>
15#include <functional>
16#include <limits>
17
18namespace amrex {
19
20namespace Reduce {
21
22// The declaration of these functions are here to work around doxygen issues.
23
38template <typename T, std::integral N>
39T Sum (N n, T const* v, T init_val = 0);
40
57template <typename T, std::integral N, typename F>
58requires (!std::same_as<T*,std::decay_t<F>>)
59T Sum (N n, F const& f, T init_val = 0);
60
75template <typename T, std::integral N>
76T Min (N n, T const* v, T init_val = std::numeric_limits<T>::max());
77
94template <typename T, std::integral N, typename F>
95requires (!std::same_as<T*,std::decay_t<F>>)
96T Min (N n, F const& f, T init_val = std::numeric_limits<T>::max());
97
112template <typename T, std::integral N>
113T Max (N n, T const* v, T init_val = std::numeric_limits<T>::lowest());
114
131template <typename T, std::integral N, typename F>
132requires (!std::same_as<T*,std::decay_t<F>>)
133T Max (N n, F const& f, T init_val = std::numeric_limits<T>::lowest());
134
148template <typename T, std::integral N>
149std::pair<T,T> MinMax (N n, T const* v);
150
166template <typename T, std::integral N, typename F>
167requires (!std::same_as<T*,std::decay_t<F>>)
168std::pair<T,T> MinMax (N n, F const& f);
169
185template <typename T, std::integral N, typename P>
186bool AnyOf (N n, T const* v, P const& pred);
187
201template <typename P, int dim>
202bool AnyOf (BoxND<dim> const& box, P const& pred);
203
204}
205
207namespace Reduce::detail {
208
209#ifdef AMREX_USE_GPU
210#ifdef AMREX_USE_SYCL
211 template <std::size_t I, typename T, typename P>
213 void for_each_parallel (T& d, T const& s, Gpu::Handler const& h)
214 {
215 P().parallel_update(amrex::get<I>(d), amrex::get<I>(s), h);
216 }
217
218 template <std::size_t I, typename T, typename P, typename P1, typename... Ps>
220 void for_each_parallel (T& d, T const& s, Gpu::Handler const& h)
221 {
222 P().parallel_update(amrex::get<I>(d), amrex::get<I>(s), h);
223 for_each_parallel<I+1,T,P1,Ps...>(d, s, h);
224 }
225#else
226 template <std::size_t I, typename T, typename P>
228 void for_each_parallel (T& d, T const& s)
229 {
230 P().parallel_update(amrex::get<I>(d), amrex::get<I>(s));
231 }
232
233 template <std::size_t I, typename T, typename P, typename P1, typename... Ps>
235 void for_each_parallel (T& d, T const& s)
236 {
237 P().parallel_update(amrex::get<I>(d), amrex::get<I>(s));
238 for_each_parallel<I+1,T,P1,Ps...>(d, s);
239 }
240#endif
241#endif
242
243 template <std::size_t I, typename T, typename P>
245 void for_each_local (T& d, T const& s)
246 {
247 P().local_update(amrex::get<I>(d), amrex::get<I>(s));
248 }
249
250 template <std::size_t I, typename T, typename P, typename P1, typename... Ps>
252 void for_each_local (T& d, T const& s)
253 {
254 P().local_update(amrex::get<I>(d), amrex::get<I>(s));
255 for_each_local<I+1,T,P1,Ps...>(d, s);
256 }
257
258 template <std::size_t I, typename T, typename P>
260 constexpr void for_each_init (T& t)
261 {
262 P().init(amrex::get<I>(t));
263 }
264
265 template <std::size_t I, typename T, typename P, typename P1, typename... Ps>
267 constexpr void for_each_init (T& t)
268 {
269 P().init(amrex::get<I>(t));
270 for_each_init<I+1,T,P1,Ps...>(t);
271 }
272}
274
277{
278
279#ifdef AMREX_USE_GPU
280#ifdef AMREX_USE_SYCL
281 template <typename T>
283 void parallel_update (T& d, T const& s, Gpu::Handler const& h) const noexcept {
284 T r = Gpu::blockReduceSum(s,h);
285 if (h.threadIdx() == 0) { d += r; }
286 }
287#else
288 template <typename T, int MT=AMREX_GPU_MAX_THREADS>
290 void parallel_update (T& d, T const& s) const noexcept {
291 T r = Gpu::blockReduceSum<MT>(s);
292 if (threadIdx.x == 0) { d += r; }
293 }
294#endif
295#endif
296
297 template <typename T>
299 void local_update (T& d, T const& s) const noexcept { d += s; }
300
301 template <typename T>
302 constexpr void init (T& t) const noexcept { t = 0; }
303};
304
307{
308#ifdef AMREX_USE_GPU
309#ifdef AMREX_USE_SYCL
310 template <typename T>
312 void parallel_update (T& d, T const& s, Gpu::Handler const& h) const noexcept {
313 T r = Gpu::blockReduceMin(s,h);
314 if (h.threadIdx() == 0) { d = amrex::min(d,r); }
315 }
316#else
317 template <typename T, int MT=AMREX_GPU_MAX_THREADS>
319 void parallel_update (T& d, T const& s) const noexcept {
320 T r = Gpu::blockReduceMin<MT>(s);
321 if (threadIdx.x == 0) { d = amrex::min(d,r); }
322 }
323#endif
324#endif
325
326 template <typename T>
328 void local_update (T& d, T const& s) const noexcept { d = amrex::min(d,s); }
329
330 template <typename T>
331 requires (std::numeric_limits<T>::is_specialized)
332 constexpr void init (T& t) const noexcept { t = std::numeric_limits<T>::max(); }
333
334 template <typename T>
335 requires (!std::numeric_limits<T>::is_specialized)
336 constexpr void init (T& t) const noexcept { t = T::max(); }
337};
338
341{
342#ifdef AMREX_USE_GPU
343#ifdef AMREX_USE_SYCL
344 template <typename T>
346 void parallel_update (T& d, T const& s, Gpu::Handler const& h) const noexcept {
347 T r = Gpu::blockReduceMax(s,h);
348 if (h.threadIdx() == 0) { d = amrex::max(d,r); }
349 }
350#else
351 template <typename T, int MT=AMREX_GPU_MAX_THREADS>
353 void parallel_update (T& d, T const& s) const noexcept {
354 T r = Gpu::blockReduceMax<MT>(s);
355 if (threadIdx.x == 0) { d = amrex::max(d,r); }
356 }
357#endif
358#endif
359
360 template <typename T>
362 void local_update (T& d, T const& s) const noexcept { d = amrex::max(d,s); }
363
364 template <typename T>
365 requires (std::numeric_limits<T>::is_specialized)
366 constexpr void init (T& t) const noexcept { t = std::numeric_limits<T>::lowest(); }
367
368 template <typename T>
369 requires (!std::numeric_limits<T>::is_specialized)
370 constexpr void init (T& t) const noexcept { t = T::lowest(); }
371};
372
375{
376#ifdef AMREX_USE_GPU
377#ifdef AMREX_USE_SYCL
378 template <std::integral T>
380 void parallel_update (T& d, T s, Gpu::Handler const& h) const noexcept {
382 if (h.threadIdx() == 0) { d = d && r; }
383 }
384#else
385 template <std::integral T, int MT=AMREX_GPU_MAX_THREADS>
387 void parallel_update (T& d, T s) const noexcept {
388 T r = Gpu::blockReduceLogicalAnd<MT>(s);
389 if (threadIdx.x == 0) { d = d && r; }
390 }
391#endif
392#endif
393
394 template <std::integral T>
396 void local_update (T& d, T s) const noexcept { d = d && s; }
397
398 template <std::integral T>
399 constexpr void init (T& t) const noexcept { t = true; }
400};
401
404{
405#ifdef AMREX_USE_GPU
406#ifdef AMREX_USE_SYCL
407 template <std::integral T>
409 void parallel_update (T& d, T s, Gpu::Handler const& h) const noexcept {
410 T r = Gpu::blockReduceLogicalOr(s,h);
411 if (h.threadIdx() == 0) { d = d || r; }
412 }
413#else
414 template <std::integral T, int MT=AMREX_GPU_MAX_THREADS>
416 void parallel_update (T& d, T s) const noexcept {
417 T r = Gpu::blockReduceLogicalOr<MT>(s);
418 if (threadIdx.x == 0) { d = d || r; }
419 }
420#endif
421#endif
422
423 template <std::integral T>
425 void local_update (T& d, T s) const noexcept { d = d || s; }
426
427 template <std::integral T>
428 constexpr void init (T& t) const noexcept { t = false; }
429};
430
431template <typename... Ps> class ReduceOps;
432
433#ifdef AMREX_USE_GPU
434
436template <typename... Ts>
438{
439public:
440 using Type = GpuTuple<Ts...>;
441
442 template <typename... Ps>
443 explicit ReduceData (ReduceOps<Ps...>& reduce_op)
444 : m_max_blocks(Gpu::Device::maxBlocksPerLaunch()),
445 m_host_tuple((Type*)(The_Pinned_Arena()->alloc(sizeof(Type)))),
446 m_device_tuple((Type*)(The_Arena()->alloc((AMREX_GPU_MAX_STREAMS)
447 * m_max_blocks * sizeof(Type)))),
448 m_fn_value([&reduce_op,this] () -> Type { return this->value(reduce_op); })
449 {
450 reduce_op.resetResultReadiness();
451 static_assert(std::is_trivially_copyable<Type>(),
452 "ReduceData::Type must be trivially copyable");
453 static_assert(std::is_trivially_destructible<Type>(),
454 "ReduceData::Type must be trivially destructible");
455
456 new (m_host_tuple) Type();
457 m_nblocks.fill(0);
458 }
459
462 !m_used_external_stream || m_value_called,
463 "ReduceData used on an external GPU stream must call value() before destruction.");
464 The_Pinned_Arena()->free(m_host_tuple);
465 The_Arena()->free(m_device_tuple);
466 }
467
468 ReduceData (ReduceData<Ts...> const&) = delete;
470 void operator= (ReduceData<Ts...> const&) = delete;
471 void operator= (ReduceData<Ts...> &&) = delete;
472
474 {
475 Type r = m_fn_value();
476 m_value_called = true;
477 return r;
478 }
479
480 template <typename... Ps>
482 {
483 Type r = reduce_op.value(*this);
484 m_value_called = true;
485 return r;
486 }
487
488 Type* devicePtr () { return m_device_tuple; }
490 return m_device_tuple+streamIndexChecked(s)*m_max_blocks;
491 }
492
493 Type* hostPtr () { return m_host_tuple; }
494
496 int& nBlocks (gpuStream_t const& s) { return m_nblocks[streamIndexChecked(s)]; }
497
498 int maxBlocks () const { return m_max_blocks; }
499
500 int maxStreamIndex () const { return m_max_stream_index; }
502 m_max_stream_index = std::max(m_max_stream_index,streamIndexChecked(s));
503 }
504
505 void markValueCalled () noexcept { m_value_called = true; }
506
507private:
508 int streamIndexChecked (gpuStream_t const& s)
509 {
510 int const idx = Gpu::Device::streamIndex(s);
511 m_used_external_stream = m_used_external_stream || Gpu::Device::usingExternalStream();
512 if (idx == 0) {
513 if (m_stream_index_zero_set) {
514 AMREX_ALWAYS_ASSERT_WITH_MESSAGE(m_stream_index_zero == s,
515 "ReduceData cannot be reused across different external GPU streams "
516 "or between an external GPU stream and AMReX stream 0.");
517 } else {
518 m_stream_index_zero = s;
519 m_stream_index_zero_set = true;
520 }
521 }
522 return idx;
523 }
524
525 int m_max_blocks;
526 int m_max_stream_index = 0;
527 Type* m_host_tuple = nullptr;
528 Type* m_device_tuple = nullptr;
529 GpuArray<int,AMREX_GPU_MAX_STREAMS> m_nblocks;
530 gpuStream_t m_stream_index_zero{};
531 bool m_stream_index_zero_set = false;
532 bool m_used_external_stream = false;
533 bool m_value_called = false;
534 std::function<Type()> m_fn_value;
535};
536
538namespace Reduce::detail {
539
540 // call_f_intvect_box
541
542 template <typename F, int dim>
544 auto call_f_intvect_box (F const& f, IntVectND<dim> iv, IndexTypeND<dim>) noexcept ->
545 decltype(amrex::detail::call_f_intvect_inner(std::make_index_sequence<dim>(), f, iv))
546 {
547 return amrex::detail::call_f_intvect_inner(std::make_index_sequence<dim>(), f, iv);
548 }
549
550 template <typename F, int dim>
552 auto call_f_intvect_box (F const& f, IntVectND<dim> iv, IndexTypeND<dim> t) noexcept ->
553 decltype(f(BoxND<dim>(iv, iv, t)))
554 {
555 return f(BoxND<dim>(iv, iv, t));
556 }
557
558 // call_f_intvect_n
559 template <typename F, typename T, int dim>
561 auto call_f_intvect_n (F const& f, IntVectND<dim> iv, T n) noexcept ->
562 decltype(amrex::detail::call_f_intvect_inner(std::make_index_sequence<dim>(), f, iv, n))
563 {
564 return amrex::detail::call_f_intvect_inner(std::make_index_sequence<dim>(), f, iv, n);
565 }
566
567 // mf_call_f
568
569 struct iterate_box {};
570 struct iterate_box_comp {};
571
572 template <typename I, typename F, typename T, typename... Ps>
573 requires (std::same_as<iterate_box,I>)
575 void mf_call_f (F const& f, int ibox, int i, int j, int k, int, T& r) noexcept
576 {
577 auto const& pr = f(ibox,i,j,k);
578 Reduce::detail::for_each_local<0, T, Ps...>(r, pr);
579 }
580
581 template <typename I, typename F, typename T, typename... Ps>
582 requires (std::same_as<iterate_box_comp,I>)
584 void mf_call_f (F const& f, int ibox, int i, int j, int k, int ncomp, T& r) noexcept
585 {
586 for (int n = 0; n < ncomp; ++n) {
587 auto const& pr = f(ibox,i,j,k,n);
588 Reduce::detail::for_each_local<0, T, Ps...>(r, pr);
589 }
590 }
591}
593
595template <typename... Ps>
597{
598public:
599
601
602 // This is public for CUDA
603 template <typename I, typename MF, typename D, typename F>
604 void eval_mf (I, MF const& mf, IntVect const& nghost, int ncomp, D& reduce_data, F const& f)
605 {
606 using ReduceTuple = typename D::Type;
607 const int nboxes = mf.local_size();
608 if (nboxes > 0) {
609 auto const& parforinfo = mf.getParForInfo(nghost);
610 auto nblocks_per_box = parforinfo.getNBlocksPerBox(AMREX_GPU_MAX_THREADS);
611 AMREX_ASSERT(Long(nblocks_per_box)*Long(nboxes) < Long(std::numeric_limits<int>::max()));
612 const int nblocks = nblocks_per_box * nboxes;
613 const BoxIndexer* dp_boxes = parforinfo.getBoxes();
614
615 auto const& stream = Gpu::gpuStream();
616 auto pdst = reduce_data.devicePtr(stream);
617 int nblocks_ec = std::min(nblocks, reduce_data.maxBlocks());
618 AMREX_ASSERT(Long(nblocks_ec)*2 <= Long(std::numeric_limits<int>::max()));
619 int& nblocks_ref = reduce_data.nBlocks(stream);
620 auto old_nblocks = static_cast<unsigned int>(nblocks_ref);
621 nblocks_ref = amrex::max(nblocks_ref, nblocks_ec);
622 reduce_data.updateMaxStreamIndex(stream);
623
624#ifdef AMREX_USE_SYCL
625 // device reduce needs local(i.e., shared) memory
626 constexpr std::size_t shared_mem_bytes = sizeof(ReduceTuple)
627 * std::max(Gpu::Device::warp_size, AMREX_GPU_MAX_THREADS/Gpu::Device::warp_size);
628 amrex::launch<AMREX_GPU_MAX_THREADS>(nblocks_ec, shared_mem_bytes, stream,
629 [=] AMREX_GPU_DEVICE (Gpu::Handler const& gh) noexcept
630 {
631 Dim1 blockIdx {gh.blockIdx()};
632 Dim1 threadIdx{gh.threadIdx()};
633#else
634 amrex::launch_global<AMREX_GPU_MAX_THREADS>
635 <<<nblocks_ec, AMREX_GPU_MAX_THREADS, 0, stream>>>
636 ([=] AMREX_GPU_DEVICE () noexcept
637 {
638#endif
639 ReduceTuple r;
640 Reduce::detail::for_each_init<0, ReduceTuple, Ps...>(r);
641 ReduceTuple& dst = pdst[blockIdx.x];
642 if (threadIdx.x == 0 && blockIdx.x >= old_nblocks) {
643 dst = r;
644 }
645 for (int iblock = blockIdx.x; iblock < nblocks; iblock += nblocks_ec) {
646 int ibox = iblock / nblocks_per_box;
647 auto icell = std::uint64_t(iblock-ibox*nblocks_per_box)*AMREX_GPU_MAX_THREADS + threadIdx.x;
648
649 BoxIndexer const& indexer = dp_boxes[ibox];
650 if (icell < indexer.numPts()) {
651 auto [i, j, k] = indexer(icell);
652 Reduce::detail::mf_call_f<I, F, ReduceTuple, Ps...>
653 (f, ibox, i, j, k, ncomp, r);
654 }
655 }
656#ifdef AMREX_USE_SYCL
657 Reduce::detail::for_each_parallel<0, ReduceTuple, Ps...>(dst, r, gh);
658#else
659 Reduce::detail::for_each_parallel<0, ReduceTuple, Ps...>(dst, r);
660#endif
661 });
662 }
663 }
664
665 // This is public for CUDA
666 template <typename I, int dim, typename D, typename F>
667 void eval_box (I, BoxND<dim> const& box, int ncomp, D& reduce_data, F const& f)
668 {
669 if (box.isEmpty()) { return; }
670 using ReduceTuple = typename D::Type;
671 auto const& stream = Gpu::gpuStream();
672 auto dp = reduce_data.devicePtr(stream);
673 int& nblocks = reduce_data.nBlocks(stream);
674 const BoxIndexerND<dim> indexer(box);
675 IndexTypeND<dim> ixtype = box.ixType();
676 constexpr int nitems_per_thread = 4;
677 Long nblocks_ec = (box.numPts() + nitems_per_thread*AMREX_GPU_MAX_THREADS-1)
678 / (nitems_per_thread*AMREX_GPU_MAX_THREADS);
679 nblocks_ec = std::min<Long>(nblocks_ec, reduce_data.maxBlocks());
680 reduce_data.updateMaxStreamIndex(stream);
681#ifdef AMREX_USE_SYCL
682 // device reduce needs local(i.e., shared) memory
683 constexpr std::size_t shared_mem_bytes = sizeof(ReduceTuple)
684 * std::max(Gpu::Device::warp_size, AMREX_GPU_MAX_THREADS/Gpu::Device::warp_size);
685 amrex::launch<AMREX_GPU_MAX_THREADS>(nblocks_ec, shared_mem_bytes, stream,
686 [=] AMREX_GPU_DEVICE (Gpu::Handler const& gh) noexcept
687 {
688 Dim1 blockIdx {gh.blockIdx()};
689 Dim1 threadIdx{gh.threadIdx()};
690 Dim1 gridDim {gh.gridDim()};
691#else
692 amrex::launch<AMREX_GPU_MAX_THREADS>(nblocks_ec, 0, stream,
693 [=] AMREX_GPU_DEVICE () noexcept
694 {
695#endif
696 ReduceTuple r;
697 Reduce::detail::for_each_init<0, ReduceTuple, Ps...>(r);
698 ReduceTuple& dst = *(dp+blockIdx.x);
699 if (threadIdx.x == 0 && static_cast<int>(blockIdx.x) >= nblocks) {
700 dst = r;
701 }
702 for (std::uint64_t icell = std::uint64_t(AMREX_GPU_MAX_THREADS)*blockIdx.x+threadIdx.x,
703 stride = std::uint64_t(AMREX_GPU_MAX_THREADS)*gridDim.x;
704 icell < indexer.numPts();
705 icell += stride)
706 {
707 auto iv = indexer.intVect(icell);
708 amrex::ignore_unused(f,ncomp,ixtype); // work around first-capture
709 if constexpr (std::is_same_v<Reduce::detail::iterate_box,I>) {
710 auto pr = Reduce::detail::call_f_intvect_box(f, iv, ixtype);
711 Reduce::detail::for_each_local<0, ReduceTuple, Ps...>(r, pr);
712 } else {
713 for (int n = 0; n < ncomp; ++n) {
714 auto pr = Reduce::detail::call_f_intvect_n(f, iv, n);
715 Reduce::detail::for_each_local<0, ReduceTuple, Ps...>(r, pr);
716 }
717 }
718 }
719#ifdef AMREX_USE_SYCL
720 Reduce::detail::for_each_parallel<0, ReduceTuple, Ps...>(dst, r, gh);
721#else
722 Reduce::detail::for_each_parallel<0, ReduceTuple, Ps...>(dst, r);
723#endif
724 });
725 nblocks = std::max(nblocks, static_cast<int>(nblocks_ec));
726 }
727
729
730 template <FabArrayType MF, typename D, typename F>
731#ifndef AMREX_USE_CUDA
733#endif
734 void eval (MF const& mf, IntVect const& nghost, D& reduce_data, F&& f)
735 {
736 using ReduceTuple = typename D::Type;
737 const int nboxes = mf.local_size();
738 if (nboxes == 0) {
739 return;
740 } else if (!mf.isFusingCandidate()) {
741 for (MFIter mfi(mf); mfi.isValid(); ++mfi) {
742 Box const& b = amrex::grow(mfi.validbox(), nghost);
743 const int li = mfi.LocalIndex();
744 this->eval(b, reduce_data,
745 [=] AMREX_GPU_DEVICE (int i, int j, int k) noexcept -> ReduceTuple
746 {
747 return f(li, i, j, k);
748 });
749 }
750 } else {
751 eval_mf(Reduce::detail::iterate_box{},
752 mf, nghost, 0, reduce_data, std::forward<F>(f));
753 }
754 }
755
756 template <FabArrayType MF, typename D, typename F>
757#ifndef AMREX_USE_CUDA
759#endif
760 void eval (MF const& mf, IntVect const& nghost, int ncomp, D& reduce_data, F&& f)
761 {
762 using ReduceTuple = typename D::Type;
763
764 const int nboxes = mf.local_size();
765
766 if (nboxes == 0) {
767 return;
768 } else if (!mf.isFusingCandidate()) {
769 for (MFIter mfi(mf); mfi.isValid(); ++mfi) {
770 Box const& b = amrex::grow(mfi.validbox(), nghost);
771 const int li = mfi.LocalIndex();
772 this->eval(b, ncomp, reduce_data,
773 [=] AMREX_GPU_DEVICE (int i, int j, int k, int n) noexcept -> ReduceTuple
774 {
775 return f(li, i, j, k, n);
776 });
777 }
778 } else {
779 eval_mf(Reduce::detail::iterate_box_comp{},
780 mf, nghost, ncomp, reduce_data, std::forward<F>(f));
781 }
782 }
783
784 template <typename D, typename F, int dim>
785 void eval (BoxND<dim> const& box, D & reduce_data, F const& f)
786 {
787 eval_box(Reduce::detail::iterate_box{}, box, 0, reduce_data, f);
788 }
789
790 template <std::integral N, typename D, typename F, int dim>
791 void eval (BoxND<dim> const& box, N ncomp, D & reduce_data, F const& f)
792 {
793 eval_box(Reduce::detail::iterate_box_comp{}, box, ncomp, reduce_data, f);
794 }
795
796 template <std::integral N, typename D, typename F>
797 void eval (N n, D & reduce_data, F const& f)
798 {
799 if (n <= 0) { return; }
800 using ReduceTuple = typename D::Type;
801 auto const& stream = Gpu::gpuStream();
802 auto dp = reduce_data.devicePtr(stream);
803 int& nblocks = reduce_data.nBlocks(stream);
804 constexpr int nitems_per_thread = 4;
805 Long nblocks_ec_long = (Long(n) + nitems_per_thread*AMREX_GPU_MAX_THREADS-1)
806 / (nitems_per_thread*AMREX_GPU_MAX_THREADS);
807 int nblocks_ec = static_cast<int>(std::min<Long>(nblocks_ec_long,
808 reduce_data.maxBlocks()));
809 reduce_data.updateMaxStreamIndex(stream);
810#ifdef AMREX_USE_SYCL
811 // device reduce needs local(i.e., shared) memory
812 constexpr std::size_t shared_mem_bytes = sizeof(ReduceTuple)
813 * std::max(Gpu::Device::warp_size, AMREX_GPU_MAX_THREADS/Gpu::Device::warp_size);
814 amrex::launch<AMREX_GPU_MAX_THREADS>(nblocks_ec, shared_mem_bytes, stream,
815 [=] AMREX_GPU_DEVICE (Gpu::Handler const& gh) noexcept
816 {
817 Dim1 blockIdx {gh.blockIdx()};
818 Dim1 threadIdx{gh.threadIdx()};
819 Dim1 gridDim {gh.gridDim()};
820#else
821 amrex::launch<AMREX_GPU_MAX_THREADS>(nblocks_ec, 0, stream,
822 [=] AMREX_GPU_DEVICE () noexcept
823 {
824#endif
825 ReduceTuple r;
826 Reduce::detail::for_each_init<0, ReduceTuple, Ps...>(r);
827 ReduceTuple& dst = *(dp+blockIdx.x);
828 if (threadIdx.x == 0 && static_cast<int>(blockIdx.x) >= nblocks) {
829 dst = r;
830 }
831 for (Long i = Long(AMREX_GPU_MAX_THREADS)*blockIdx.x+threadIdx.x,
832 stride = Long(AMREX_GPU_MAX_THREADS)*gridDim.x;
833 i < Long(n);
834 i += stride)
835 {
836 auto pr = f(N(i));
837 Reduce::detail::for_each_local<0, ReduceTuple, Ps...>(r,pr);
838 }
839#ifdef AMREX_USE_SYCL
840 Reduce::detail::for_each_parallel<0, ReduceTuple, Ps...>(dst, r, gh);
841#else
842 Reduce::detail::for_each_parallel<0, ReduceTuple, Ps...>(dst, r);
843#endif
844 });
845 nblocks = amrex::max(nblocks, nblocks_ec);
846 }
847
848 template <typename D>
849 typename D::Type value (D & reduce_data)
850 {
851 auto hp = reduce_data.hostPtr();
852
853 if (m_result_is_ready) {
854 reduce_data.markValueCalled();
855 return *hp;
856 }
857
858 using ReduceTuple = typename D::Type;
859 auto const& stream = Gpu::gpuStream();
860 auto dp = reduce_data.devicePtr();
861 auto const& nblocks = reduce_data.nBlocks();
862#if defined(AMREX_USE_SYCL)
863 if (reduce_data.maxStreamIndex() == 0 && nblocks[0] <= 4096) {
864 const int N = nblocks[0];
865 if (N == 0) {
866 Reduce::detail::for_each_init<0, ReduceTuple, Ps...>(*hp);
867 } else {
869 Gpu::dtoh_memcpy_async(tmp.data(), dp, sizeof(ReduceTuple)*N);
870 Gpu::streamSynchronize();
871 for (int i = 1; i < N; ++i) {
872 Reduce::detail::for_each_local<0, ReduceTuple, Ps...>(tmp[0], tmp[i]);
873 }
874 *hp = tmp[0];
875 }
876 } else
877#endif
878 {
879 int maxblocks = reduce_data.maxBlocks();
880#ifdef AMREX_USE_SYCL
881 // device reduce needs local(i.e., shared) memory
882 constexpr std::size_t shared_mem_bytes = sizeof(ReduceTuple)
883 * std::max(Gpu::Device::warp_size, AMREX_GPU_MAX_THREADS/Gpu::Device::warp_size);
884#ifndef AMREX_NO_SYCL_REDUCE_WORKAROUND
885 // xxxxx SYCL todo: reduce bug workaround
887 auto presult = dtmp.data();
888#else
889 auto presult = hp;
890#endif
891 amrex::launch<AMREX_GPU_MAX_THREADS>(1, shared_mem_bytes, stream,
892 [=] AMREX_GPU_DEVICE (Gpu::Handler const& gh) noexcept
893 {
894 ReduceTuple r;
895 Reduce::detail::for_each_init<0, ReduceTuple, Ps...>(r);
896 ReduceTuple dst = r;
897 for (int istream = 0, nstreams = nblocks.size(); istream < nstreams; ++istream) {
898 auto dp_stream = dp+istream*maxblocks;
899 for (int i = gh.item->get_global_id(0), stride = gh.item->get_global_range(0);
900 i < nblocks[istream]; i += stride) {
901 Reduce::detail::for_each_local<0, ReduceTuple, Ps...>(r, dp_stream[i]);
902 }
903 }
904 Reduce::detail::for_each_parallel<0, ReduceTuple, Ps...>(dst, r, gh);
905 if (gh.threadIdx() == 0) { *presult = dst; }
906 });
907#ifndef AMREX_NO_SYCL_REDUCE_WORKAROUND
908 Gpu::dtoh_memcpy_async(hp, dtmp.data(), sizeof(ReduceTuple));
909#endif
910#else
911 amrex::launch<AMREX_GPU_MAX_THREADS>(1, 0, stream,
912 [=] AMREX_GPU_DEVICE () noexcept
913 {
914 ReduceTuple r;
915 Reduce::detail::for_each_init<0, ReduceTuple, Ps...>(r);
916 ReduceTuple dst = r;
917 for (int istream = 0, nstreams = nblocks.size(); istream < nstreams; ++istream) {
918 auto dp_stream = dp+istream*maxblocks;
919 for (int i = AMREX_GPU_MAX_THREADS*blockIdx.x+threadIdx.x, stride = AMREX_GPU_MAX_THREADS*gridDim.x;
920 i < nblocks[istream]; i += stride) {
921 Reduce::detail::for_each_local<0, ReduceTuple, Ps...>(r, dp_stream[i]);
922 }
923 }
924 Reduce::detail::for_each_parallel<0, ReduceTuple, Ps...>(dst, r);
925 if (threadIdx.x == 0) { *hp = dst; }
926 });
927#endif
928 Gpu::streamSynchronize();
929 }
930
931 m_result_is_ready = true;
932 reduce_data.markValueCalled();
933 return *hp;
934 }
935
936private:
937 template <typename... T> friend class ReduceData;
938 bool m_result_is_ready = false;
939 void resetResultReadiness () { m_result_is_ready = false; }
940};
941
942namespace Reduce {
943
944template <typename T, std::integral N>
945T Sum (N n, T const* v, T init_val)
946{
947 ReduceOps<ReduceOpSum> reduce_op;
948 ReduceData<T> reduce_data(reduce_op);
949 using ReduceTuple = typename decltype(reduce_data)::Type;
950 reduce_op.eval(n, reduce_data, [=] AMREX_GPU_DEVICE (N i) -> ReduceTuple { return {v[i]}; });
951 ReduceTuple hv = reduce_data.value(reduce_op);
952 return amrex::get<0>(hv) + init_val;
953}
954
955template <typename T, std::integral N, typename F>
956requires (!std::same_as<T*,std::decay_t<F>>)
957T Sum (N n, F const& f, T init_val)
958{
959 ReduceOps<ReduceOpSum> reduce_op;
960 ReduceData<T> reduce_data(reduce_op);
961 using ReduceTuple = typename decltype(reduce_data)::Type;
962 reduce_op.eval(n, reduce_data, [=] AMREX_GPU_DEVICE (N i) -> ReduceTuple { return {f(i)}; });
963 ReduceTuple hv = reduce_data.value(reduce_op);
964 return amrex::get<0>(hv) + init_val;
965}
966
967template <typename T, std::integral N>
968T Min (N n, T const* v, T init_val)
969{
970 ReduceOps<ReduceOpMin> reduce_op;
971 ReduceData<T> reduce_data(reduce_op);
972 using ReduceTuple = typename decltype(reduce_data)::Type;
973 reduce_op.eval(n, reduce_data, [=] AMREX_GPU_DEVICE (N i) -> ReduceTuple { return {v[i]}; });
974 ReduceTuple hv = reduce_data.value(reduce_op);
975 return std::min(amrex::get<0>(hv),init_val);
976}
977
978template <typename T, std::integral N, typename F>
979requires (!std::same_as<T*,std::decay_t<F>>)
980T Min (N n, F const& f, T init_val)
981{
982 ReduceOps<ReduceOpMin> reduce_op;
983 ReduceData<T> reduce_data(reduce_op);
984 using ReduceTuple = typename decltype(reduce_data)::Type;
985 reduce_op.eval(n, reduce_data, [=] AMREX_GPU_DEVICE (N i) -> ReduceTuple { return {f(i)}; });
986 ReduceTuple hv = reduce_data.value(reduce_op);
987 return std::min(amrex::get<0>(hv),init_val);
988}
989
990template <typename T, std::integral N>
991T Max (N n, T const* v, T init_val)
992{
993 ReduceOps<ReduceOpMax> reduce_op;
994 ReduceData<T> reduce_data(reduce_op);
995 using ReduceTuple = typename decltype(reduce_data)::Type;
996 reduce_op.eval(n, reduce_data, [=] AMREX_GPU_DEVICE (N i) -> ReduceTuple { return {v[i]}; });
997 ReduceTuple hv = reduce_data.value(reduce_op);
998 return std::max(amrex::get<0>(hv),init_val);
999}
1000
1001template <typename T, std::integral N, typename F>
1002requires (!std::same_as<T*,std::decay_t<F>>)
1003T Max (N n, F const& f, T init_val)
1004{
1005 ReduceOps<ReduceOpMax> reduce_op;
1006 ReduceData<T> reduce_data(reduce_op);
1007 using ReduceTuple = typename decltype(reduce_data)::Type;
1008 reduce_op.eval(n, reduce_data, [=] AMREX_GPU_DEVICE (N i) -> ReduceTuple { return {f(i)}; });
1009 ReduceTuple hv = reduce_data.value(reduce_op);
1010 return std::max(amrex::get<0>(hv),init_val);
1011}
1012
1013template <typename T, std::integral N>
1014std::pair<T,T> MinMax (N n, T const* v)
1015{
1017 ReduceData<T,T> reduce_data(reduce_op);
1018 using ReduceTuple = typename decltype(reduce_data)::Type;
1019 reduce_op.eval(n, reduce_data, [=] AMREX_GPU_DEVICE (N i) -> ReduceTuple {
1020 return {v[i],v[i]};
1021 });
1022 auto hv = reduce_data.value(reduce_op);
1023 return std::make_pair(amrex::get<0>(hv), amrex::get<1>(hv));
1024}
1025
1026template <typename T, std::integral N, typename F>
1027requires (!std::same_as<T*,std::decay_t<F>>)
1028std::pair<T,T> MinMax (N n, F const& f)
1029{
1031 ReduceData<T,T> reduce_data(reduce_op);
1032 using ReduceTuple = typename decltype(reduce_data)::Type;
1033 reduce_op.eval(n, reduce_data, [=] AMREX_GPU_DEVICE (N i) -> ReduceTuple {
1034 T tmp = f(i);
1035 return {tmp,tmp};
1036 });
1037 auto hv = reduce_data.value(reduce_op);
1038 return std::make_pair(amrex::get<0>(hv), amrex::get<1>(hv));
1039}
1040
1041template <typename T, std::integral N, typename P>
1042bool AnyOf (N n, T const* v, P const& pred)
1043{
1044 Gpu::LaunchSafeGuard lsg(true);
1046 int* dp = ds.dataPtr();
1047 auto ec = Gpu::ExecutionConfig(n);
1048 ec.numBlocks.x = std::min(ec.numBlocks.x, Gpu::Device::maxBlocksPerLaunch());
1049
1050#ifdef AMREX_USE_SYCL
1051 const int num_ints = std::max(Gpu::Device::warp_size, int(ec.numThreads.x)/Gpu::Device::warp_size) + 1;
1052 const std::size_t shared_mem_bytes = num_ints*sizeof(int);
1053 amrex::launch<AMREX_GPU_MAX_THREADS>(ec.numBlocks.x, shared_mem_bytes, Gpu::gpuStream(),
1054 [=] AMREX_GPU_DEVICE (Gpu::Handler const& gh) noexcept {
1055 int* has_any = &(static_cast<int*>(gh.sharedMemory())[num_ints-1]);
1056 if (gh.threadIdx() == 0) { *has_any = *dp; }
1057 gh.sharedBarrier();
1058
1059 if (!(*has_any))
1060 {
1061 int r = false;
1062 for (Long i = Long(AMREX_GPU_MAX_THREADS)*gh.blockIdx()+gh.threadIdx(),
1063 stride = Long(AMREX_GPU_MAX_THREADS)*gh.gridDim();
1064 i < Long(n) && !r; i += stride)
1065 {
1066 r = pred(v[i]) ? 1 : 0;
1067 }
1068
1069 r = Gpu::blockReduce<Gpu::Device::warp_size>
1070 (r, Gpu::warpReduce<Gpu::Device::warp_size,int,amrex::Plus<int> >(), 0, gh);
1071 if (gh.threadIdx() == 0 && r) { *dp = 1; }
1072 }
1073 });
1074#else
1075 amrex::launch<AMREX_GPU_MAX_THREADS>(ec.numBlocks.x, 0, Gpu::gpuStream(),
1076 [=] AMREX_GPU_DEVICE () noexcept {
1077 __shared__ int has_any;
1078 if (threadIdx.x == 0) { has_any = *dp; }
1079 __syncthreads();
1080
1081 if (!has_any)
1082 {
1083 int r = false;
1084 for (Long i = Long(AMREX_GPU_MAX_THREADS)*blockIdx.x+threadIdx.x,
1085 stride = Long(AMREX_GPU_MAX_THREADS)*gridDim.x;
1086 i < Long(n) && !r; i += stride)
1087 {
1088 r = pred(v[i]) ? 1 : 0;
1089 }
1090 r = Gpu::blockReduce<Gpu::Device::warp_size>
1091 (r, Gpu::warpReduce<Gpu::Device::warp_size,int,amrex::Plus<int> >(), 0);
1092 if (threadIdx.x == 0 && r) *dp = 1;
1093 }
1094 });
1095#endif
1096 return ds.dataValue();
1097}
1098
1099template <typename P, int dim>
1100bool AnyOf (BoxND<dim> const& box, P const& pred)
1101{
1102 Gpu::LaunchSafeGuard lsg(true);
1104 int* dp = ds.dataPtr();
1105 const BoxIndexerND<dim> indexer(box);
1106 auto ec = Gpu::ExecutionConfig(box.numPts());
1107 ec.numBlocks.x = std::min(ec.numBlocks.x, Gpu::Device::maxBlocksPerLaunch());
1108
1109#ifdef AMREX_USE_SYCL
1110 const int num_ints = std::max(Gpu::Device::warp_size, int(ec.numThreads.x)/Gpu::Device::warp_size) + 1;
1111 const std::size_t shared_mem_bytes = num_ints*sizeof(int);
1112 amrex::launch<AMREX_GPU_MAX_THREADS>(ec.numBlocks.x, shared_mem_bytes, Gpu::gpuStream(),
1113 [=] AMREX_GPU_DEVICE (Gpu::Handler const& gh) noexcept {
1114 int* has_any = &(static_cast<int*>(gh.sharedMemory())[num_ints-1]);
1115 if (gh.threadIdx() == 0) { *has_any = *dp; }
1116 gh.sharedBarrier();
1117
1118 if (!(*has_any))
1119 {
1120 int r = false;
1121 for (std::uint64_t icell = std::uint64_t(AMREX_GPU_MAX_THREADS)*gh.blockIdx()+gh.threadIdx(),
1122 stride = std::uint64_t(AMREX_GPU_MAX_THREADS)*gh.gridDim();
1123 icell < indexer.numPts() && !r;
1124 icell += stride)
1125 {
1126 auto iv = indexer.intVect(icell);
1127 r = amrex::detail::call_f_intvect(pred, iv) ? 1 : 0;
1128 }
1129 r = Gpu::blockReduce<Gpu::Device::warp_size>
1130 (r, Gpu::warpReduce<Gpu::Device::warp_size,int,amrex::Plus<int> >(), 0, gh);
1131 if (gh.threadIdx() == 0 && r) { *dp = 1; }
1132 }
1133 });
1134#else
1135 AMREX_LAUNCH_KERNEL(AMREX_GPU_MAX_THREADS, ec.numBlocks, ec.numThreads, 0,
1136 Gpu::gpuStream(),
1137 [=] AMREX_GPU_DEVICE () noexcept {
1138 __shared__ int has_any;
1139 if (threadIdx.x == 0) { has_any = *dp; }
1140 __syncthreads();
1141
1142 if (!has_any)
1143 {
1144 int r = false;
1145 for (std::uint64_t icell = std::uint64_t(AMREX_GPU_MAX_THREADS)*blockIdx.x+threadIdx.x,
1146 stride = std::uint64_t(AMREX_GPU_MAX_THREADS)*gridDim.x;
1147 icell < indexer.numPts() && !r;
1148 icell += stride)
1149 {
1150 auto iv = indexer.intVect(icell);
1151 r = amrex::detail::call_f_intvect(pred, iv) ? 1 : 0;
1152 }
1153 r = Gpu::blockReduce<Gpu::Device::warp_size>
1154 (r, Gpu::warpReduce<Gpu::Device::warp_size,int,amrex::Plus<int> >(), 0);
1155 if (threadIdx.x == 0 && r) *dp = 1;
1156 }
1157 });
1158#endif
1159 return ds.dataValue();
1160}
1161
1162}
1163
1164#else
1165
1166template <typename... Ts>
1167class ReduceData
1168{
1169public:
1170 using Type = GpuTuple<Ts...>;
1171
1172 template <typename... Ps>
1173 explicit ReduceData (ReduceOps<Ps...>& reduce_op)
1174 : m_tuple(OpenMP::in_parallel() ? 1 : OpenMP::get_max_threads()),
1175 m_fn_value([&reduce_op,this] () -> Type { return this->value(reduce_op); })
1176 {
1177 reduce_op.resetResultReadiness();
1178 for (auto& t : m_tuple) {
1179 Reduce::detail::for_each_init<0, Type, Ps...>(t);
1180 }
1181 }
1182
1183 ~ReduceData () = default;
1184 ReduceData (ReduceData<Ts...> const&) = delete;
1185 ReduceData (ReduceData<Ts...> &&) = delete;
1186 void operator= (ReduceData<Ts...> const&) = delete;
1187 void operator= (ReduceData<Ts...> &&) = delete;
1188
1189 Type value () { return m_fn_value(); }
1190
1191 template <typename... Ps>
1192 Type value (ReduceOps<Ps...>& reduce_op)
1193 {
1194 return reduce_op.value(*this);
1195 }
1196
1197 Vector<Type>& reference () { return m_tuple; }
1198
1199 Type& reference (int tid)
1200 {
1201 if (m_tuple.size() == 1) {
1202 // No OpenMP or already inside OpenMP parallel when reduce_data is constructed
1203 return m_tuple[0];
1204 } else {
1205 return m_tuple[tid];
1206 }
1207 }
1208
1209private:
1210 Vector<Type> m_tuple;
1211 std::function<Type()> m_fn_value;
1212};
1213
1214namespace Reduce::detail {
1215
1216 // call_f_intvect
1217
1218 template <typename F, int dim>
1220 auto call_f_intvect (F const& f, IntVectND<dim> iv) noexcept ->
1221 decltype(amrex::detail::call_f_intvect_inner(std::make_index_sequence<dim>(), f, iv))
1222 {
1223 return amrex::detail::call_f_intvect_inner(std::make_index_sequence<dim>(), f, iv);
1224 }
1225
1226 // call_f_intvect_n
1227
1228 template <typename F, typename T, int dim>
1230 auto call_f_intvect_n (F const& f, IntVectND<dim> iv, T n) noexcept ->
1231 decltype(amrex::detail::call_f_intvect_inner(std::make_index_sequence<dim>(), f, iv, n))
1232 {
1233 return amrex::detail::call_f_intvect_inner(std::make_index_sequence<dim>(), f, iv, n);
1234 }
1235}
1236
1237template <typename... Ps>
1238class ReduceOps
1239{
1240private:
1241
1242 // call_f_box
1243
1244 template <typename D, typename F, int dim>
1245 requires (std::same_as<std::decay_t<decltype(
1246 Reduce::detail::call_f_intvect(std::declval<F const&>(), IntVectND<dim>()))>,
1247 typename D::Type>)
1249 static void call_f_box (BoxND<dim> const& box, typename D::Type & r, F const& f) noexcept
1250 {
1251 using ReduceTuple = typename D::Type;
1252 For(box,
1253 [&] (IntVectND<dim> iv) {
1254 auto pr = Reduce::detail::call_f_intvect(f, iv);
1255 Reduce::detail::for_each_local<0, ReduceTuple, Ps...>(r, pr);
1256 });
1257 }
1258
1259 template <typename D, typename F, int dim>
1260 requires (std::same_as<std::decay_t<decltype(
1261 std::declval<F const&>()(std::declval<BoxND<dim> const&>()))>,
1262 typename D::Type>)
1264 static void call_f_box (BoxND<dim> const& box, typename D::Type & r, F const& f) noexcept
1265 {
1266 using ReduceTuple = typename D::Type;
1267 Reduce::detail::for_each_local<0, ReduceTuple, Ps...>(r, f(box));
1268 }
1269
1270public:
1271
1272 template <FabArrayType MF, typename D, typename F>
1273 requires (IsCallable<F, int, int, int, int>::value)
1274 void eval (MF const& mf, IntVect const& nghost, D & reduce_data, F const& f)
1275 {
1276 using ReduceTuple = typename D::Type;
1277#ifdef AMREX_USE_OMP
1278#pragma omp parallel
1279#endif
1280 {
1281 ReduceTuple rr;
1282 Reduce::detail::for_each_init<0, ReduceTuple, Ps...>(rr);
1283 for (MFIter mfi(mf,true); mfi.isValid(); ++mfi) {
1284 Box const& b = mfi.growntilebox(nghost);
1285 const int li = mfi.LocalIndex();
1286 const auto lo = amrex::lbound(b);
1287 const auto hi = amrex::ubound(b);
1288 for (int k = lo.z; k <= hi.z; ++k) {
1289 for (int j = lo.y; j <= hi.y; ++j) {
1290 for (int i = lo.x; i <= hi.x; ++i) {
1291 Reduce::detail::for_each_local<0, ReduceTuple, Ps...>(rr, f(li,i,j,k));
1292 }}}
1293 }
1294 Reduce::detail::for_each_local<0, ReduceTuple, Ps...>(
1295 reduce_data.reference(OpenMP::get_thread_num()), rr);
1296 }
1297 }
1298
1299 template <FabArrayType MF, typename D, typename F>
1300 requires (IsCallable<F, int, int, int, int, int>::value)
1301 void eval (MF const& mf, IntVect const& nghost, int ncomp, D & reduce_data, F const& f)
1302 {
1303 using ReduceTuple = typename D::Type;
1304#ifdef AMREX_USE_OMP
1305#pragma omp parallel
1306#endif
1307 {
1308 ReduceTuple rr;
1309 Reduce::detail::for_each_init<0, ReduceTuple, Ps...>(rr);
1310 for (MFIter mfi(mf,true); mfi.isValid(); ++mfi) {
1311 Box const& b = mfi.growntilebox(nghost);
1312 const int li = mfi.LocalIndex();
1313 const auto lo = amrex::lbound(b);
1314 const auto hi = amrex::ubound(b);
1315 for (int n = 0; n < ncomp; ++n) {
1316 for (int k = lo.z; k <= hi.z; ++k) {
1317 for (int j = lo.y; j <= hi.y; ++j) {
1318 for (int i = lo.x; i <= hi.x; ++i) {
1319 Reduce::detail::for_each_local<0, ReduceTuple, Ps...>(rr, f(li,i,j,k,n));
1320 }}}}
1321 }
1322 Reduce::detail::for_each_local<0, ReduceTuple, Ps...>(
1323 reduce_data.reference(OpenMP::get_thread_num()), rr);
1324 }
1325 }
1326
1327 template <typename D, typename F, int dim>
1328 void eval (BoxND<dim> const& box, D & reduce_data, F&& f)
1329 {
1330 using ReduceTuple = typename D::Type;
1331 ReduceTuple rr;
1332 Reduce::detail::for_each_init<0, ReduceTuple, Ps...>(rr);
1333 call_f_box<D>(box, rr, std::forward<F>(f));
1334 Reduce::detail::for_each_local<0, ReduceTuple, Ps...>(
1335 reduce_data.reference(OpenMP::get_thread_num()), rr);
1336 }
1337
1338 template <std::integral N, typename D, typename F, int dim>
1339 void eval (BoxND<dim> const& box, N ncomp, D & reduce_data, F const& f)
1340 {
1341 using ReduceTuple = typename D::Type;
1342 ReduceTuple rr;
1343 Reduce::detail::for_each_init<0, ReduceTuple, Ps...>(rr);
1344 For(box, ncomp,
1345 [&] (IntVectND<dim> iv, int n) {
1346 auto pr = Reduce::detail::call_f_intvect_n(f, iv, n);
1347 Reduce::detail::for_each_local<0, ReduceTuple, Ps...>(rr, pr);
1348 });
1349 Reduce::detail::for_each_local<0, ReduceTuple, Ps...>(
1350 reduce_data.reference(OpenMP::get_thread_num()), rr);
1351 }
1352
1353 template <std::integral N, typename D, typename F>
1354 void eval (N n, D & reduce_data, F const& f)
1355 {
1356 using ReduceTuple = typename D::Type;
1357 ReduceTuple rr;
1358 Reduce::detail::for_each_init<0, ReduceTuple, Ps...>(rr);
1359 for (N i = 0; i < n; ++i) {
1360 Reduce::detail::for_each_local<0, ReduceTuple, Ps...>(rr, f(i));
1361 }
1362 Reduce::detail::for_each_local<0, ReduceTuple, Ps...>(
1363 reduce_data.reference(OpenMP::get_thread_num()), rr);
1364 }
1365
1366 template <typename D>
1367 typename D::Type value (D & reduce_data)
1368 {
1369 auto& rrv = reduce_data.reference();
1370 if (! m_result_is_ready) {
1371 using ReduceTuple = typename D::Type;
1372 if (rrv.size() > 1) {
1373 for (int i = 1, N = rrv.size(); i < N; ++i) {
1374 Reduce::detail::for_each_local<0, ReduceTuple, Ps...>(rrv[0], rrv[i]);
1375 }
1376 }
1377 m_result_is_ready = true;
1378 }
1379 return rrv[0];
1380 }
1381
1382private:
1383 template <typename... T> friend class ReduceData;
1384 bool m_result_is_ready = false;
1385 void resetResultReadiness () { m_result_is_ready = false; }
1386};
1387
1388namespace Reduce {
1389
1390template <typename T, std::integral N, typename F>
1391requires (!std::same_as<T*,std::decay_t<F>>)
1392T Sum (N n, F const& f, T init_val)
1393{
1394 T r = init_val;
1395#ifdef AMREX_USE_OMP
1396#pragma omp parallel for reduction(+:r)
1397#endif
1398 for (N i = 0; i < n; ++i) {
1399 r += f(i);
1400 }
1401 return r;
1402}
1403
1404template <typename T, std::integral N>
1405T Sum (N n, T const* v, T init_val)
1406{
1407 return Sum(n, [=] (N i) -> T { return v[i]; }, init_val);
1408}
1409
1410template <typename T, std::integral N, typename F>
1411requires (!std::same_as<T*,std::decay_t<F>>)
1412T Min (N n, F const& f, T init_val)
1413{
1414 T r = init_val;
1415#ifdef AMREX_USE_OMP
1416#pragma omp parallel for reduction(min:r)
1417#endif
1418 for (N i = 0; i < n; ++i) {
1419 r = std::min(r,f(i));
1420 }
1421 return r;
1422}
1423
1424template <typename T, std::integral N>
1425T Min (N n, T const* v, T init_val)
1426{
1427 return Reduce::Min(n, [=] (N i) -> T { return v[i]; }, init_val);
1428}
1429
1430template <typename T, std::integral N, typename F>
1431requires (!std::same_as<T*,std::decay_t<F>>)
1432T Max (N n, F const& f, T init_val)
1433{
1434 T r = init_val;
1435#ifdef AMREX_USE_OMP
1436#pragma omp parallel for reduction(max:r)
1437#endif
1438 for (N i = 0; i < n; ++i) {
1439 r = std::max(r,f(i));
1440 }
1441 return r;
1442}
1443
1444template <typename T, std::integral N>
1445T Max (N n, T const* v, T init_val)
1446{
1447 return Reduce::Max(n, [=] (N i) -> T { return v[i]; }, init_val);
1448}
1449
1450template <typename T, std::integral N, typename F>
1451requires (!std::same_as<T*,std::decay_t<F>>)
1452std::pair<T,T> MinMax (N n, F const& f)
1453{
1454 T r_min = std::numeric_limits<T>::max();
1455 T r_max = std::numeric_limits<T>::lowest();
1456#ifdef AMREX_USE_OMP
1457#pragma omp parallel for reduction(min:r_min) reduction(max:r_max)
1458#endif
1459 for (N i = 0; i < n; ++i) {
1460 T tmp = f(i);
1461 r_min = std::min(r_min,tmp);
1462 r_max = std::max(r_max,tmp);
1463 }
1464 return std::make_pair(r_min,r_max);
1465}
1466
1467template <typename T, std::integral N>
1468std::pair<T,T> MinMax (N n, T const* v)
1469{
1470 return Reduce::MinMax<T>(n, [=] (N i) -> T { return v[i]; });
1471}
1472
1473template <typename T, std::integral N, typename P>
1474bool AnyOf (N n, T const* v, P const& pred)
1475{
1476 return std::any_of(v, v+n, pred);
1477}
1478
1479template <typename P, int dim>
1480bool AnyOf (BoxND<dim> const& box, P const& pred)
1481{
1482 for (auto iv : box.iterator()) { // NOLINT(readability-use-anyofallof)
1483 if (Reduce::detail::call_f_intvect(pred, iv)) { return true; }
1484 }
1485 return false;
1486}
1487
1488}
1489
1490#endif
1491
1496template <typename... Ts, typename... Ps>
1498constexpr GpuTuple<Ts...>
1500{
1501 GpuTuple<Ts...> r{};
1502 Reduce::detail::for_each_init<0, decltype(r), Ps...>(r);
1503 return r;
1504}
1505
1510template <typename... Ts, typename... Ps>
1512constexpr GpuTuple<Ts...>
1514{
1515 GpuTuple<Ts...> r{};
1516 Reduce::detail::for_each_init<0, decltype(r), Ps...>(r);
1517 return r;
1518}
1519
1521template <typename Ops, typename Ts>
1522class ReducerImpl;
1523
1524template <typename... Ops, typename... Ts>
1525class ReducerImpl<TypeList<Ops...>, TypeList<Ts...>>
1526{
1527public:
1528 static_assert(sizeof...(Ops) > 0);
1529 static_assert(sizeof...(Ts) > 0);
1530 static_assert(sizeof...(Ops) == sizeof...(Ts));
1531
1532 ReducerImpl ()
1533 : m_reduce_data(m_reduce_op)
1534 {}
1535
1536protected:
1537 using Result_t = GpuTuple<Ts...>;
1538 ReduceOps<Ops...> m_reduce_op;
1539 ReduceData<Ts...> m_reduce_data;
1540};
1542
1607template <typename Ops, typename Ts>
1609 : public ReducerImpl<ToTypeList_t<Ops>, ToTypeList_t<Ts>>
1610{
1611 using Base = ReducerImpl<ToTypeList_t<Ops>, ToTypeList_t<Ts>>;
1612public:
1613
1614 using Result_t = typename Base::Result_t;
1615 static constexpr int size = GpuTupleSize<Result_t>::value;
1616
1617 Reducer () = default;
1618 ~Reducer () = default;
1619
1621 Reducer (Reducer const&) = delete;
1622 Reducer (Reducer &&) = delete;
1623 void operator= (Reducer const&) = delete;
1624 void operator= (Reducer &&) = delete;
1626
1640 template <typename F, int dim>
1643 void eval (BoxND<dim> const& box, F&& f)
1644 {
1645 this->m_reduce_op.eval(box, this->m_reduce_data, std::forward<F>(f));
1646 }
1647
1663 template <typename F, int dim>
1665 IsCallable<F, IntVectND<dim>, int>::value)
1666 void eval (BoxND<dim> const& box, int ncomp, F&& f)
1667 {
1668 this->m_reduce_op.eval(box, ncomp, this->m_reduce_data, std::forward<F>(f));
1669 }
1670
1690 template <FabArrayType MF, typename F>
1692 void eval (MF const& mf, IntVect const& nghost, F && f)
1693 {
1694 this->m_reduce_op.eval(mf, nghost, this->m_reduce_data, std::forward<F>(f));
1695 }
1696
1719 template <FabArrayType MF, typename F>
1721 void eval (MF const& mf, IntVect const& nghost, int ncomp, F && f)
1722 {
1723 this->m_reduce_op.eval(mf, nghost, ncomp, this->m_reduce_data, std::forward<F>(f));
1724 }
1725
1738 template <typename N, typename F>
1739 requires (IsCallable<F, N>::value)
1740 void eval (N n, F && f)
1741 {
1742 this->m_reduce_op.eval(n, this->m_reduce_data, std::forward<F>(f));
1743 }
1744
1755 [[nodiscard]] Result_t getResult ()
1756 {
1757 return this->m_reduce_data.value(this->m_reduce_op);
1758 }
1759};
1760
1761}
1762
1763#endif
Memory arena base class and global arena accessors.
#define AMREX_ALWAYS_ASSERT_WITH_MESSAGE(EX, MSG)
Definition AMReX_BLassert.H:49
#define AMREX_ASSERT(EX)
Definition AMReX_BLassert.H:38
#define AMREX_FORCE_INLINE
Definition AMReX_Extension.H:124
#define AMREX_GPU_MAX_STREAMS
Definition AMReX_GpuDevice.H:21
#define AMREX_LAUNCH_KERNEL(MT, blocks, threads, sharedMem, stream,...)
Definition AMReX_GpuLaunch.H:37
#define AMREX_GPU_DEVICE
Definition AMReX_GpuQualifiers.H:18
#define AMREX_GPU_HOST_DEVICE
Definition AMReX_GpuQualifiers.H:20
Convenience header for the core AMReX GPU facilities.
Real * pdst
Definition AMReX_HypreMLABecLap.cpp:1132
virtual void free(void *pt)=0
Free a previously allocated block pointed to by pt.
A Rectangular Domain on an Integer Lattice.
Definition AMReX_Box.H:54
__host__ __device__ bool isEmpty() const noexcept
Checks if it is an empty BoxND.
Definition AMReX_Box.H:223
__host__ __device__ Long numPts() const noexcept
Return the number of points contained in the BoxND.
Definition AMReX_Box.H:385
__host__ __device__ IndexTypeND< dim > ixType() const noexcept
Return the indexing type.
Definition AMReX_Box.H:148
GPU-compatible tuple.
Definition AMReX_Tuple.H:104
static int streamIndex(gpuStream_t s=gpuStream()) noexcept
Definition AMReX_GpuDevice.cpp:715
static bool usingExternalStream() noexcept
Definition AMReX_GpuDevice.cpp:842
Cell-Based or Node-Based Indices.
Definition AMReX_IndexType.H:36
Iterator for looping ever tiles and boxes of amrex::FabArray based containers.
Definition AMReX_MFIter.H:88
bool isValid() const noexcept
Is the iterator valid i.e. is it associated with a FAB?
Definition AMReX_MFIter.H:176
Dynamically allocated vector for trivially copyable data.
Definition AMReX_PODVector.H:308
T * data() noexcept
Definition AMReX_PODVector.H:672
Definition AMReX_Reduce.H:438
~ReduceData()
Definition AMReX_Reduce.H:460
int maxStreamIndex() const
Definition AMReX_Reduce.H:500
Type value()
Definition AMReX_Reduce.H:473
void updateMaxStreamIndex(gpuStream_t const &s)
Definition AMReX_Reduce.H:501
int & nBlocks(gpuStream_t const &s)
Definition AMReX_Reduce.H:496
ReduceData(ReduceOps< Ps... > &reduce_op)
Definition AMReX_Reduce.H:443
void markValueCalled() noexcept
Definition AMReX_Reduce.H:505
Type * devicePtr(gpuStream_t const &s)
Definition AMReX_Reduce.H:489
Type value(ReduceOps< Ps... > &reduce_op)
Definition AMReX_Reduce.H:481
Type * devicePtr()
Definition AMReX_Reduce.H:488
GpuArray< int, 8 > & nBlocks()
Definition AMReX_Reduce.H:495
ReduceData(ReduceData< Ts... > const &)=delete
Type * hostPtr()
Definition AMReX_Reduce.H:493
int maxBlocks() const
Definition AMReX_Reduce.H:498
ReduceData(ReduceData< Ts... > &&)=delete
Definition AMReX_Reduce.H:597
void eval(BoxND< dim > const &box, N ncomp, D &reduce_data, F const &f)
Definition AMReX_Reduce.H:791
D::Type value(D &reduce_data)
Definition AMReX_Reduce.H:849
void eval(BoxND< dim > const &box, D &reduce_data, F const &f)
Definition AMReX_Reduce.H:785
void eval(MF const &mf, IntVect const &nghost, D &reduce_data, F &&f)
Definition AMReX_Reduce.H:734
void eval(N n, D &reduce_data, F const &f)
Definition AMReX_Reduce.H:797
Class for local reductions (e.g., sum, min and max).
Definition AMReX_Reduce.H:1610
Reducer()=default
Result_t getResult()
Get the final reduction result.
Definition AMReX_Reduce.H:1755
typename Base::Result_t Result_t
Reduction result type, GpuTuple<U...>, where U... are the types in Ts.
Definition AMReX_Reduce.H:1614
~Reducer()=default
void eval(BoxND< dim > const &box, F &&f)
Reduction over a Box.
Definition AMReX_Reduce.H:1643
amrex_long Long
Definition AMReX_INT.H:30
T Min(N n, T const *v, T init_val=std::numeric_limits< T >::max())
Compute the minimum of an array of values.
Definition AMReX_Reduce.H:968
bool AnyOf(N n, T const *v, P const &pred)
Test whether any element in an array satisfies a unary predicate.
Definition AMReX_Reduce.H:1042
std::pair< T, T > MinMax(N n, T const *v)
Compute the minimum and maximum of an array of values.
Definition AMReX_Reduce.H:1014
T Max(N n, T const *v, T init_val=std::numeric_limits< T >::lowest())
Compute the maximum of an array of values.
Definition AMReX_Reduce.H:991
T Sum(N n, T const *v, T init_val=0)
Compute the sum of an array of values.
Definition AMReX_Reduce.H:945
__host__ __device__ Dim3 ubound(Array4< T > const &a) noexcept
Return the inclusive upper bounds of an Array4 in Dim3 form.
Definition AMReX_Array4.H:1365
__host__ __device__ Dim3 lbound(Array4< T > const &a) noexcept
Return the inclusive lower bounds of an Array4 in Dim3 form.
Definition AMReX_Array4.H:1351
__host__ __device__ BoxND< dim > grow(const BoxND< dim > &b, int i) noexcept
Return a copy of b grown uniformly by i cells in every direction.
Definition AMReX_Box.H:1326
Arena * The_Pinned_Arena()
Definition AMReX_Arena.cpp:855
Arena * The_Arena()
Definition AMReX_Arena.cpp:815
void Sum(Gpu::DeviceVector< T > &v, MPI_Comm comm)
Definition AMReX_GpuParallelReduce.H:37
__host__ __device__ constexpr const T & min(const T &a, const T &b) noexcept
Definition AMReX_Algorithm.H:31
__host__ __device__ constexpr const T & max(const T &a, const T &b) noexcept
Definition AMReX_Algorithm.H:53
__host__ __device__ AMREX_FORCE_INLINE T Max(T *const m, T const value) noexcept
Definition AMReX_GpuAtomic.H:419
__host__ __device__ AMREX_FORCE_INLINE T Min(T *const m, T const value) noexcept
Definition AMReX_GpuAtomic.H:356
__device__ int blockReduceLogicalOr(int source) noexcept
Definition AMReX_GpuReduce.H:559
__device__ T blockReduceMax(T source) noexcept
Definition AMReX_GpuReduce.H:458
__device__ T blockReduceMin(T source) noexcept
Definition AMReX_GpuReduce.H:403
__device__ int blockReduceLogicalAnd(int source) noexcept
Definition AMReX_GpuReduce.H:511
__device__ T blockReduceSum(T source) noexcept
Definition AMReX_GpuReduce.H:353
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
void For(TypeList< CTOs... > ctos, std::array< int, sizeof...(CTOs)> const &runtime_options, T N, F &&f)
Definition AMReX_CTOParallelForImpl.H:400
cudaStream_t gpuStream_t
Definition AMReX_GpuControl.H:79
__host__ __device__ constexpr GpuTuple< Ts... > IdentityTuple(GpuTuple< Ts... >, ReduceOps< Ps... >) noexcept
Return a GpuTuple containing the identity element for each operation in ReduceOps....
Definition AMReX_Reduce.H:1499
BoxND< 3 > Box
Box is an alias for amrex::BoxND instantiated with AMREX_SPACEDIM.
Definition AMReX_BaseFwd.H:35
typename ToTypeList< T >::type ToTypeList_t
Definition AMReX_TypeList.H:233
const int[]
Definition AMReX_BLProfiler.cpp:1665
Utility that maps flattened point indices back to IntVectND coordinates.
Definition AMReX_Box.H:2494
__host__ __device__ IntVectND< dim > intVect(std::uint64_t icell) const
Convert flattened point index icell to its IntVectND coordinate.
Definition AMReX_Box.H:2517
__host__ __device__ std::uint64_t numPts() const
Return the number of points covered by the indexed box.
Definition AMReX_Box.H:2552
Fixed-size array that can be used on GPU.
Definition AMReX_Array.H:52
Definition AMReX_Tuple.H:133
Definition AMReX_GpuMemory.H:57
T * dataPtr()
Definition AMReX_GpuMemory.H:91
T dataValue() const
Definition AMReX_GpuMemory.H:93
Definition AMReX_GpuLaunch.H:121
Definition AMReX_GpuTypes.H:88
Definition AMReX_GpuControl.H:127
Definition AMReX_GpuReduce.H:290
Test if a given type T is callable with arguments of type Args...
Definition AMReX_TypeTraits.H:208
Function object that returns the sum of two values.
Definition AMReX_Functional.H:23
Definition AMReX_Reduce.H:375
__host__ __device__ void local_update(T &d, T s) const noexcept
Definition AMReX_Reduce.H:396
constexpr void init(T &t) const noexcept
Definition AMReX_Reduce.H:399
__device__ void parallel_update(T &d, T s) const noexcept
Definition AMReX_Reduce.H:387
Definition AMReX_Reduce.H:404
__device__ void parallel_update(T &d, T s) const noexcept
Definition AMReX_Reduce.H:416
__host__ __device__ void local_update(T &d, T s) const noexcept
Definition AMReX_Reduce.H:425
constexpr void init(T &t) const noexcept
Definition AMReX_Reduce.H:428
Definition AMReX_Reduce.H:341
constexpr void init(T &t) const noexcept
Definition AMReX_Reduce.H:366
__host__ __device__ void local_update(T &d, T const &s) const noexcept
Definition AMReX_Reduce.H:362
__device__ void parallel_update(T &d, T const &s) const noexcept
Definition AMReX_Reduce.H:353
Definition AMReX_Reduce.H:307
constexpr void init(T &t) const noexcept
Definition AMReX_Reduce.H:332
__device__ void parallel_update(T &d, T const &s) const noexcept
Definition AMReX_Reduce.H:319
__host__ __device__ void local_update(T &d, T const &s) const noexcept
Definition AMReX_Reduce.H:328
Definition AMReX_Reduce.H:277
__device__ void parallel_update(T &d, T const &s) const noexcept
Definition AMReX_Reduce.H:290
__host__ __device__ void local_update(T &d, T const &s) const noexcept
Definition AMReX_Reduce.H:299
constexpr void init(T &t) const noexcept
Definition AMReX_Reduce.H:302
Struct for holding types.
Definition AMReX_TypeList.H:13