Block-Structured AMR Software Framework
Loading...
Searching...
No Matches
AMReX_MFParallelForG.H
Go to the documentation of this file.
1#ifndef AMREX_MF_PARALLEL_FOR_G_H_
2#define AMREX_MF_PARALLEL_FOR_G_H_
3#include <AMReX_Config.H>
4
5#ifdef AMREX_USE_GPU
6
7#include <AMReX_Concepts.H>
8#include <AMReX_GpuDevice.H>
9#include <algorithm>
10#include <cmath>
11#include <limits>
12
14namespace amrex::detail {
15
16inline
17void build_par_for_boxes (char*& hp, BoxIndexer*& pboxes, Vector<Box> const& boxes)
18{
19 if (boxes.empty()) { return; }
20 const int nboxes = boxes.size();
21 const std::size_t nbytes = nboxes*sizeof(BoxIndexer);
22 hp = (char*)The_Pinned_Arena()->alloc(nbytes);
23 auto* hp_boxes = (BoxIndexer*)hp;
24 for (int i = 0; i < nboxes; ++i) {
25 new (hp_boxes+i) BoxIndexer(boxes[i]);
26 }
27
28 auto dp = (char*) The_Arena()->alloc(nbytes);
29 Gpu::htod_memcpy_async(dp, hp, nbytes);
30 pboxes = (BoxIndexer*)dp;
31}
32
33inline
34void destroy_par_for_boxes (char* hp, char* dp)
35{
36 // The host-to-device copy in build_par_for_boxes() is asynchronous. In a
37 // NoSync region it may still be pending when the owning ParForInfo is
38 // destroyed (e.g., when a temporary FabArray with a unique BoxArray dies).
39 // Use stream-ordered frees so that the buffers are not reused too early.
40 if (hp) {
43 }
44}
45
46namespace parfor_mf_detail {
47 template <typename F>
49 auto call_f (F const& f, int b, int i, int j, int k, int) noexcept
50 -> decltype(f(0,0,0,0))
51 {
52 f(b,i,j,k);
53 }
54
55 template <typename F>
57 auto call_f (F const& f, int b, int i, int j, int k, int ncomp) noexcept
58 -> decltype(f(0,0,0,0,0))
59 {
60 for (int n = 0; n < ncomp; ++n) {
61 f(b,i,j,k,n);
62 }
63 }
64}
65
66template <int MT, FabArrayType MF, typename F>
67void
68ParallelFor_doit (MF const& mf, IntVect const& nghost, int ncomp, IntVect const&, bool, F const& f)
69{
70 const auto& index_array = mf.IndexArray();
71 const int nboxes = index_array.size();
72
73 if (nboxes == 0) {
74 return;
75 } else if (nboxes == 1) {
76 Box const& b = amrex::grow(mf.box(index_array[0]), nghost);
77 amrex::ParallelFor(b, [=] AMREX_GPU_DEVICE (int i, int j, int k) noexcept
78 {
79 parfor_mf_detail::call_f(f, 0, i, j, k, ncomp);
80 });
81 } else {
82 auto const& parforinfo = mf.getParForInfo(nghost);
83 auto nblocks_per_box = parforinfo.getNBlocksPerBox(MT);
84 AMREX_ASSERT(Long(nblocks_per_box)*Long(nboxes) < Long(std::numeric_limits<int>::max()));
85 const int nblocks = nblocks_per_box * nboxes;
86 const BoxIndexer* dp_boxes = parforinfo.getBoxes();
87
88#if defined(AMREX_USE_CUDA) || defined(AMREX_USE_HIP)
89
90 amrex::launch_global<MT>
91 <<<nblocks, MT, 0, Gpu::gpuStream()>>>
92 ([=] AMREX_GPU_DEVICE () noexcept
93 {
94 int ibox = int(blockIdx.x) / nblocks_per_box;
95 auto icell = std::uint64_t(blockIdx.x-ibox*nblocks_per_box)*MT + threadIdx.x;
96
97#elif defined(AMREX_USE_SYCL)
98
99 amrex::launch<MT>(nblocks, Gpu::gpuStream(),
100 [=] AMREX_GPU_DEVICE (sycl::nd_item<1> const& item) noexcept
101 {
102 int blockIdxx = item.get_group_linear_id();
103 int threadIdxx = item.get_local_linear_id();
104 int ibox = int(blockIdxx) / nblocks_per_box;
105 auto icell = std::uint64_t(blockIdxx-ibox*nblocks_per_box)*MT + threadIdxx;
106#endif
107 BoxIndexer const& indexer = dp_boxes[ibox];
108 if (icell < indexer.numPts()) {
109 auto [i, j, k] = indexer(icell);
110 parfor_mf_detail::call_f(f, ibox, i, j, k, ncomp);
111 }
112 });
113 }
115}
116
117template <FabArrayType MF, typename F>
118void
119ParallelFor_doit (MF const& mf, IntVect const& nghost, int ncomp, IntVect const& ts, bool dynamic, F&& f)
120{
121#ifdef AMREX_USE_CUDA
122 constexpr int MT = 128;
123#else
124 constexpr int MT = AMREX_GPU_MAX_THREADS;
125#endif
126 ParallelFor_doit<MT>(mf, nghost, ncomp, ts, dynamic, std::forward<F>(f));
127}
128
129template <FabArrayType MF, typename F>
130void
131ParallelFor_doit (MF const& mf, IntVect const& nghost, IntVect const& ts, bool dynamic, F&& f)
132{
133#ifdef AMREX_USE_CUDA
134 constexpr int MT = 128;
135#else
136 constexpr int MT = AMREX_GPU_MAX_THREADS;
137#endif
138 ParallelFor_doit<MT>(mf, nghost, 1, ts, dynamic, std::forward<F>(f));
139}
140
141// One launch over the valid points of all boxes whose index equals offset
142// modulo stride.
143template <int MT, FabArrayType MF, typename F>
144void
145ParallelForStrided_doit (MF const& mf, IntVect const& stride, IntVect const& offset, F const& f)
146{
147 const int nboxes = mf.IndexArray().size();
148 if (nboxes == 0) { return; }
149
150 auto const& parforinfo = mf.getParForInfo(stride, offset);
151 const auto nblocks_per_box = parforinfo.getNBlocksPerBox(MT);
152 if (nblocks_per_box == 0) { return; }
153 AMREX_ASSERT(Long(nblocks_per_box)*Long(nboxes) < Long(std::numeric_limits<int>::max()));
154 const int nblocks = nblocks_per_box * nboxes;
155 const BoxIndexer* dp_boxes = parforinfo.getBoxes();
156
157#if defined(AMREX_USE_CUDA) || defined(AMREX_USE_HIP)
158
159 amrex::launch_global<MT>
160 <<<nblocks, MT, 0, Gpu::gpuStream()>>>
161 ([=] AMREX_GPU_DEVICE () noexcept
162 {
163 int ibox = int(blockIdx.x) / nblocks_per_box;
164 auto icell = std::uint64_t(blockIdx.x-ibox*nblocks_per_box)*MT + threadIdx.x;
165
166#elif defined(AMREX_USE_SYCL)
167
168 amrex::launch<MT>(nblocks, Gpu::gpuStream(),
169 [=] AMREX_GPU_DEVICE (sycl::nd_item<1> const& item) noexcept
170 {
171 int blockIdxx = item.get_group_linear_id();
172 int threadIdxx = item.get_local_linear_id();
173 int ibox = int(blockIdxx) / nblocks_per_box;
174 auto icell = std::uint64_t(blockIdxx-ibox*nblocks_per_box)*MT + threadIdxx;
175#endif
176 BoxIndexer const& indexer = dp_boxes[ibox];
177 if (icell < indexer.numPts()) {
178 IntVect iv = indexer.intVect(icell);
179 for (int idim = 0; idim < AMREX_SPACEDIM; ++idim) {
180 iv[idim] = indexer.lo[idim] + (iv[idim]-indexer.lo[idim])*stride[idim];
181 }
182 auto const d3 = iv.dim3();
183 f(ibox, d3.x, d3.y, d3.z);
184 }
185 });
187}
188
189// Number of points along x of the box tracked by a BoxIndexer.
191int box_indexer_len_x (BoxIndexer const& indexer)
192{
193#if (AMREX_SPACEDIM == 1)
194 return int(indexer.npts);
195#else
196 return int(indexer.fdm[0].divisor);
197#endif
198}
199
200// One launch over the red or black points of all boxes. Consecutive threads
201// take consecutive points of one row, as in the single-box path.
202template <int MT, FabArrayType MF, typename F>
203void
204ParallelForRedBlack_doit (MF const& mf, int redblack, F const& f)
205{
206 const int nboxes = mf.IndexArray().size();
207 if (nboxes == 0) { return; }
208
209 auto const& parforinfo = mf.getParForInfoRedBlack();
210 const auto nblocks_per_box = parforinfo.getNBlocksPerBox(MT);
211 if (nblocks_per_box == 0) { return; }
212 AMREX_ASSERT(Long(nblocks_per_box)*Long(nboxes) < Long(std::numeric_limits<int>::max()));
213 const int nblocks = nblocks_per_box * nboxes;
214 const BoxIndexer* dp_boxes = parforinfo.getBoxes();
215
216#if defined(AMREX_USE_CUDA) || defined(AMREX_USE_HIP)
217
218 amrex::launch_global<MT>
219 <<<nblocks, MT, 0, Gpu::gpuStream()>>>
220 ([=] AMREX_GPU_DEVICE () noexcept
221 {
222 int ibox = int(blockIdx.x) / nblocks_per_box;
223 auto icell = std::uint64_t(blockIdx.x-ibox*nblocks_per_box)*MT + threadIdx.x;
224
225#elif defined(AMREX_USE_SYCL)
226
227 amrex::launch<MT>(nblocks, Gpu::gpuStream(),
228 [=] AMREX_GPU_DEVICE (sycl::nd_item<1> const& item) noexcept
229 {
230 int blockIdxx = item.get_group_linear_id();
231 int threadIdxx = item.get_local_linear_id();
232 int ibox = int(blockIdxx) / nblocks_per_box;
233 auto icell = std::uint64_t(blockIdxx-ibox*nblocks_per_box)*MT + threadIdxx;
234#endif
235 // indexers[0]: even i, indexers[1]: odd i. Enumerate the larger
236 // lattice, then pick the one with the parity this row needs.
237 BoxIndexer const* indexers = dp_boxes + 2*ibox;
238 const int big = (indexers[1].numPts() > indexers[0].numPts()) ? 1 : 0;
239 if (icell < indexers[big].numPts()) {
240 const IntVect iv = indexers[big].intVect(icell);
241 const int ic = iv[0] - indexers[big].lo[0];
242 auto const d3 = iv.dim3();
243 const int parity = (d3.y + d3.z + redblack) & 1;
244 BoxIndexer const& indexer = indexers[parity];
245 if (ic < box_indexer_len_x(indexer)) {
246 f(ibox, indexer.lo[0] + 2*ic, d3.y, d3.z);
247 }
248 }
249 });
251}
252
253template <FabArrayType MF, typename F>
254void
255ParallelForStrided_doit (MF const& mf, IntVect const& stride, IntVect const& offset, F const& f)
256{
257 const auto& index_array = mf.IndexArray();
258 if (index_array.size() == 1) {
259 Box const& b = strided_box(mf.box(index_array[0]), stride, offset);
260 const IntVect lo = b.smallEnd();
261 amrex::ParallelFor(b, [=] AMREX_GPU_DEVICE (int i, int j, int k) noexcept
262 {
263 AMREX_D_TERM(i = lo[0] + (i-lo[0])*stride[0];,
264 j = lo[1] + (j-lo[1])*stride[1];,
265 k = lo[2] + (k-lo[2])*stride[2];)
266 f(0, i, j, k);
267 });
268 } else {
269#ifdef AMREX_USE_CUDA
270 constexpr int MT = 128;
271#else
272 constexpr int MT = AMREX_GPU_MAX_THREADS;
273#endif
274 ParallelForStrided_doit<MT>(mf, stride, offset, f);
275 }
276}
277
278template <FabArrayType MF, typename F>
279void
280ParallelForRedBlack_doit (MF const& mf, int redblack, F const& f)
281{
282 const auto& index_array = mf.IndexArray();
283 if (index_array.size() == 1) {
284 // Checkerboard: i = 2*ic + ((j+k+redblack)&1)
285 Box b = mf.box(index_array[0]);
286 const int xlo = b.smallEnd(0);
287 const int xhi = b.bigEnd(0);
288 b.setSmall(0, amrex::coarsen(xlo,2));
289 b.setBig (0, amrex::coarsen(xhi,2));
290 amrex::ParallelFor(b, [=] AMREX_GPU_DEVICE (int ic, int j, int k) noexcept
291 {
292 const int i = 2*ic + ((j+k+redblack) & 1);
293 if (i >= xlo && i <= xhi) {
294 f(0, i, j, k);
295 }
296 });
297 } else {
298#ifdef AMREX_USE_CUDA
299 constexpr int MT = 128;
300#else
301 constexpr int MT = AMREX_GPU_MAX_THREADS;
302#endif
303 ParallelForRedBlack_doit<MT>(mf, redblack, f);
304 }
305}
306
307}
309
310#endif
311#endif
#define AMREX_ASSERT(EX)
Definition AMReX_BLassert.H:38
#define AMREX_FORCE_INLINE
Definition AMReX_Extension.H:124
#define AMREX_GPU_ERROR_CHECK()
Definition AMReX_GpuError.H:151
#define AMREX_GPU_DEVICE
Definition AMReX_GpuQualifiers.H:18
#define AMREX_GPU_HOST_DEVICE
Definition AMReX_GpuQualifiers.H:20
Array4< int const > offset
Definition AMReX_HypreMLABecLap.cpp:1139
#define AMREX_D_TERM(a, b, c)
Definition AMReX_SPACE.H:172
virtual void * alloc(std::size_t sz)=0
Allocate sz bytes from this arena.
__host__ __device__ const IntVectND< dim > & smallEnd() const &noexcept
Return the inclusive lower bound of the box.
Definition AMReX_Box.H:124
__host__ __device__ constexpr Dim3 dim3() const noexcept
Definition AMReX_IntVect.H:262
amrex_long Long
Definition AMReX_INT.H:30
__host__ __device__ BoxND< dim > coarsen(const BoxND< dim > &b, int ref_ratio) noexcept
Return a copy of b coarsened by the isotropic ratio ref_ratio.
Definition AMReX_Box.H:1469
__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:869
Arena * The_Arena()
Definition AMReX_Arena.cpp:829
void freeAsync(Arena *arena, void *mem) noexcept
Definition AMReX_GpuDevice.H:345
void htod_memcpy_async(void *p_d, const void *p_h, const std::size_t sz) noexcept
Definition AMReX_GpuDevice.H:421
gpuStream_t gpuStream() noexcept
Definition AMReX_GpuDevice.H:291
void ParallelFor(TypeList< CTOs... > ctos, std::array< int, sizeof...(CTOs)> const &runtime_options, T N, F &&f)
Definition AMReX_CTOParallelForImpl.H:202
BoxND< 3 > Box
Box is an alias for amrex::BoxND instantiated with AMREX_SPACEDIM.
Definition AMReX_BaseFwd.H:35
BoxIndexerND< 3 > BoxIndexer
Definition AMReX_Box.H:2608
IntVectND< 3 > IntVect
IntVect is an alias for amrex::IntVectND instantiated with AMREX_SPACEDIM.
Definition AMReX_BaseFwd.H:38
const int[]
Definition AMReX_BLProfiler.cpp:1665