1#ifndef AMREX_PAR_REDUCE_H_
2#define AMREX_PAR_REDUCE_H_
3#include <AMReX_Config.H>
46template <
typename... Ops,
typename... Ts,
BaseFabType FAB,
typename F>
54 reduce_op.eval(fa, nghost, reduce_data, std::forward<F>(f));
55 return reduce_data.value(reduce_op);
96template <
typename Op,
typename T, BaseFabType FAB,
typename F>
104 reduce_op.
eval(fa, nghost, reduce_data, std::forward<F>(f));
105 auto const& hv = reduce_data.
value(reduce_op);
147template <
typename... Ops,
typename... Ts, BaseFabType FAB,
typename F>
148typename ReduceData<Ts...>::Type
155 reduce_op.eval(fa, nghost, ncomp, reduce_data, std::forward<F>(f));
156 return reduce_data.value(reduce_op);
194template <
typename Op,
typename T, BaseFabType FAB,
typename F>
202 reduce_op.
eval(fa, nghost, ncomp, reduce_data, std::forward<F>(f));
203 auto const& hv = reduce_data.
value(reduce_op);
241template <
typename... Ops,
typename... Ts, BaseFabType FAB,
typename F>
242typename ReduceData<Ts...>::Type
246 return ParReduce(operation_list, type_list, fa,
IntVect(0), std::forward<F>(f));
285template <
typename Op,
typename T, BaseFabType FAB,
typename F>
290 return ParReduce(operation_list, type_list, fa,
IntVect(0), std::forward<F>(f));
294namespace Reduce::detail {
299template <
bool PerBox,
typename... Ops>
302 template <
typename RT, BaseFabType FAB,
typename F>
304 eval (FabArray<FAB>
const& fa, IntVect
const& nghost,
int ncomp, F
const& f)
306 const int nlocal = fa.local_size();
307 const int nkeys = PerBox ? nlocal*ncomp : ncomp;
308 Vector<RT> r(std::max(nkeys,0));
309 for (
auto& x : r) { for_each_init<0, RT, Ops...>(x); }
310 if (nlocal == 0 || ncomp <= 0) {
return r; }
316 constexpr int MT = AMREX_GPU_MAX_THREADS;
317 const int maxblocks =
int(Gpu::Device::maxBlocksPerLaunch());
319 for (
int L = 0; L < nlocal; ++L) {
320 Box b = fa.box(fa.IndexArray()[L]);
324 const Long cells_per_block = std::max(
Long(MT)*4, (ntot*ncomp + maxblocks - 1) / maxblocks);
325 Gpu::PinnedVector<int> hoff(nlocal+1, 0);
326 for (
int L = 0; L < nlocal; ++L) {
327 Box b = fa.box(fa.IndexArray()[L]);
329 Long const nb = (npts + cells_per_block - 1) / cells_per_block;
331 hoff[L+1] = hoff[L] +
int(nb);
333 const int nb1 = hoff[nlocal];
335 const int nblocks = nb1*ncomp;
336 if (nblocks == 0) {
return r; }
338 auto const& stream = Gpu::gpuStream();
339 Gpu::DeviceVector<int> doff(nlocal+1);
340 Gpu::copyAsync(Gpu::hostToDevice, hoff.begin(), hoff.end(), doff.begin());
341 Gpu::DeviceVector<RT> dpart(nblocks);
342 Gpu::DeviceVector<RT> dr(nkeys);
343 int const* poff = doff.data();
344 RT* ppart = dpart.data();
346 BoxIndexer const* pbx = fa.getParForInfo(nghost).getBoxes();
349 constexpr std::size_t shared_mem_bytes =
sizeof(RT)
350 * std::max(Gpu::Device::warp_size, MT/Gpu::Device::warp_size);
354 Dim1 blockIdx {gh.blockIdx()};
355 Dim1 threadIdx{gh.threadIdx()};
356 Dim1 gridDim {gh.gridDim()};
362 for (
int b =
int(blockIdx.x); b < nblocks; b +=
int(gridDim.x)) {
363 const int n = b / nb1;
364 const int bb = b - n*nb1;
366 const int nb = poff[L+1] - poff[L];
369 for_each_init<0, RT, Ops...>(t);
370 for (
auto icell = std::uint64_t(bb-poff[L])*MT + threadIdx.x;
371 icell < indexer.numPts(); icell += std::uint64_t(nb)*MT)
373 auto [i, j, k] = indexer(icell);
374 for_each_local<0, RT, Ops...>(t, f(L,i,j,k,n));
377 if (threadIdx.x == 0) { for_each_init<0, RT, Ops...>(dst); }
379 for_each_parallel<0, RT, Ops...>(dst, t, gh);
381 for_each_parallel<0, RT, Ops...>(dst, t);
391 Dim1 blockIdx {gh.blockIdx()};
392 Dim1 threadIdx{gh.threadIdx()};
393 Dim1 gridDim {gh.gridDim()};
399 for (
int key =
int(blockIdx.x); key < nkeys; key +=
int(gridDim.x)) {
403 const int L = key / ncomp;
404 const int n = key - L*ncomp;
405 begin = n*nb1 + poff[L];
406 end = n*nb1 + poff[L+1];
412 for_each_init<0, RT, Ops...>(t);
413 for (
int ib = begin +
int(threadIdx.x); ib < end; ib += MT) {
414 for_each_local<0, RT, Ops...>(t, ppart[ib]);
417 if (threadIdx.x == 0) { for_each_init<0, RT, Ops...>(dst); }
419 for_each_parallel<0, RT, Ops...>(dst, t, gh);
421 for_each_parallel<0, RT, Ops...>(dst, t);
426 Gpu::dtoh_memcpy_async(r.data(), pr,
sizeof(RT)*nkeys);
427 Gpu::streamSynchronize();
429 if constexpr (PerBox) {
434 constexpr int d = AMREX_SPACEDIM-1;
435 const int nthreads = OpenMP::in_parallel() ? 1 : OpenMP::get_max_threads();
436 const bool split = nthreads > 1 && nkeys < 4*nthreads;
437 Vector<Box> bx(nlocal);
438 Vector<int> thick(nlocal), nchunk(nlocal), ioff(nlocal+1, 0);
439 for (
int L = 0; L < nlocal; ++L) {
440 bx[L] = fa.box(fa.IndexArray()[L]);
441 if (bx[L].ok()) { bx[L].grow(nghost); }
443 const int len = bx[L].ok() ? bx[L].length(d) : 1;
444 thick[L] = (
split && bx[L].ok()) ?
int(std::max(
Long(1),
Long(16384)/(bx[L].numPts()/len))) : len;
445 nchunk[L] = (len + thick[L] - 1) / thick[L];
446 ioff[L+1] = ioff[L] + nchunk[L]*ncomp;
449 const int nitems = ioff[nlocal];
450 Vector<RT> part(split ? nitems : 0);
451 RT* pr =
split ? part.data() : r.data();
453#pragma omp parallel for schedule(dynamic) if (!system::regtest_reduction)
455 for (
int item = 0; item < nitems; ++item) {
457 const int m = item - ioff[L];
458 const int n = m / nchunk[L];
459 const int c = m - n*nchunk[L];
461 b.
setSmall(d, bx[L].smallEnd(d) + c*thick[L]);
462 b.
setBig(d, std::min(b.smallEnd(d)+thick[L]-1, bx[L].bigEnd(d)));
466 for_each_init<0, RT, Ops...>(rem);
468 lanes_set<0>(acc, rem);
469 for (
int k = lo.z; k <= hi.z; ++k) {
470 for (
int j = lo.y; j <= hi.y; ++j) {
471 reduce_row<Ops...>(lo.x, hi.x, acc, rem,
472 [&] (
int i) {
return f(L,i,j,k,n); });
475 for_each_init<0, RT, Ops...>(
x);
476 lanes_fold<0, RT, Ops...>(acc,
x);
477 for_each_local<0, RT, Ops...>(
x, rem);
480 for (
int L = 0; L < nlocal; ++L) {
481 for (
int m = 0; m < nchunk[L]*ncomp; ++m) {
482 for_each_local<0, RT, Ops...>(r[L*ncomp+m/nchunk[L]], part[ioff[L]+m]);
487 const int nthreads = OpenMP::in_parallel() ? 1 : OpenMP::get_max_threads();
488 Vector<RT> rt(std::size_t(nthreads)*ncomp);
489 for (
auto& x : rt) { for_each_init<0, RT, Ops...>(
x); }
491#pragma omp parallel if (nthreads > 1 && !system::regtest_reduction)
494 RT* rr = rt.data() + std::size_t(nthreads > 1 ? OpenMP::get_thread_num() : 0)*ncomp;
496 for (
int n = 0; n < ncomp; ++n) {
498 for_each_init<0, RT, Ops...>(rem);
500 lanes_set<0>(acc, rem);
501 for (MFIter mfi(fa,
true); mfi.isValid(); ++mfi) {
502 Box const& b = mfi.growntilebox(nghost);
503 const int L = mfi.LocalIndex();
506 for (
int k = lo.z; k <= hi.z; ++k) {
507 for (
int j = lo.y; j <= hi.y; ++j) {
508 reduce_row<Ops...>(lo.x, hi.x, acc, rem,
509 [&] (
int i) {
return f(L,i,j,k,n); });
512 lanes_fold<0, RT, Ops...>(acc, rr[n]);
513 for_each_local<0, RT, Ops...>(rr[n], rem);
516 for (
int it = 0; it < nthreads; ++it) {
517 for (
int n = 0; n < ncomp; ++n) {
518 for_each_local<0, RT, Ops...>(r[n], rt[std::size_t(it)*ncomp+n]);
561template <
typename... Ops,
typename... Ts, BaseFabType FAB,
typename F>
562Vector<
typename ReduceData<Ts...>::Type>
567 return Reduce::detail::ParReduceComp<
false,Ops...>::template
568 eval<
typename ReduceData<Ts...>::Type>(fa, nghost, ncomp, std::forward<F>(f));
576template <
typename Op,
typename T, BaseFabType FAB,
typename F>
582 auto const& rt = Reduce::detail::ParReduceComp<false,Op>::template
583 eval<typename ReduceData<T>::Type>(fa, nghost, ncomp, std::forward<F>(f));
585 for (
int i = 0, N =
int(rt.size()); i < N; ++i) { r[i] =
amrex::get<0>(rt[i]); }
598template <
typename... Ops,
typename... Ts, BaseFabType FAB,
typename F>
599Vector<
typename ReduceData<Ts...>::Type>
604 return Reduce::detail::ParReduceComp<
true,Ops...>::template
605 eval<
typename ReduceData<Ts...>::Type>(fa, nghost, ncomp, std::forward<F>(f));
613template <
typename Op,
typename T, BaseFabType FAB,
typename F>
619 auto const& rt = Reduce::detail::ParReduceComp<true,Op>::template
620 eval<typename ReduceData<T>::Type>(fa, nghost, ncomp, std::forward<F>(f));
622 for (
int i = 0, N =
int(rt.size()); i < N; ++i) { r[i] =
amrex::get<0>(rt[i]); }
#define AMREX_ALWAYS_ASSERT(EX)
Definition AMReX_BLassert.H:50
#define AMREX_GPU_DEVICE
Definition AMReX_GpuQualifiers.H:18
__host__ __device__ BoxND & setBig(const IntVectND< dim > &bg) noexcept
Redefine the big end of the BoxND.
Definition AMReX_Box.H:516
__host__ __device__ bool ok() const noexcept
Return true if high bounds are >= low bounds and the index type is valid.
Definition AMReX_Box.H:229
__host__ __device__ BoxND & setSmall(const IntVectND< dim > &sm) noexcept
Definition AMReX_Box.H:508
An Array of FortranArrayBox(FAB)-like Objects.
Definition AMReX_FabArray.H:356
Definition AMReX_Reduce.H:556
Type value()
Definition AMReX_Reduce.H:591
Definition AMReX_Reduce.H:715
void eval(MF const &mf, IntVect const &nghost, D &reduce_data, F &&f)
Definition AMReX_Reduce.H:852
This class is a thin wrapper around std::vector. Unlike vector, Vector::operator[] provides bound che...
Definition AMReX_Vector.H:29
Checks if a type is derived from amrex::BaseFab.
Definition AMReX_Concepts.H:13
amrex_long Long
Definition AMReX_INT.H:30
__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:1328
__host__ __device__ T bisect(T lo, T hi, F f, T tol=1e-12, int max_iter=100)
Find a root of a scalar function on a bracketing interval using bisection.
Definition AMReX_Algorithm.H:170
Definition AMReX_Amr.cpp:50
__host__ __device__ void ignore_unused(const Ts &...)
No-op helper that marks variables as intentionally unused.
Definition AMReX.H:273
@ F
Definition AMReX_Order.H:7
ReduceData< Ts... >::Type ParReduce(TypeList< Ops... > operation_list, TypeList< Ts... > type_list, FabArray< FAB > const &fa, IntVect const &nghost, F &&f)
Parallel reduce for MultiFab/FabArray. The reduce result is local and it's the user's responsibility ...
Definition AMReX_ParReduce.H:48
Vector< typename ReduceData< Ts... >::Type > ParReducePerComp(TypeList< Ops... > operation_list, TypeList< Ts... > type_list, FabArray< FAB > const &fa, IntVect const &nghost, int ncomp, F &&f)
Parallel reduce for MultiFab/FabArray with a separate result for each component. The reduce result is...
Definition AMReX_ParReduce.H:563
__host__ __device__ Dim3 begin(BoxND< dim > const &box) noexcept
Return the iterator begin coordinate of box as Dim3.
Definition AMReX_Box.H:2241
void launch(T const &n, L const &f) noexcept
Definition AMReX_GpuLaunchFunctsC.H:122
BoxND< 3 > Box
Box is an alias for amrex::BoxND instantiated with AMREX_SPACEDIM.
Definition AMReX_BaseFwd.H:35
@ x
Definition AMReX_Orientation.H:14
IntVectND< 3 > IntVect
IntVect is an alias for amrex::IntVectND instantiated with AMREX_SPACEDIM.
Definition AMReX_BaseFwd.H:38
BoxIndexerND< 3 > BoxIndexer
Definition AMReX_Box.H:2610
const int[]
Definition AMReX_BLProfiler.cpp:1665
Vector< typename ReduceData< Ts... >::Type > ParReducePerBoxComp(TypeList< Ops... > operation_list, TypeList< Ts... > type_list, FabArray< FAB > const &fa, IntVect const &nghost, int ncomp, F &&f)
Parallel reduce for MultiFab/FabArray with a separate result for each local box and component....
Definition AMReX_ParReduce.H:600
__host__ __device__ Dim3 end(BoxND< dim > const &box) noexcept
Return the iterator end coordinate of box as Dim3.
Definition AMReX_Box.H:2259
std::vector< std::string > split(std::string const &s, std::string const &sep)
Split a string using given tokens in sep.
Definition AMReX_String.cpp:44
__host__ __device__ constexpr int get(IntVectND< dim > const &iv) noexcept
Get the Idx-th element of IntVectND<dim>.
Definition AMReX_IntVect.H:1360
Struct for holding types.
Definition AMReX_TypeList.H:13