Block-Structured AMR Software Framework
Loading...
Searching...
No Matches
AMReX_FBI.H
Go to the documentation of this file.
1#ifndef AMREX_FBI_H_
2#define AMREX_FBI_H_
3
8
9namespace amrex {
10
17template <class FAB>
18struct FabCopyTag {
19 FAB const* sfab;
21 IntVect offset; // sbox.smallEnd() - dbox.smallEnd()
22};
23
26 char const* p;
28};
29
31namespace detail {
32
33#ifdef AMREX_USE_GPU
34
35template <class T0, class T1>
36struct CellStore
37{
39 operator() (T0* d, T1 s) const noexcept
40 {
41 *d = static_cast<T0>(s);
42 }
43};
44
45template <class T0, class T1>
46struct CellAdd
47{
49 operator() (T0* d, T1 s) const noexcept
50 {
51 *d += static_cast<T0>(s);
52 }
53};
54
55template <class T0, class T1>
56struct CellAtomicAdd
57{
58 template<class U0=T0>
59 requires (amrex::HasAtomicAdd<U0>::value)
61 operator() (U0* d, T1 s) const noexcept
62 {
63 Gpu::Atomic::AddNoRet(d, static_cast<U0>(s));
64 }
65};
66
67template <class T0, class T1, class F>
68void
69fab_to_fab (Vector<Array4CopyTag<T0, T1> > const& copy_tags, int scomp, int dcomp, int ncomp,
70 F && f)
71{
72 TagVector<Array4CopyTag<T0, T1>> tv{copy_tags};
73
74 detail::ParallelFor_doit(tv,
76 int icell, int ncells, int i, int j, int k, Array4CopyTag<T0, T1> const& tag) noexcept
77 {
78 if (icell < ncells) {
79 for (int n = 0; n < ncomp; ++n) {
80 f(&(tag.dfab(i,j,k,n+dcomp)),
81 tag.sfab(i+tag.offset.x,j+tag.offset.y,k+tag.offset.z,n+scomp));
82 }
83 }
84 });
85}
86
87template <class TagType, class F>
88void
89fab_to_fab_store (Vector<TagType> const& tags, int scomp, int dcomp, int ncomp, F&&f)
90{
92 [=] AMREX_GPU_DEVICE (int i, int j, int k, TagType const& tag) noexcept
93 {
94 int m = Gpu::Atomic::Add(&(tag.mask(i,j,k)), 1);
95 if (m == 0) {
96 for (int n = 0; n < ncomp; ++n) {
97 f(&(tag.dfab(i,j,k,n+dcomp)),
98 tag.sfab(i+tag.offset.x,j+tag.offset.y,k+tag.offset.z,n+scomp));
99 }
100 }
101 });
102}
103
104template <class TagType, class F>
105void
106fab_to_fab_other (Vector<TagType> const& tags, int scomp, int dcomp, int ncomp, F&&f)
107{
109 [=] AMREX_GPU_DEVICE (int i, int j, int k, TagType const& tag) noexcept
110 {
111 int* m = &(tag.mask(i,j,k));
112 bool my_turn = false;
113 do {
114#if defined(AMREX_USE_SYCL)
115 my_turn = (Gpu::Atomic::Exch(m, 1) == 0);
116#else
117 my_turn = (Gpu::Atomic::CAS(m, 0, 1) == 0);
118#endif
119 if (my_turn) {
120#if defined(AMREX_USE_SYCL)
121 sycl::atomic_fence(sycl::memory_order::acq_rel, sycl::memory_scope::device);
122#else
123 __threadfence();
124#endif
125 for (int n = 0; n < ncomp; ++n) {
126 f(&(tag.dfab(i,j,k,n+dcomp)),
127 tag.sfab(i+tag.offset.x,j+tag.offset.y,k+tag.offset.z,n+scomp));
128 }
129#if defined(AMREX_USE_SYCL)
130 sycl::atomic_fence(sycl::memory_order::acq_rel, sycl::memory_scope::device);
131#else
132 __threadfence(); // It appears that we need this fence too if a GPU is shared.
133#endif
134 Gpu::Atomic::Exch(m, 0);
135 }
136 else {
137#if defined(AMREX_USE_CUDA)
138
139#if defined(__CUDA_ARCH__) && (__CUDA_ARCH__ < 700)
140#if defined(_WIN32)
141 volatile int tmp; // prevent optimization
142 for (int c = 0; c < 2; ++c) {
143 ++tmp;
144 }
145#else
146 for (int c = 0; c < 2; ++c) {
147 __asm__ volatile(""); // prevent optimization
148 }
149#endif
150#else
151 __nanosleep(1);
152#endif
153
154#elif defined(AMREX_USE_HIP)
155
156 __builtin_amdgcn_s_sleep(1);
157
158#elif defined(AMREX_USE_SYCL)
159
160 for (int c = 0; c < 2; ++c) {
161 __asm__ volatile(""); // prevent optimization
162 }
163
164#endif
165 }
166 } while (!my_turn);
167 });
168}
169
170template <class T0, class T1, class F>
171void
172fab_to_fab (Vector<Array4CopyTag<T0, T1> > const& copy_tags, int scomp, int dcomp,
173 int ncomp, F && f, Vector<Array4Tag<int> > const& masks)
174{
175 using TagType = Array4MaskCopyTag<T0, T1>;
176 Vector<TagType> tags;
177 const int N = copy_tags.size();
178 tags.reserve(N);
179 for (int i = 0; i < N; ++i) {
180 tags.push_back(TagType{.dfab = copy_tags[i].dfab, .sfab = copy_tags[i].sfab,
181 .mask = masks[i].dfab, .dbox = copy_tags[i].dbox,
182 .offset = copy_tags[i].offset});
183 }
184
185 if constexpr (std::is_same_v<F, CellStore<T0,T1>>)
186 {
187 fab_to_fab_store(tags, scomp, dcomp, ncomp, std::forward<F>(f));
188 }
189 else
190 {
191 fab_to_fab_other(tags, scomp, dcomp, ncomp, std::forward<F>(f));
192 }
193 // Note that Tag ParalleFor has a stream sync call in the end.
194}
195
196template <typename T0, typename T1>
197requires (amrex::IsStoreAtomic<T0>::value)
198void
199fab_to_fab_atomic_cpy (Vector<Array4CopyTag<T0, T1> > const& copy_tags, int scomp,
200 int dcomp, int ncomp, Vector<Array4Tag<int> > const&)
201{
202 fab_to_fab<T0, T1>(copy_tags, scomp, dcomp, ncomp, CellStore<T0, T1>());
203}
204
205template <typename T0, typename T1>
206requires (!amrex::IsStoreAtomic<T0>::value)
207void
208fab_to_fab_atomic_cpy (Vector<Array4CopyTag<T0, T1> > const& copy_tags, int scomp,
209 int dcomp, int ncomp, Vector<Array4Tag<int> > const& masks)
210{
211 fab_to_fab(copy_tags, scomp, dcomp, ncomp, CellStore<T0, T1>(), masks);
212}
213
214template <typename T0, typename T1>
215requires (amrex::HasAtomicAdd<T0>::value)
216void
217fab_to_fab_atomic_add (Vector<Array4CopyTag<T0, T1> > const& copy_tags, int scomp,
218 int dcomp, int ncomp, Vector<Array4Tag<int> > const&)
219{
220 fab_to_fab(copy_tags, scomp, dcomp, ncomp, CellAtomicAdd<T0, T1>());
221}
222
223template <typename T0, typename T1>
224requires (!amrex::HasAtomicAdd<T0>::value)
225void
226fab_to_fab_atomic_add (Vector<Array4CopyTag<T0, T1> > const& copy_tags, int scomp,
227 int dcomp, int ncomp, Vector<Array4Tag<int> > const& masks)
228{
229 fab_to_fab(copy_tags, scomp, dcomp, ncomp, CellAdd<T0, T1>(), masks);
230}
231
232template <typename T0, typename T1, class F>
233void deterministic_fab_to_fab (Vector<Array4CopyTag<T0,T1>> const& a_tags, int scomp,
234 int dcomp, int ncomp, F const& f)
235{
236 if (a_tags.empty()) { return; }
237
238 using TagType = Array4CopyTag<T0,T1>;
239
240 struct TiledTag {
241 int tag_index;
242 std::pair<int,Box> dindex_tilebox;
243 bool operator< (TiledTag const& rhs) const noexcept {
244 return this->dindex_tilebox < rhs.dindex_tilebox;
245 }
246 bool operator!= (TiledTag const& rhs) const noexcept {
247 return this->dindex_tilebox != rhs.dindex_tilebox;
248 }
249 };
250 Vector<TiledTag> tiled_tags;
251
252 auto const ixtype = a_tags[0].dbox.ixType();
253
254 constexpr int tile_size = 64;
255 for (int itag = 0; itag < a_tags.size(); ++itag) {
256 auto const& tag = a_tags[itag];
257 auto const& dlo = tag.dbox.smallEnd();
258 auto const& dhi = tag.dbox.bigEnd();
265#if (AMREX_SPACEDIM == 3)
266 for (int kt = tlo[2]; kt <= thi[2]; ++kt)
267#endif
268 {
269#if (AMREX_SPACEDIM >= 2)
270 for (int jt = tlo[1]; jt <= thi[1]; ++jt)
271#endif
272 {
273 for (int it = tlo[0]; it <= thi[0]; ++it)
274 {
275 IntVect lo(AMREX_D_DECL(it*tile_size,
276 jt*tile_size,
277 kt*tile_size));
278 tiled_tags.push_back(TiledTag{
279 .tag_index = itag,
280 .dindex_tilebox = std::make_pair(tag.dindex, Box(lo, lo+(tile_size-1), ixtype))
281 });
282 }
283 }
284 }
285 }
286
287 std::sort(tiled_tags.begin(), tiled_tags.end());
288
291 h_tags.reserve(tiled_tags.size());
292
293 for (unsigned int itag = 0; itag < tiled_tags.size(); ++itag) {
294 if (itag == 0) {
295 h_ntags.push_back(0);
296 } else if (tiled_tags[itag-1] != tiled_tags[itag]) {
297 h_ntags.push_back(itag);
298 }
299 auto const& ttag = tiled_tags[itag];
300 auto const& btag = a_tags[ttag.tag_index];
301 h_tags.push_back(TagType{.dfab = btag.dfab, .dindex = btag.dindex, .sfab = btag.sfab,
302 .dbox = btag.dbox & ttag.dindex_tilebox.second,
303 .offset = btag.offset});
304 }
305 h_ntags.push_back((unsigned int)tiled_tags.size());
306
307 Gpu::DeviceVector<TagType> d_tags(h_tags.size());
308 Gpu::DeviceVector<unsigned int> d_ntags(h_ntags.size());
309 Gpu::copyAsync(Gpu::hostToDevice,h_tags.begin(),h_tags.end(),d_tags.begin());
310 Gpu::copyAsync(Gpu::hostToDevice,h_ntags.begin(),h_ntags.end(),d_ntags.begin());
311 auto const* ptag = d_tags.data();
312 auto const* pntags = d_ntags.data();
313 auto const nblocks = int(h_ntags.size()-1);
314 constexpr auto nthreads = 256;
316#ifdef AMREX_USE_SYCL
317 [=] AMREX_GPU_DEVICE (sycl::nd_item<1> const& item) noexcept
318 [[sycl::reqd_work_group_size(nthreads)]]
319#else
320 [=] AMREX_GPU_DEVICE () noexcept
321#endif
322 {
323#ifdef AMREX_USE_SYCL
324 Dim1 blockIdx{item.get_group_linear_id()};
325 Dim1 threadIdx{item.get_local_linear_id()};
326#endif
327
328 for (unsigned int itag = pntags[blockIdx.x]; itag < pntags[blockIdx.x+1]; ++itag) {
329 auto const tag = ptag[itag];
330 auto ncells = int(tag.dbox.numPts());
331 const auto len = amrex::length(tag.dbox);
332 const auto lo = amrex::lbound(tag.dbox);
333 for (int icell = int(threadIdx.x); icell < ncells; icell += nthreads) {
334 int k = icell / (len.x*len.y);
335 int j = (icell - k*(len.x*len.y)) / len.x;
336 int i = (icell - k*(len.x*len.y)) - j*len.x;
337 i += lo.x;
338 j += lo.y;
339 k += lo.z;
340 for (int n = 0; n < ncomp; ++n) {
341 f(tag.dfab.ptr(i,j,k,n+dcomp),
342 tag.sfab(i + tag.offset.x,
343 j + tag.offset.y,
344 k + tag.offset.z, n+scomp));
345 }
346 }
347
348 if (itag+1 < pntags[blockIdx.x+1]) {
349#ifdef AMREX_USE_SYCL
350 sycl::group_barrier(item.get_group());
351#else
352 __syncthreads();
353#endif
354 }
355 }
356 });
358}
359
360template <typename B, typename V, typename TT>
361requires (amrex::HasAtomicAdd<V>::value)
362void unpack_recv_buffer_gpu_atomic_add (char* pbuffer, TagVector<TT> const& tv,
363 int dcomp, int ncomp)
364{
365 detail::ParallelFor_doit(tv,
366 [=] AMREX_GPU_DEVICE (
367 int icell, int ncells, int i, int j, int k, TT const& tag) noexcept
368 {
369 if (icell < ncells) {
370 Array4<B const> sfab{(B const*)(pbuffer+tag.poff),
371 amrex::begin(tag.bx), amrex::end(tag.bx), ncomp};
372 for (int n = 0; n < ncomp; ++n) {
373 Gpu::Atomic::AddNoRet(tag.dfab.ptr(i,j,k,n+dcomp),
374 (V)sfab(i,j,k,n));
375 }
376 }
377 });
378}
379
380template <typename B, typename V, typename TT>
381requires (!amrex::HasAtomicAdd<V>::value)
382void unpack_recv_buffer_gpu_atomic_add (char* pbuffer, TagVector<TT> const& tv,
383 int dcomp, int ncomp)
384{
385 amrex::ignore_unused(pbuffer, tv, dcomp, ncomp);
386 amrex::Abort("unpack_recv_buffer_gpu: should NOT get here");
387}
388
389#endif /* AMREX_USE_GPU */
390
391}
393
394template <class FAB>
395void
396FabArray<FAB>::FB_local_copy_cpu (const FB& TheFB, int scomp, int ncomp)
397{
398 auto const& LocTags = *(TheFB.m_LocTags);
399 auto N_locs = static_cast<int>(LocTags.size());
400 if (N_locs == 0) { return; }
401 bool is_thread_safe = TheFB.m_threadsafe_loc;
402 if (is_thread_safe)
403 {
404#ifdef AMREX_USE_OMP
405#pragma omp parallel for
406#endif
407 for (int i = 0; i < N_locs; ++i)
408 {
409 const CopyComTag& tag = LocTags[i];
410#ifdef BL_USE_TEAM
411 // With MPI teams, the tags include teammates' FABs. Only write our own.
412 if (distributionMap[tag.dstIndex] != ParallelDescriptor::MyProc()) { continue; }
413#endif
414
417
418 const FAB* sfab = &(get(tag.srcIndex));
419 FAB* dfab = &(get(tag.dstIndex));
420 dfab->template copy<RunOn::Host>(*sfab, tag.sbox, scomp, tag.dbox, scomp, ncomp);
421 }
422#ifdef BL_USE_TEAM
423 // Teammates may read our FABs next.
425#endif
426 }
427 else
428 {
430 for (int i = 0; i < N_locs; ++i)
431 {
432 const CopyComTag& tag = LocTags[i];
433#ifdef BL_USE_TEAM
434 // With MPI teams, the tags include teammates' FABs. Only write our own.
435 if (distributionMap[tag.dstIndex] != ParallelDescriptor::MyProc()) { continue; }
436#endif
437
440
441 loc_copy_tags[tag.dstIndex].push_back
442 ({this->fabPtr(tag.srcIndex), tag.dbox, tag.sbox.smallEnd()-tag.dbox.smallEnd()});
443 }
444#ifdef AMREX_USE_OMP
445#pragma omp parallel
446#endif
447 for (MFIter mfi(*this); mfi.isValid(); ++mfi)
448 {
449 const auto& tags = loc_copy_tags[mfi];
450 auto dfab = this->array(mfi);
451 for (auto const & tag : tags)
452 {
453 auto const sfab = tag.sfab->array();
454 const auto offset = tag.offset.dim3();
455 amrex::LoopConcurrentOnCpu(tag.dbox, ncomp,
456 [=] (int i, int j, int k, int n) noexcept
457 {
458 dfab(i,j,k,n+scomp) = sfab(i+offset.x,j+offset.y,k+offset.z,n+scomp);
459 });
460 }
461 }
462 }
463}
464
465template <class FAB>
466void
467FabArray<FAB>::FB_local_add_cpu (const FB& TheFB, int scomp, int ncomp)
468{
469 auto const& LocTags = *(TheFB.m_LocTags);
470 auto N_locs = static_cast<int>(LocTags.size());
471 if (N_locs == 0) { return; }
472
474 // We must make a temporary copy of the data first to avoid race condition.
475 std::vector<FAB> src_fabs(N_locs);
476 for (int itag = 0; itag < N_locs; ++itag) {
477 const CopyComTag& tag = LocTags[itag];
478 src_fabs[itag].resize(tag.sbox,ncomp);
479 loc_copy_tags[tag.dstIndex].push_back
480 (FabCopyTag<FAB>{&(src_fabs[itag]),
481 tag.dbox, tag.sbox.smallEnd()-tag.dbox.smallEnd()});
482 }
483
484#ifdef AMREX_USE_OMP
485#pragma omp parallel for
486#endif
487 for (int itag = 0; itag < N_locs; ++itag) {
488 const CopyComTag& tag = LocTags[itag];
489 src_fabs[itag].template copy<RunOn::Host>(this->operator[](tag.srcIndex), scomp, 0, ncomp);
490 }
491
492#ifdef AMREX_USE_OMP
493#pragma omp parallel
494#endif
495 for (MFIter mfi(*this); mfi.isValid(); ++mfi)
496 {
497 const auto& tags = loc_copy_tags[mfi];
498 const auto& dfab = this->array(mfi);
499 for (auto const & tag : tags)
500 {
501 auto const sfab = tag.sfab->array();
502 const auto offset = tag.offset.dim3();
503 amrex::LoopConcurrentOnCpu(tag.dbox, ncomp,
504 [&] (int i, int j, int k, int n) noexcept
505 {
506 dfab(i,j,k,n+scomp) += sfab(i+offset.x,j+offset.y,k+offset.z,n);
507 });
508 }
509 }
510}
511
512#ifdef AMREX_USE_GPU
513
514template <class FAB>
517{
518 auto const& LocTags = *(TheFB.m_LocTags);
519 int N_locs = LocTags.size();
520
521 using TagType = Array4CopyTag<value_type>;
522
524 if (auto it = m_fb_local_copy_handler.find(TheFB.m_id);
525 it != m_fb_local_copy_handler.end())
526 {
527 tv = it->second.get();
528 } else {
529 Vector<TagType> loc_copy_tags;
530 loc_copy_tags.reserve(N_locs);
531
532 for (int i = 0; i < N_locs; ++i)
533 {
534 const CopyComTag& tag = LocTags[i];
535
538
539 int li = this->localindex(tag.dstIndex);
540 loc_copy_tags.push_back
541 (TagType{.dfab = this->atLocalIdx(li).array(),
542 .dindex = tag.dstIndex,
543 .sfab = this->fabPtr(tag.srcIndex)->const_array(),
544 .dbox = tag.dbox,
545 .offset = (tag.sbox.smallEnd()-tag.dbox.smallEnd()).dim3()});
546 }
547
548 auto utv = std::make_unique<TagVector<TagType>>(loc_copy_tags);
549 tv = utv.get();
550 m_fb_local_copy_handler[TheFB.m_id] = std::move(utv);
551 }
552
553 return tv;
554}
555
556template <class FAB>
557void
558FabArray<FAB>::FB_local_copy_gpu (const FB& TheFB, int scomp, int ncomp)
559{
560 auto const& LocTags = *(TheFB.m_LocTags);
561 int N_locs = LocTags.size();
562 if (N_locs == 0) { return; }
563 bool is_thread_safe = TheFB.m_threadsafe_loc;
564
565 using TagType = Array4CopyTag<value_type>;
566
567 if (is_thread_safe || amrex::IsStoreAtomic<value_type>::value)
568 {
569 auto* tv = FB_get_local_copy_tag_vector(TheFB);
570
571 detail::ParallelFor_doit(*tv,
572 [=] AMREX_GPU_DEVICE (
573 int icell, int ncells, int i, int j, int k, TagType const& tag) noexcept
574 {
575 if (icell < ncells) {
576 for (int n = 0; n < ncomp; ++n) {
577 tag.dfab(i,j,k,n+scomp) = tag.sfab(i+tag.offset.x,
578 j+tag.offset.y,
579 k+tag.offset.z,n+scomp);
580 }
581 }
582 });
583 }
584 else
585 {
586 Vector<TagType> loc_copy_tags;
587 loc_copy_tags.reserve(N_locs);
588
589 Vector<BaseFab<int>> maskfabs(this->local_size());
590 Vector<Array4Tag<int> > masks_unique;
591 masks_unique.reserve(this->local_size());
592 Vector<Array4Tag<int> > masks;
593 masks.reserve(N_locs);
594
595 for (int i = 0; i < N_locs; ++i)
596 {
597 const CopyComTag& tag = LocTags[i];
598
601
602 int li = this->localindex(tag.dstIndex);
603 loc_copy_tags.push_back
604 (TagType{.dfab = this->atLocalIdx(li).array(),
605 .dindex = tag.dstIndex,
606 .sfab = this->fabPtr(tag.srcIndex)->const_array(),
607 .dbox = tag.dbox,
608 .offset = (tag.sbox.smallEnd()-tag.dbox.smallEnd()).dim3()});
609
610 if (!maskfabs[li].isAllocated()) {
611 maskfabs[li].resize(this->atLocalIdx(li).box());
612 masks_unique.emplace_back(Array4Tag<int>{.dfab = maskfabs[li].array()});
613 }
614 masks.emplace_back(Array4Tag<int>{.dfab = maskfabs[li].array()});
615 }
616
617 amrex::ParallelFor(masks_unique,
618 [=] AMREX_GPU_DEVICE (int i, int j, int k, Array4Tag<int> const& msk) noexcept
619 {
620 msk.dfab(i,j,k) = 0;
621 });
622
623 detail::fab_to_fab_atomic_cpy<value_type, value_type>(
624 loc_copy_tags, scomp, scomp, ncomp, masks);
625 }
626}
627
628template <class FAB>
629void
630FabArray<FAB>::FB_local_add_gpu (const FB& TheFB, int scomp, int ncomp, bool deterministic)
631{
632 auto const& LocTags = *(TheFB.m_LocTags);
633 int N_locs = LocTags.size();
634 if (N_locs == 0) { return; }
635
636 using TagType = Array4CopyTag<value_type>;
637
638 Vector<TagType> loc_copy_tags_1, loc_copy_tags_2;
639 loc_copy_tags_1.reserve(N_locs);
640 loc_copy_tags_2.reserve(N_locs);
641
642 Vector<FAB> src_fabs(N_locs);
643 for (int itag = 0; itag < N_locs; ++itag) {
644 const CopyComTag& tag = LocTags[itag];
645 src_fabs[itag].resize(tag.sbox,ncomp);
646 loc_copy_tags_1.push_back(
647 TagType{.dfab = src_fabs[itag].array(), .dindex = -1,
648 .sfab = this->const_array(tag.srcIndex,scomp), .dbox = tag.sbox,
649 .offset = Dim3{.x = 0, .y = 0, .z = 0}});
650 loc_copy_tags_2.push_back(
651 TagType{.dfab = this->array(tag.dstIndex,scomp), .dindex = tag.dstIndex,
652 .sfab = src_fabs[itag].const_array(), .dbox = tag.dbox,
653 .offset = (tag.sbox.smallEnd()-tag.dbox.smallEnd()).dim3()});
654 }
655
656 // Note that we have shifted the starting component to zero in the code above.
657
658 // TODO: We could try to cache the tags like in FillBoundary.
659
660 detail::fab_to_fab(loc_copy_tags_1, 0, 0, ncomp,
661 detail::CellStore<value_type, value_type>{});
662 if (deterministic || ! amrex::HasAtomicAdd<value_type>::value) {
663 detail::deterministic_fab_to_fab(loc_copy_tags_2, 0, 0, ncomp,
664 detail::CellAdd<value_type,value_type>{});
665 } else {
667 detail::fab_to_fab(loc_copy_tags_2, 0, 0, ncomp,
668 detail::CellAtomicAdd<value_type, value_type>{});
669 }
670 ((void)0);
671 }
672
673 // Note that fab_to_fab is synchronous.
674}
675
676template <class FAB>
677void
679 const CommMetaData& thecmd, int scomp, int ncomp)
680{
681 auto const& LocTags = *(thecmd.m_LocTags);
682 int N_locs = LocTags.size();
683 if (N_locs == 0) { return; }
684 bool is_thread_safe = thecmd.m_threadsafe_loc;
685
686 using TagType = Array4BoxTag<value_type>;
687 Vector<TagType> loc_setval_tags;
688 loc_setval_tags.reserve(N_locs);
689
691
692 for (int i = 0; i < N_locs; ++i)
693 {
694 const CopyComTag& tag = LocTags[i];
696 loc_setval_tags.push_back(TagType{.dfab = this->array(tag.dstIndex), .dbox = tag.dbox});
697 }
698
699 amrex::ParallelFor(loc_setval_tags, ncomp,
700 [x,scomp] AMREX_GPU_DEVICE (int i, int j, int k, int n, TagType const& tag) noexcept
701 {
702 tag.dfab(i,j,k,n+scomp) = x;
703 });
704}
705
706template <class FAB>
707void
709 const CommMetaData& thecmd, int scomp, int ncomp)
710{
711 auto const& RcvTags = *(thecmd.m_RcvTags);
712 bool is_thread_safe = thecmd.m_threadsafe_rcv;
713
714 using TagType = Array4BoxTag<value_type>;
715 Vector<TagType> rcv_setval_tags;
716
717 for (auto it = RcvTags.begin(); it != RcvTags.end(); ++it) {
718 for (auto const& tag: it->second) {
719 rcv_setval_tags.push_back(TagType{.dfab = this->array(tag.dstIndex), .dbox = tag.dbox});
720 }
721 }
722
723 if (rcv_setval_tags.empty()) { return; }
724
726
727 amrex::ParallelFor(rcv_setval_tags, ncomp,
728 [x,scomp] AMREX_GPU_DEVICE (int i, int j, int k, int n, TagType const& tag) noexcept
729 {
730 tag.dfab(i,j,k,n+scomp) = x;
731 });
732}
733
734#if defined(__CUDACC__) && defined (AMREX_USE_CUDA)
735template <class FAB>
736void
737FabArray<FAB>::FB_local_copy_cuda_graph_1 (const FB& TheFB, int scomp, int ncomp)
738{
739 const int N_locs = (*TheFB.m_LocTags).size();
741 for (int i = 0; i < N_locs; ++i)
742 {
743 const CopyComTag& tag = (*TheFB.m_LocTags)[i];
744
745 BL_ASSERT(distributionMap[tag.dstIndex] == ParallelDescriptor::MyProc());
746 BL_ASSERT(distributionMap[tag.srcIndex] == ParallelDescriptor::MyProc());
747
748 loc_copy_tags[tag.dstIndex].push_back
749 ({this->fabPtr(tag.srcIndex), tag.dbox, tag.sbox.smallEnd()-tag.dbox.smallEnd()});
750 }
751
752 // Create Graph if one is needed.
753 if ( !(TheFB.m_localCopy.ready()) )
754 {
755 const_cast<FB&>(TheFB).m_localCopy.resize(N_locs);
756
757 int idx = 0;
758 // Record the graph.
759 for (MFIter mfi(*this, MFItInfo().DisableDeviceSync()); mfi.isValid(); ++mfi)
760 {
761 amrex::Gpu::Device::startGraphRecording( (mfi.LocalIndex() == 0),
762 const_cast<FB&>(TheFB).m_localCopy.getHostPtr(0),
763 (TheFB).m_localCopy.getDevicePtr(0),
764 std::size_t(sizeof(CopyMemory)*N_locs) );
765
766 const auto& tags = loc_copy_tags[mfi];
767 for (auto const & tag : tags)
768 {
769 const auto offset = tag.offset.dim3();
770 CopyMemory* cmem = TheFB.m_localCopy.getDevicePtr(idx++);
771 AMREX_HOST_DEVICE_FOR_3D (tag.dbox, i, j, k,
772 {
773 // Build the Array4's.
774 auto const dst = cmem->getDst<value_type>();
775 auto const src = cmem->getSrc<value_type>();
776 for (int n = 0; n < cmem->ncomp; ++n) {
777 dst(i,j,k,(cmem->scomp)+n) = src(i+offset.x,j+offset.y,k+offset.z,(cmem->scomp)+n);
778 }
779 });
780 }
781
782 bool last_iter = mfi.LocalIndex() == (this->local_size()-1);
783 cudaGraphExec_t graphExec = amrex::Gpu::Device::stopGraphRecording(last_iter);
784 if (last_iter) { const_cast<FB&>(TheFB).m_localCopy.setGraph( graphExec ); }
785 }
786 }
787
788 // Setup Launch Parameters
789 // This is perfectly threadable, right?
790 // Additional optimization -> Check to see whether values need to be reset?
791 // Can then remove this setup and memcpy from CudaGraph::executeGraph.
792 int idx = 0;
793 for (MFIter mfi(*this); mfi.isValid(); ++mfi)
794 {
795 auto const dst_array = this->array(mfi);
796 const auto& tags = loc_copy_tags[mfi];
797 for (auto const & tag : tags)
798 {
799 const_cast<FB&>(TheFB).m_localCopy.setParams(idx++, makeCopyMemory(tag.sfab->array(),
800 dst_array,
801 scomp, ncomp));
802 }
803 }
804
805 // Launch Graph
806 TheFB.m_localCopy.executeGraph();
807}
808
809#ifdef AMREX_USE_MPI
810template <class FAB>
811void
812FabArray<FAB>::FB_local_copy_cuda_graph_n (const FB& TheFB, int scomp, int ncomp)
813{
814 const int N_locs = TheFB.m_LocTags->size();
815
816 int launches = 0; // Used for graphs only.
817 LayoutData<Vector<FabCopyTag<FAB> > > loc_copy_tags(boxArray(),DistributionMap());
818 for (int i = 0; i < N_locs; ++i)
819 {
820 const CopyComTag& tag = (*TheFB.m_LocTags)[i];
821
822 BL_ASSERT(ParallelDescriptor::sameTeam(distributionMap[tag.dstIndex]));
823 BL_ASSERT(ParallelDescriptor::sameTeam(distributionMap[tag.srcIndex]));
824
825 if (distributionMap[tag.dstIndex] == ParallelDescriptor::MyProc())
826 {
827 loc_copy_tags[tag.dstIndex].push_back
828 ({this->fabPtr(tag.srcIndex), tag.dbox, tag.sbox.smallEnd()-tag.dbox.smallEnd()});
829 launches++;
830 }
831 }
832
833 FillBoundary_test();
834
835 if ( !(TheFB.m_localCopy.ready()) )
836 {
837 const_cast<FB&>(TheFB).m_localCopy.resize(launches);
838
839 int idx = 0;
840 int cuda_stream = 0;
841 for (MFIter mfi(*this, MFItInfo().DisableDeviceSync()); mfi.isValid(); ++mfi)
842 {
843 const auto& tags = loc_copy_tags[mfi];
844 for (int t = 0; t<tags.size(); ++t)
845 {
846 Gpu::Device::setStreamIndex(cuda_stream++);
847 amrex::Gpu::Device::startGraphRecording( (idx == 0),
848 const_cast<FB&>(TheFB).m_localCopy.getHostPtr(0),
849 (TheFB).m_localCopy.getDevicePtr(0),
850 std::size_t(sizeof(CopyMemory)*launches) );
851
852 const auto& tag = tags[t];
853 const Dim3 offset = tag.offset.dim3();
854
855 CopyMemory* cmem = TheFB.m_localCopy.getDevicePtr(idx++);
856 AMREX_HOST_DEVICE_FOR_3D(tag.dbox, i, j, k,
857 {
858 auto const dst = cmem->getDst<value_type>();
859 auto const src = cmem->getSrc<value_type>();
860 for (int n = 0; n < cmem->ncomp; ++n) {
861 dst(i,j,k,(cmem->scomp)+n) = src(i+offset.x,j+offset.y,k+offset.z,(cmem->scomp)+n);
862 }
863 });
864
865 bool last_iter = idx == launches;
866 cudaGraphExec_t graphExec = Gpu::Device::stopGraphRecording(last_iter);
867 if (last_iter) { const_cast<FB&>(TheFB).m_localCopy.setGraph( graphExec ); }
868 }
869 }
870 }
871
872 // Setup Launch Parameters
873 // This is perfectly threadable, right?
874 int idx = 0;
875 for (MFIter mfi(*this); mfi.isValid(); ++mfi)
876 {
877 const auto& dst_array = this->array(mfi);
878 const auto& tags = loc_copy_tags[mfi];
879 for (auto const & tag : tags)
880 {
881 const_cast<FB&>(TheFB).m_localCopy.setParams(idx++, makeCopyMemory(tag.sfab->array(),
882 dst_array,
883 scomp, ncomp));
884 }
885 }
886
887 // Launch Graph without synch. Local work is entirely independent.
888 TheFB.m_localCopy.executeGraph(false);
889}
890#endif /* AMREX_USE_MPI */
891
892#endif /* __CUDACC__ */
893
894#endif /* AMREX_USE_GPU */
895
896#ifdef AMREX_USE_MPI
897
898#ifdef AMREX_USE_GPU
899
900#if defined(__CUDACC__) && defined(AMREX_USE_CUDA)
901
902template <class FAB>
903void
904FabArray<FAB>::FB_pack_send_buffer_cuda_graph (const FB& TheFB, int scomp, int ncomp,
905 Vector<char*>& send_data,
906 Vector<std::size_t> const& send_size,
907 Vector<typename FabArray<FAB>::CopyComTagsContainer const*> const& send_cctc)
908{
909 const int N_snds = send_data.size();
910 if (N_snds == 0) { return; }
911
912 if ( !(TheFB.m_copyToBuffer.ready()) )
913 {
914 // Set size of CudaGraph buffer.
915 // Is the conditional ever expected false?
916 int launches = 0;
917 for (int send = 0; send < N_snds; ++send) {
918 if (send_size[send] > 0) {
919 launches += send_cctc[send]->size();
920 }
921 }
922 const_cast<FB&>(TheFB).m_copyToBuffer.resize(launches);
923
924 // Record the graph.
925 int idx = 0;
926 for (Gpu::StreamIter sit(N_snds,Gpu::StreamItInfo().DisableDeviceSync());
927 sit.isValid(); ++sit)
928 {
929 amrex::Gpu::Device::startGraphRecording( (sit() == 0),
930 const_cast<FB&>(TheFB).m_copyToBuffer.getHostPtr(0),
931 (TheFB).m_copyToBuffer.getDevicePtr(0),
932 std::size_t(sizeof(CopyMemory)*launches) );
933
934 const int j = sit();
935 if (send_size[j] > 0)
936 {
937 auto const& cctc = *send_cctc[j];
938 for (auto const& tag : cctc)
939 {
940 const Box& bx = tag.sbox;
941 CopyMemory* cmem = TheFB.m_copyToBuffer.getDevicePtr(idx++);
942 AMREX_HOST_DEVICE_FOR_3D (bx, ii, jj, kk,
943 {
944 auto const pfab = cmem->getDst<value_type>();
945 auto const sfab = cmem->getSrc<value_type>();
946 for (int n = 0; n < cmem->ncomp; ++n)
947 {
948 pfab(ii,jj,kk,n) = sfab(ii,jj,kk,n+(cmem->scomp));
949 }
950 });
951 }
952 }
953
954 bool last_iter = sit() == (N_snds-1);
955 cudaGraphExec_t graphExec = amrex::Gpu::Device::stopGraphRecording(last_iter);
956 if (last_iter) { const_cast<FB&>(TheFB).m_copyToBuffer.setGraph( graphExec ); }
957 }
958 }
959
960 // Setup Launch Parameters
961 int idx = 0;
962 for (int send = 0; send < N_snds; ++send)
963 {
964 const int j = send;
965 if (send_size[j] > 0)
966 {
967 char* dptr = send_data[j];
968 auto const& cctc = *send_cctc[j];
969 for (auto const& tag : cctc)
970 {
971 const_cast<FB&>(TheFB).m_copyToBuffer.setParams(idx++, makeCopyMemory(this->array(tag.srcIndex),
972 amrex::makeArray4((value_type*)(dptr),
973 tag.sbox,
974 ncomp),
975 scomp, ncomp));
976
977 dptr += (tag.sbox.numPts() * ncomp * sizeof(value_type));
978 }
979 amrex::ignore_unused(send_size);
980 BL_ASSERT(dptr <= send_data[j] + send_size[j]);
981 }
982 }
983
984 // Launch Graph synched, so copyToBuffer is complete prior to posting sends.
985 TheFB.m_copyToBuffer.executeGraph();
986}
987
988template <class FAB>
989void
990FabArray<FAB>::FB_unpack_recv_buffer_cuda_graph (const FB& TheFB, int dcomp, int ncomp,
991 Vector<char*> const& recv_data,
992 Vector<std::size_t> const& recv_size,
993 Vector<CopyComTagsContainer const*> const& recv_cctc,
994 bool /*is_thread_safe*/)
995{
996 const int N_rcvs = recv_cctc.size();
997 if (N_rcvs == 0) { return; }
998
999 int launches = 0;
1000 LayoutData<Vector<VoidCopyTag> > recv_copy_tags(boxArray(),DistributionMap());
1001 for (int k = 0; k < N_rcvs; ++k)
1002 {
1003 if (recv_size[k] > 0)
1004 {
1005 const char* dptr = recv_data[k];
1006 auto const& cctc = *recv_cctc[k];
1007 for (auto const& tag : cctc)
1008 {
1009 recv_copy_tags[tag.dstIndex].push_back(VoidCopyTag{.p = dptr, .dbox = tag.dbox});
1010 dptr += tag.dbox.numPts() * ncomp * sizeof(value_type);
1011 launches++;
1012 }
1013 amrex::ignore_unused(recv_size);
1014 BL_ASSERT(dptr <= recv_data[k] + recv_size[k]);
1015 }
1016 }
1017
1018 if ( !(TheFB.m_copyFromBuffer.ready()) )
1019 {
1020 const_cast<FB&>(TheFB).m_copyFromBuffer.resize(launches);
1021
1022 int idx = 0;
1023 for (MFIter mfi(*this, MFItInfo().DisableDeviceSync()); mfi.isValid(); ++mfi)
1024 {
1025 amrex::Gpu::Device::startGraphRecording( (mfi.LocalIndex() == 0),
1026 const_cast<FB&>(TheFB).m_copyFromBuffer.getHostPtr(0),
1027 (TheFB).m_copyFromBuffer.getDevicePtr(0),
1028 std::size_t(sizeof(CopyMemory)*launches) );
1029
1030 const auto& tags = recv_copy_tags[mfi];
1031 for (auto const & tag : tags)
1032 {
1033 CopyMemory* cmem = TheFB.m_copyFromBuffer.getDevicePtr(idx++);
1034 AMREX_HOST_DEVICE_FOR_3D (tag.dbox, i, j, k,
1035 {
1036 auto const pfab = cmem->getSrc<value_type>();
1037 auto const dfab = cmem->getDst<value_type>();
1038 for (int n = 0; n < cmem->ncomp; ++n)
1039 {
1040 dfab(i,j,k,n+(cmem->scomp)) = pfab(i,j,k,n);
1041 }
1042 });
1043 }
1044
1045 bool last_iter = mfi.LocalIndex() == (this->local_size()-1);
1046 cudaGraphExec_t graphExec = amrex::Gpu::Device::stopGraphRecording(last_iter);
1047 if (last_iter) { const_cast<FB&>(TheFB).m_copyFromBuffer.setGraph( graphExec ); }
1048 }
1049 }
1050
1051 // Setup graph.
1052 int idx = 0;
1053 for (MFIter mfi(*this); mfi.isValid(); ++mfi)
1054 {
1055 auto dst_array = this->array(mfi);
1056 const auto & tags = recv_copy_tags[mfi];
1057 for (auto const & tag : tags)
1058 {
1059 const_cast<FB&>(TheFB).m_copyFromBuffer.setParams(idx++, makeCopyMemory(amrex::makeArray4((value_type*)(tag.p),
1060 tag.dbox,
1061 ncomp),
1062 dst_array,
1063 dcomp, ncomp));
1064 }
1065 }
1066
1067 // Launch Graph - synced because next action is freeing recv buffer.
1068 TheFB.m_copyFromBuffer.executeGraph();
1069}
1070
1071#endif /* __CUDACC__ */
1072
1073template <class FAB>
1074template <typename BUF>
1075auto
1077 Vector<std::size_t> const& send_size,
1078 Vector<CopyComTagsContainer const*> const& send_cctc,
1079 int ncomp, std::uint64_t id) const
1081{
1082 using TagType = CommSendBufTag<value_type>;
1083
1084 auto kit = std::find_if(send_cctc.begin(), send_cctc.end(),
1085 [] (CopyComTagsContainer const* p) { return p != nullptr; });
1086 if (kit == send_cctc.end()) {
1087 return nullptr;
1088 }
1089
1090 auto get_tags = [&] () -> Vector<TagType>
1091 {
1092 Vector<TagType> snd_copy_tags;
1093 char* pbuf = send_data[0];
1094 const int N_snds = send_data.size();
1095 for (int j = 0; j < N_snds; ++j)
1096 {
1097 if (send_size[j] > 0)
1098 {
1099 char* dptr = send_data[j];
1100 auto const& cctc = *send_cctc[j];
1101 for (auto const& tag : cctc)
1102 {
1103 snd_copy_tags.emplace_back
1104 (TagType{.sfab = this->const_array(tag.srcIndex), .poff = dptr-pbuf, .bx = tag.sbox});
1105 dptr += (tag.sbox.numPts() * ncomp * sizeof(BUF));
1106 }
1107 }
1108 }
1109 return snd_copy_tags;
1110 };
1111
1113 std::tuple<std::uint64_t,std::size_t,int> key{id, sizeof(BUF), ncomp};
1114
1115 if (auto it = m_send_copy_handler.find(key); it != m_send_copy_handler.end()) {
1116 tv = it->second.get();
1117 } else {
1118 if (m_send_copy_handler.size() > 32) {
1119 // Just in case. If this is used in ParallelCopy, it's possible
1120 // that the sending FabArray is the same, but the receiving
1121 // FabArray is different every time. Then the size of this map
1122 // could increase indefinitely.
1123 m_send_copy_handler.clear();
1124 }
1125 auto snd_copy_tags = get_tags();
1126 auto utv = std::make_unique<TagVector<TagType>>(snd_copy_tags);
1127 tv = utv.get();
1128 m_send_copy_handler[key] = std::move(utv);
1129 }
1130
1131 return tv;
1132}
1133
1134template <class FAB>
1135template <typename BUF>
1136void
1137FabArray<FAB>::pack_send_buffer_gpu (FabArray<FAB> const& src, int scomp, int ncomp,
1138 Vector<char*> const& send_data,
1139 Vector<std::size_t> const& send_size,
1140 Vector<CopyComTagsContainer const*> const& send_cctc,
1141 std::uint64_t id)
1142{
1143 const int N_snds = send_data.size();
1144 if (N_snds == 0) { return; }
1145
1146 using TagType = CommSendBufTag<value_type>;
1147
1148 auto* tv = src.template get_send_copy_tag_vector<BUF>
1149 (send_data, send_size, send_cctc, ncomp, id);
1150 if (tv == nullptr) { return; }
1151
1152 char* pbuffer = send_data[0];
1153
1154 detail::ParallelFor_doit(*tv,
1155 [=] AMREX_GPU_DEVICE (
1156 int icell, int ncells, int i, int j, int k, TagType const& tag) noexcept
1157 {
1158 if (icell < ncells) {
1159 Array4<BUF> dfab{(BUF*)(pbuffer+tag.poff),
1160 amrex::begin(tag.bx), amrex::end(tag.bx), ncomp};
1161 for (int n = 0; n < ncomp; ++n) {
1162 dfab(i,j,k,n) = (BUF)tag.sfab(i,j,k,n+scomp);
1163 }
1164 }
1165 });
1166
1168}
1169
1170template <class FAB>
1171template <typename BUF>
1172auto
1174 Vector<std::size_t> const& recv_size,
1175 Vector<CopyComTagsContainer const*> const& recv_cctc,
1176 int ncomp, std::uint64_t id)
1178{
1179 using TagType = CommRecvBufTag<value_type>;
1180
1181 auto kit = std::find_if(recv_cctc.begin(), recv_cctc.end(),
1182 [] (CopyComTagsContainer const* p) { return p != nullptr; });
1183 if (kit == recv_cctc.end()) {
1184 return nullptr;
1185 }
1186
1187 auto get_tags = [&] () -> Vector<TagType>
1188 {
1189 Vector<TagType> recv_copy_tags;
1190 char* pbuf = recv_data[0];
1191 const int N_rcvs = recv_cctc.size();
1192 for (int k = 0; k < N_rcvs; ++k)
1193 {
1194 if (recv_size[k] > 0)
1195 {
1196 char* dptr = recv_data[k];
1197 auto const& cctc = *recv_cctc[k];
1198 for (auto const& tag : cctc)
1199 {
1200 const int li = this->localindex(tag.dstIndex);
1201 recv_copy_tags.emplace_back
1202 (TagType{.dfab = this->atLocalIdx(li).array(), .poff = dptr-pbuf, .bx = tag.dbox});
1203 dptr += tag.dbox.numPts() * ncomp * sizeof(BUF);
1204 }
1205 }
1206 }
1207 return recv_copy_tags;
1208 };
1209
1211 std::tuple<std::uint64_t,std::size_t,int> key{id, sizeof(BUF), ncomp};
1212
1213 if (auto it = m_recv_copy_handler.find(key); it != m_recv_copy_handler.end()) {
1214 tv = it->second.get();
1215 } else {
1216 if (m_recv_copy_handler.size() > 32) {
1217 // Just in case. If this is used in ParallelCopy, it's possible
1218 // that the receiving FabArray is the same, but the sending
1219 // FabArray is different every time. Then the size of this map
1220 // could increase indefinitely.
1221 m_recv_copy_handler.clear();
1222 }
1223 auto recv_copy_tags = get_tags();
1224 auto utv = std::make_unique<TagVector<TagType>>(recv_copy_tags);
1225 tv = utv.get();
1226 m_recv_copy_handler[key] = std::move(utv);
1227 }
1228
1229 return tv;
1230}
1231
1232template <class FAB>
1233template <typename BUF>
1234void
1236 Vector<char*> const& recv_data,
1237 Vector<std::size_t> const& recv_size,
1238 Vector<CopyComTagsContainer const*> const& recv_cctc,
1239 CpOp op, bool is_thread_safe, std::uint64_t id,
1240 bool deterministic)
1241{
1242 const int N_rcvs = recv_cctc.size();
1243 if (N_rcvs == 0) { return; }
1244
1245 bool use_mask = false;
1246 if (!is_thread_safe)
1247 {
1249 (op == FabArrayBase::ADD && !amrex::HasAtomicAdd <value_type>::value))
1250 {
1251 use_mask = true;
1252 }
1253 }
1254
1255 if (deterministic)
1256 {
1257 AMREX_ALWAYS_ASSERT(op == FabArrayBase::ADD); // Only ADD for now
1258 using TagType = Array4CopyTag<value_type,BUF>;
1260 tags.reserve(N_rcvs);
1261 for (int k = 0; k < N_rcvs; ++k) {
1262 if (recv_size[k] > 0) {
1263 char const* dptr = recv_data[k];
1264 auto const& cctc = *recv_cctc[k];
1265 for (auto const& tag : cctc) {
1266 tags.emplace_back(
1267 TagType{.dfab = dst.array(tag.dstIndex), .dindex = tag.dstIndex,
1268 .sfab = Array4<BUF const>((BUF const*)dptr,
1269 amrex::begin(tag.dbox),
1270 amrex::end(tag.dbox), ncomp),
1271 .dbox = tag.dbox,
1272 .offset = Dim3{.x = 0, .y = 0, .z = 0}});
1273 dptr += tag.dbox.numPts() * ncomp * sizeof(BUF);
1274 }
1275 }
1276 }
1278 detail::deterministic_fab_to_fab<value_type,BUF>
1279 (tags, 0, dcomp, ncomp, detail::CellAdd<value_type,BUF>{});
1280 } else {
1281 amrex::Abort("SumBoundary requires operator+=");
1282 }
1283 }
1284 else if (!use_mask)
1285 {
1286 using TagType = CommRecvBufTag<value_type>;
1287 auto* tv = dst.template get_recv_copy_tag_vector<BUF>
1288 (recv_data, recv_size, recv_cctc, ncomp, id);
1289 if (tv == nullptr) { return; }
1290
1291 char* pbuffer = recv_data[0];
1292
1293 if (op == FabArrayBase::COPY)
1294 {
1295 detail::ParallelFor_doit(*tv,
1296 [=] AMREX_GPU_DEVICE (
1297 int icell, int ncells, int i, int j, int k, TagType const& tag) noexcept
1298 {
1299 if (icell < ncells) {
1300 Array4<BUF const> sfab{(BUF const*)(pbuffer+tag.poff),
1301 amrex::begin(tag.bx), amrex::end(tag.bx), ncomp};
1302 for (int n = 0; n < ncomp; ++n) {
1303 tag.dfab(i,j,k,n+dcomp) = (value_type)sfab(i,j,k,n);
1304 }
1305 }
1306 });
1307 }
1308 else
1309 {
1310 if (is_thread_safe) {
1311 detail::ParallelFor_doit(*tv,
1312 [=] AMREX_GPU_DEVICE (
1313 int icell, int ncells, int i, int j, int k, TagType const& tag) noexcept
1314 {
1315 if (icell < ncells) {
1316 Array4<BUF const> sfab{(BUF const*)(pbuffer+tag.poff),
1317 amrex::begin(tag.bx), amrex::end(tag.bx), ncomp};
1318 for (int n = 0; n < ncomp; ++n) {
1319 tag.dfab(i,j,k,n+dcomp) += (value_type)sfab(i,j,k,n);
1320 }
1321 }
1322 });
1323 } else {
1324 detail::unpack_recv_buffer_gpu_atomic_add<BUF, value_type>
1325 (pbuffer, *tv, dcomp, ncomp);
1326 }
1327 }
1329 }
1330 else
1331 {
1332 char* pbuffer = recv_data[0];
1333
1334 using TagType = Array4CopyTag<value_type, BUF>;
1335 Vector<TagType> recv_copy_tags;
1336 recv_copy_tags.reserve(N_rcvs);
1337
1338 Vector<BaseFab<int> > maskfabs(dst.local_size());
1339 Vector<Array4Tag<int> > masks_unique;
1340 masks_unique.reserve(dst.local_size());
1341 Vector<Array4Tag<int> > masks;
1342
1343 for (int k = 0; k < N_rcvs; ++k)
1344 {
1345 if (recv_size[k] > 0)
1346 {
1347 std::size_t offset = recv_data[k]-recv_data[0];
1348 const char* dptr = pbuffer + offset;
1349 auto const& cctc = *recv_cctc[k];
1350 for (auto const& tag : cctc)
1351 {
1352 const int li = dst.localindex(tag.dstIndex);
1353 recv_copy_tags.emplace_back(TagType{
1354 .dfab = dst.atLocalIdx(li).array(), .dindex = tag.dstIndex,
1355 .sfab = amrex::makeArray4((BUF const*)(dptr), tag.dbox, ncomp),
1356 .dbox = tag.dbox,
1357 .offset = Dim3{.x = 0, .y = 0, .z = 0}
1358 });
1359 dptr += tag.dbox.numPts() * ncomp * sizeof(BUF);
1360
1361 if (!maskfabs[li].isAllocated()) {
1362 maskfabs[li].resize(dst.atLocalIdx(li).box());
1363 masks_unique.emplace_back(Array4Tag<int>{.dfab = maskfabs[li].array()});
1364 }
1365 masks.emplace_back(Array4Tag<int>{.dfab = maskfabs[li].array()});
1366 }
1367 BL_ASSERT(dptr <= pbuffer + offset + recv_size[k]);
1368 }
1369 }
1370
1371 amrex::ParallelFor(masks_unique,
1372 [=] AMREX_GPU_DEVICE (int i, int j, int k, Array4Tag<int> const& msk) noexcept
1373 {
1374 msk.dfab(i,j,k) = 0;
1375 });
1376
1377 if (op == FabArrayBase::COPY)
1378 {
1379 detail::fab_to_fab_atomic_cpy<value_type, BUF>(
1380 recv_copy_tags, 0, dcomp, ncomp, masks);
1381 }
1382 else
1383 {
1384 detail::fab_to_fab_atomic_add<value_type, BUF>(
1385 recv_copy_tags, 0, dcomp, ncomp, masks);
1386 }
1387
1388 // There is Gpu::streamSynchronize in fab_to_fab.
1389 }
1390}
1391
1392#endif /* AMREX_USE_GPU */
1393
1394template <class FAB>
1395template <typename BUF>
1396void
1397FabArray<FAB>::pack_send_buffer_cpu (FabArray<FAB> const& src, int scomp, int ncomp,
1398 Vector<char*> const& send_data,
1399 Vector<std::size_t> const& send_size,
1400 Vector<CopyComTagsContainer const*> const& send_cctc)
1401{
1402 amrex::ignore_unused(send_size);
1403
1404 auto const N_snds = static_cast<int>(send_data.size());
1405 if (N_snds == 0) { return; }
1406
1407#ifdef AMREX_USE_OMP
1408 if (omp_get_max_threads() > 1)
1409 {
1410 // Threads share tags, not neighbor ranks, which are often fewer than threads.
1412 for (int j = 0; j < N_snds; ++j)
1413 {
1414 if (send_size[j] > 0)
1415 {
1416 char* dptr = send_data[j];
1417 for (auto const& tag : *send_cctc[j])
1418 {
1419 tags.emplace_back(dptr, &tag);
1420 dptr += (tag.sbox.numPts() * ncomp * sizeof(BUF));
1421 }
1422 BL_ASSERT(dptr <= send_data[j] + send_size[j]);
1423 }
1424 }
1425
1426 auto const N_tags = static_cast<int>(tags.size());
1427 // Small chunks keep threads on nearby boxes, which share cache lines.
1428 int const chunk = std::clamp(N_tags/omp_get_max_threads(), 1, 64);
1429#pragma omp parallel for schedule(static,chunk)
1430 for (int i = 0; i < N_tags; ++i)
1431 {
1432 auto const& tag = *tags[i].second;
1433 const Box& bx = tag.sbox;
1434 auto const sfab = src.array(tag.srcIndex);
1435 auto pfab = amrex::makeArray4((BUF*)(tags[i].first),bx,ncomp);
1436 amrex::LoopConcurrentOnCpu( bx, ncomp,
1437 [=] (int ii, int jj, int kk, int n) noexcept
1438 {
1439 pfab(ii,jj,kk,n) = static_cast<BUF>(sfab(ii,jj,kk,n+scomp));
1440 });
1441 }
1442 return;
1443 }
1444#endif
1445
1446 for (int j = 0; j < N_snds; ++j)
1447 {
1448 if (send_size[j] > 0)
1449 {
1450 char* dptr = send_data[j];
1451 auto const& cctc = *send_cctc[j];
1452 for (auto const& tag : cctc)
1453 {
1454 const Box& bx = tag.sbox;
1455 auto const sfab = src.array(tag.srcIndex);
1456 auto pfab = amrex::makeArray4((BUF*)(dptr),bx,ncomp);
1457 amrex::LoopConcurrentOnCpu( bx, ncomp,
1458 [=] (int ii, int jj, int kk, int n) noexcept
1459 {
1460 pfab(ii,jj,kk,n) = static_cast<BUF>(sfab(ii,jj,kk,n+scomp));
1461 });
1462 dptr += (bx.numPts() * ncomp * sizeof(BUF));
1463 }
1464 BL_ASSERT(dptr <= send_data[j] + send_size[j]);
1465 }
1466 }
1467}
1468
1469template <class FAB>
1470template <typename BUF>
1471void
1473 Vector<char*> const& recv_data,
1474 Vector<std::size_t> const& recv_size,
1475 Vector<CopyComTagsContainer const*> const& recv_cctc,
1476 CpOp op, bool is_thread_safe)
1477{
1478 amrex::ignore_unused(recv_size);
1479
1480 auto const N_rcvs = static_cast<int>(recv_cctc.size());
1481 if (N_rcvs == 0) { return; }
1482
1483 if (is_thread_safe)
1484 {
1485#ifdef AMREX_USE_OMP
1486 if (omp_get_max_threads() > 1)
1487 {
1488 // Threads share tags, not neighbor ranks, which are often fewer than threads.
1490 for (int k = 0; k < N_rcvs; ++k)
1491 {
1492 if (recv_size[k] > 0)
1493 {
1494 const char* dptr = recv_data[k];
1495 for (auto const& tag : *recv_cctc[k])
1496 {
1497 tags.emplace_back(dptr, &tag);
1498 dptr += tag.dbox.numPts() * ncomp * sizeof(BUF);
1499 }
1500 BL_ASSERT(dptr <= recv_data[k] + recv_size[k]);
1501 }
1502 }
1503
1504 auto const N_tags = static_cast<int>(tags.size());
1505 // Small chunks keep threads on nearby boxes, which share cache lines.
1506 int const chunk = std::clamp(N_tags/omp_get_max_threads(), 1, 64);
1507#pragma omp parallel for schedule(static,chunk)
1508 for (int i = 0; i < N_tags; ++i)
1509 {
1510 auto const& tag = *tags[i].second;
1511 FAB& dfab = dst[tag.dstIndex];
1512 if (op == FabArrayBase::COPY)
1513 {
1514 dfab.template copyFromMem<RunOn::Host, BUF>(tag.dbox, dcomp, ncomp, tags[i].first);
1515 }
1516 else
1517 {
1518 dfab.template addFromMem<RunOn::Host, BUF>(tag.dbox, dcomp, ncomp, tags[i].first);
1519 }
1520 }
1521 return;
1522 }
1523#endif
1524
1525 for (int k = 0; k < N_rcvs; ++k)
1526 {
1527 if (recv_size[k] > 0)
1528 {
1529 const char* dptr = recv_data[k];
1530 auto const& cctc = *recv_cctc[k];
1531 for (auto const& tag : cctc)
1532 {
1533 const Box& bx = tag.dbox;
1534 FAB& dfab = dst[tag.dstIndex];
1535 if (op == FabArrayBase::COPY)
1536 {
1537 dfab.template copyFromMem<RunOn::Host, BUF>(bx, dcomp, ncomp, dptr);
1538 }
1539 else
1540 {
1541 dfab.template addFromMem<RunOn::Host, BUF>(tag.dbox, dcomp, ncomp, dptr);
1542 }
1543 dptr += bx.numPts() * ncomp * sizeof(BUF);
1544 }
1545 BL_ASSERT(dptr <= recv_data[k] + recv_size[k]);
1546 }
1547 }
1548 }
1549 else
1550 {
1551 LayoutData<Vector<VoidCopyTag> > recv_copy_tags;
1552 recv_copy_tags.define(dst.boxArray(),dst.DistributionMap());
1553 for (int k = 0; k < N_rcvs; ++k)
1554 {
1555 if (recv_size[k] > 0)
1556 {
1557 const char* dptr = recv_data[k];
1558 auto const& cctc = *recv_cctc[k];
1559 for (auto const& tag : cctc)
1560 {
1561 recv_copy_tags[tag.dstIndex].push_back(VoidCopyTag{.p = dptr, .dbox = tag.dbox});
1562 dptr += tag.dbox.numPts() * ncomp * sizeof(BUF);
1563 }
1564 BL_ASSERT(dptr <= recv_data[k] + recv_size[k]);
1565 }
1566 }
1567
1568#ifdef AMREX_USE_OMP
1569#pragma omp parallel
1570#endif
1571 for (MFIter mfi(dst); mfi.isValid(); ++mfi)
1572 {
1573 const auto& tags = recv_copy_tags[mfi];
1574 auto dfab = dst.array(mfi);
1575 for (auto const & tag : tags)
1576 {
1577 auto pfab = amrex::makeArray4((BUF*)(tag.p), tag.dbox, ncomp);
1578 if (op == FabArrayBase::COPY)
1579 {
1580 amrex::LoopConcurrentOnCpu(tag.dbox, ncomp,
1581 [=] (int i, int j, int k, int n) noexcept
1582 {
1583 dfab(i,j,k,n+dcomp) = pfab(i,j,k,n);
1584 });
1585 }
1586 else
1587 {
1588 amrex::LoopConcurrentOnCpu(tag.dbox, ncomp,
1589 [=] (int i, int j, int k, int n) noexcept
1590 {
1591 dfab(i,j,k,n+dcomp) += pfab(i,j,k,n);
1592 });
1593 }
1594 }
1595 }
1596 }
1597}
1598
1599#endif /* AMREX_USE_MPI */
1600
1601}
1602
1603#endif
#define BL_ASSERT(EX)
Definition AMReX_BLassert.H:39
#define AMREX_ALWAYS_ASSERT(EX)
Definition AMReX_BLassert.H:50
#define AMREX_FORCE_INLINE
Definition AMReX_Extension.H:124
#define AMREX_HOST_DEVICE_FOR_3D(...)
Definition AMReX_GpuLaunchMacrosC.nolint.H:106
#define AMREX_GPU_DEVICE
Definition AMReX_GpuQualifiers.H:18
while parser_exe_t p
Definition AMReX_Parser_Exe_Body.H:33
#define AMREX_D_DECL(a, b, c)
Definition AMReX_SPACE.H:171
__host__ __device__ Long numPts() const noexcept
Return the number of points contained in the BoxND.
Definition AMReX_Box.H:385
__host__ __device__ const IntVectND< dim > & smallEnd() const &noexcept
Return the inclusive lower bound of the box.
Definition AMReX_Box.H:124
friend class MFIter
Definition AMReX_FabArrayBase.H:48
CopyComTag::CopyComTagsContainer CopyComTagsContainer
Definition AMReX_FabArrayBase.H:236
int localindex(int K) const noexcept
Return local index in the vector of FABs.
Definition AMReX_FabArrayBase.H:124
const DistributionMapping & DistributionMap() const noexcept
Return constant reference to associated DistributionMapping.
Definition AMReX_FabArrayBase.H:135
int local_size() const noexcept
Return the number of local FABs in the FabArray.
Definition AMReX_FabArrayBase.H:118
CpOp
parallel copy or add
Definition AMReX_FabArrayBase.H:424
@ ADD
Definition AMReX_FabArrayBase.H:424
@ COPY
Definition AMReX_FabArrayBase.H:424
Box box(int K) const noexcept
Return the Kth Box in the BoxArray. That is, the valid region of the Kth grid.
Definition AMReX_FabArrayBase.H:106
DistributionMapping distributionMap
Definition AMReX_FabArrayBase.H:528
const BoxArray & boxArray() const noexcept
Return a constant reference to the BoxArray that defines the valid region associated with this FabArr...
Definition AMReX_FabArrayBase.H:100
An Array of FortranArrayBox(FAB)-like Objects.
Definition AMReX_FabArray.H:356
TagVector< CommSendBufTag< value_type > > const * get_send_copy_tag_vector(Vector< char * > const &send_data, Vector< std::size_t > const &send_size, Vector< CopyComTagsContainer const * > const &send_cctc, int ncomp, std::uint64_t id) const
typename std::conditional_t< IsBaseFab< FAB >::value, FAB, FABType >::value_type value_type
Definition AMReX_FabArray.H:367
void CMD_remote_setVal_gpu(value_type x, const CommMetaData &thecmd, int scomp, int ncomp)
Definition AMReX_FBI.H:708
static void unpack_recv_buffer_gpu(FabArray< FAB > &dst, int dcomp, int ncomp, Vector< char * > const &recv_data, Vector< std::size_t > const &recv_size, Vector< const CopyComTagsContainer * > const &recv_cctc, CpOp op, bool is_thread_safe, std::uint64_t id, bool deterministic)
void FB_local_add_cpu(const FB &TheFB, int scomp, int ncomp)
Definition AMReX_FBI.H:467
static void unpack_recv_buffer_cpu(FabArray< FAB > &dst, int dcomp, int ncomp, Vector< char * > const &recv_data, Vector< std::size_t > const &recv_size, Vector< const CopyComTagsContainer * > const &recv_cctc, CpOp op, bool is_thread_safe)
void FB_local_add_gpu(const FB &TheFB, int scomp, int ncomp, bool deterministic)
Definition AMReX_FBI.H:630
void FB_local_copy_gpu(const FB &TheFB, int scomp, int ncomp)
Definition AMReX_FBI.H:558
void copy(const FabArray< FAB > &src, const Periodicity &period=Periodicity::NonPeriodic(), CpOp op=FabArrayBase::COPY)
Definition AMReX_FabArray.H:977
const Vector< std::string > & tags() const noexcept
Memory-usage tags recorded for this FabArray's allocations.
Definition AMReX_FabArray.H:488
void CMD_local_setVal_gpu(value_type x, const CommMetaData &thecmd, int scomp, int ncomp)
Definition AMReX_FBI.H:678
static void pack_send_buffer_gpu(FabArray< FAB > const &src, int scomp, int ncomp, Vector< char * > const &send_data, Vector< std::size_t > const &send_size, Vector< const CopyComTagsContainer * > const &send_cctc, std::uint64_t id)
Array4< typename FabArray< FAB >::value_type const > array(const MFIter &mfi) const noexcept
Read-only Array4 view for the FAB referenced by iterator mfi.
Definition AMReX_FabArray.H:621
void FB_local_copy_cpu(const FB &TheFB, int scomp, int ncomp)
Definition AMReX_FBI.H:396
TagVector< Array4CopyTag< value_type > > const * FB_get_local_copy_tag_vector(const FB &TheFB)
Definition AMReX_FBI.H:515
static void pack_send_buffer_cpu(FabArray< FAB > const &src, int scomp, int ncomp, Vector< char * > const &send_data, Vector< std::size_t > const &send_size, Vector< const CopyComTagsContainer * > const &send_cctc)
FAB * fabPtr(const MFIter &mfi) noexcept
Return pointer to FAB.
Definition AMReX_FabArray.H:2065
TagVector< CommRecvBufTag< value_type > > const * get_recv_copy_tag_vector(Vector< char * > const &recv_data, Vector< std::size_t > const &recv_size, Vector< CopyComTagsContainer const * > const &recv_cctc, int ncomp, std::uint64_t id)
FAB & atLocalIdx(int L) noexcept
Return a reference to the FAB associated with local index L.
Definition AMReX_FabArray.H:579
Array4< typename FabArray< FAB >::value_type const > const_array(const MFIter &mfi) const noexcept
Synonym for array(const MFIter&) that highlights read-only semantics.
Definition AMReX_FabArray.H:649
a one-thingy-per-box distributed object
Definition AMReX_LayoutData.H:13
void define(const BoxArray &a_grids, const DistributionMapping &a_dm)
Definition AMReX_LayoutData.H:24
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
This class is a thin wrapper around std::vector. Unlike vector, Vector::operator[] provides bound che...
Definition AMReX_Vector.H:29
Long size() const noexcept
Definition AMReX_Vector.H:54
__host__ __device__ Dim3 length(Array4< T > const &a) noexcept
Return the spatial extents of an Array4 in Dim3 form.
Definition AMReX_Array4.H:1379
ArrayND< T, 4, true > Array4
Convenient alias for a 4D ArrayND with three spatial dimensions plus a component dimension.
Definition AMReX_Array4.H:1340
__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 > 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:1471
PinnedVector< T > HostVector
A PODVector that uses pinned host memory. Same as PinnedVector. For a vector class that uses std::all...
Definition AMReX_GpuContainers.H:67
PODVector< T, ArenaAllocator< T > > DeviceVector
A PODVector that uses the standard memory Arena. Note that the memory might or might not be managed d...
Definition AMReX_GpuContainers.H:24
int MyProc() noexcept
Definition AMReX_ParallelDescriptor.H:128
__host__ __device__ AMREX_FORCE_INLINE T Exch(T *address, T val) noexcept
Definition AMReX_GpuAtomic.H:487
__host__ __device__ AMREX_FORCE_INLINE T CAS(T *const address, T compare, T const val) noexcept
Definition AMReX_GpuAtomic.H:513
__host__ __device__ AMREX_FORCE_INLINE void AddNoRet(T *sum, T value) noexcept
Definition AMReX_GpuAtomic.H:283
__host__ __device__ AMREX_FORCE_INLINE T Add(T *sum, T value) noexcept
Definition AMReX_GpuAtomic.H:200
void copyAsync(HostToDevice, InIter begin, InIter end, OutIter result) noexcept
A host-to-device copy routine. Note this is just a wrapper around memcpy, so it assumes contiguous st...
Definition AMReX_GpuContainers.H:228
static constexpr HostToDevice hostToDevice
Definition AMReX_GpuContainers.H:105
void streamSynchronize() noexcept
Definition AMReX_GpuDevice.H:329
gpuStream_t gpuStream() noexcept
Definition AMReX_GpuDevice.H:310
bool sameTeam(int rank) noexcept
Definition AMReX_ParallelDescriptor.H:345
int TeamSize() noexcept
Definition AMReX_ParallelDescriptor.H:310
const ProcessTeam & MyTeam() noexcept
Definition AMReX_ParallelDescriptor.H:365
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
__host__ __device__ Array4< T > makeArray4(T *p, Box const &bx, int ncomp) noexcept
Definition AMReX_BaseFab.H:123
BoxArray const & boxArray(FabArrayBase const &fa)
Convenience wrapper that forwards to fa.boxArray().
Definition AMReX_FabArrayBase.cpp:2990
DistributionMapping const & DistributionMap(FabArrayBase const &fa)
Convenience wrapper that forwards to fa.DistributionMap().
Definition AMReX_FabArrayBase.cpp:2995
__host__ __device__ Dim3 begin(BoxND< dim > const &box) noexcept
Return the iterator begin coordinate of box as Dim3.
Definition AMReX_Box.H:2241
bool NeighborParticleContainer_impl< T_ParticleType, NArrayReal, NArrayInt, Allocator, CellAssignor >::use_mask
Definition AMReX_NeighborParticlesI.H:13
void launch(T const &n, L const &f) noexcept
Definition AMReX_GpuLaunchFunctsC.H:122
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
@ 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
void LoopConcurrentOnCpu(Dim3 lo, Dim3 hi, F const &f) noexcept
Definition AMReX_Loop.H:388
void Abort(const std::string &msg)
Print a fatal-error message to stderr and abort execution.
Definition AMReX.cpp:244
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:2259
__host__ __device__ constexpr int get(IntVectND< dim > const &iv) noexcept
Get the Idx-th element of IntVectND<dim>.
Definition AMReX_IntVect.H:1360
Definition AMReX_TagParallelFor.H:58
Definition AMReX_TagParallelFor.H:26
Definition AMReX_TagParallelFor.H:50
Array4< T > dfab
Definition AMReX_TagParallelFor.H:51
Definition AMReX_TagParallelFor.H:106
Definition AMReX_TagParallelFor.H:116
A simple struct holding 3 int values for a 3D index.
Definition AMReX_Dim3.H:24
Definition AMReX_FabArrayBase.H:555
bool m_threadsafe_loc
Definition AMReX_FabArrayBase.H:557
bool m_threadsafe_rcv
Definition AMReX_FabArrayBase.H:558
std::unique_ptr< MapOfCopyComTagContainers > m_RcvTags
Definition AMReX_FabArrayBase.H:561
std::unique_ptr< CopyComTagsContainer > m_LocTags
Definition AMReX_FabArrayBase.H:559
Used by a bunch of routines when communicating via MPI.
Definition AMReX_FabArrayBase.H:211
Box sbox
Definition AMReX_FabArrayBase.H:213
int srcIndex
Definition AMReX_FabArrayBase.H:215
Box dbox
Definition AMReX_FabArrayBase.H:212
int dstIndex
Definition AMReX_FabArrayBase.H:214
FillBoundary.
Definition AMReX_FabArrayBase.H:580
Describes a source FAB region participating in a copy.
Definition AMReX_FBI.H:18
IntVect offset
Definition AMReX_FBI.H:21
Box dbox
Definition AMReX_FBI.H:20
FAB const * sfab
Definition AMReX_FBI.H:19
Definition AMReX_TypeTraits.H:51
Definition AMReX_TypeTraits.H:61
Definition AMReX_TypeTraits.H:277
Definition AMReX_MFIter.H:20
void MemoryBarrier() const
memory fence
Definition AMReX_ParallelDescriptor.H:161
Definition AMReX_TagParallelFor.H:156
Lightweight tag describing raw pointer copies (used for pack/unpack).
Definition AMReX_FBI.H:25
Box dbox
Definition AMReX_FBI.H:27
char const * p
Definition AMReX_FBI.H:26