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
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>
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>
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>
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>
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>
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();
259 IntVect tlo(AMREX_D_DECL(amrex::coarsen<tile_size>(dlo[0]),
260 amrex::coarsen<tile_size>(dlo[1]),
261 amrex::coarsen<tile_size>(dlo[2])));
262 IntVect thi(AMREX_D_DECL(amrex::coarsen<tile_size>(dhi[0]),
263 amrex::coarsen<tile_size>(dhi[1]),
264 amrex::coarsen<tile_size>(dhi[2])));
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
289 Gpu::HostVector<unsigned int> h_ntags;
290 Gpu::HostVector<TagType> h_tags;
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;
315 amrex::launch<nthreads>(nblocks, Gpu::gpuStream(),
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>
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>
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
411 BL_ASSERT(distributionMap[tag.dstIndex] == ParallelDescriptor::MyProc());
412 BL_ASSERT(distributionMap[tag.srcIndex] == ParallelDescriptor::MyProc());
413
414 const FAB* sfab = &(get(tag.srcIndex));
415 FAB* dfab = &(get(tag.dstIndex));
416 dfab->template copy<RunOn::Host>(*sfab, tag.sbox, scomp, tag.dbox, scomp, ncomp);
417 }
418 }
419 else
420 {
422 for (int i = 0; i < N_locs; ++i)
423 {
424 const CopyComTag& tag = LocTags[i];
425
426 BL_ASSERT(distributionMap[tag.dstIndex] == ParallelDescriptor::MyProc());
427 BL_ASSERT(distributionMap[tag.srcIndex] == ParallelDescriptor::MyProc());
428
429 loc_copy_tags[tag.dstIndex].push_back
430 ({this->fabPtr(tag.srcIndex), tag.dbox, tag.sbox.smallEnd()-tag.dbox.smallEnd()});
431 }
432#ifdef AMREX_USE_OMP
433#pragma omp parallel
434#endif
435 for (MFIter mfi(*this); mfi.isValid(); ++mfi)
436 {
437 const auto& tags = loc_copy_tags[mfi];
438 auto dfab = this->array(mfi);
439 for (auto const & tag : tags)
440 {
441 auto const sfab = tag.sfab->array();
442 const auto offset = tag.offset.dim3();
443 amrex::LoopConcurrentOnCpu(tag.dbox, ncomp,
444 [=] (int i, int j, int k, int n) noexcept
445 {
446 dfab(i,j,k,n+scomp) = sfab(i+offset.x,j+offset.y,k+offset.z,n+scomp);
447 });
448 }
449 }
450 }
451}
452
453template <class FAB>
454void
455FabArray<FAB>::FB_local_add_cpu (const FB& TheFB, int scomp, int ncomp)
456{
457 auto const& LocTags = *(TheFB.m_LocTags);
458 auto N_locs = static_cast<int>(LocTags.size());
459 if (N_locs == 0) { return; }
460
462 // We must make a temporary copy of the data first to avoid race condition.
463 std::vector<FAB> src_fabs(N_locs);
464 for (int itag = 0; itag < N_locs; ++itag) {
465 const CopyComTag& tag = LocTags[itag];
466 src_fabs[itag].resize(tag.sbox,ncomp);
467 loc_copy_tags[tag.dstIndex].push_back
468 (FabCopyTag<FAB>{&(src_fabs[itag]),
469 tag.dbox, tag.sbox.smallEnd()-tag.dbox.smallEnd()});
470 }
471
472#ifdef AMREX_USE_OMP
473#pragma omp parallel for
474#endif
475 for (int itag = 0; itag < N_locs; ++itag) {
476 const CopyComTag& tag = LocTags[itag];
477 src_fabs[itag].template copy<RunOn::Host>(this->operator[](tag.srcIndex), scomp, 0, ncomp);
478 }
479
480#ifdef AMREX_USE_OMP
481#pragma omp parallel
482#endif
483 for (MFIter mfi(*this); mfi.isValid(); ++mfi)
484 {
485 const auto& tags = loc_copy_tags[mfi];
486 const auto& dfab = this->array(mfi);
487 for (auto const & tag : tags)
488 {
489 auto const sfab = tag.sfab->array();
490 const auto offset = tag.offset.dim3();
491 amrex::LoopConcurrentOnCpu(tag.dbox, ncomp,
492 [&] (int i, int j, int k, int n) noexcept
493 {
494 dfab(i,j,k,n+scomp) += sfab(i+offset.x,j+offset.y,k+offset.z,n);
495 });
496 }
497 }
498}
499
500#ifdef AMREX_USE_GPU
501
502template <class FAB>
505{
506 auto const& LocTags = *(TheFB.m_LocTags);
507 int N_locs = LocTags.size();
508
509 using TagType = Array4CopyTag<value_type>;
510
512 if (auto it = m_fb_local_copy_handler.find(TheFB.m_id);
513 it != m_fb_local_copy_handler.end())
514 {
515 tv = it->second.get();
516 } else {
517 Vector<TagType> loc_copy_tags;
518 loc_copy_tags.reserve(N_locs);
519
520 for (int i = 0; i < N_locs; ++i)
521 {
522 const CopyComTag& tag = LocTags[i];
523
524 BL_ASSERT(distributionMap[tag.dstIndex] == ParallelDescriptor::MyProc());
525 BL_ASSERT(distributionMap[tag.srcIndex] == ParallelDescriptor::MyProc());
526
527 int li = this->localindex(tag.dstIndex);
528 loc_copy_tags.push_back
529 (TagType{.dfab = this->atLocalIdx(li).array(),
530 .dindex = tag.dstIndex,
531 .sfab = this->fabPtr(tag.srcIndex)->const_array(),
532 .dbox = tag.dbox,
533 .offset = (tag.sbox.smallEnd()-tag.dbox.smallEnd()).dim3()});
534 }
535
536 auto utv = std::make_unique<TagVector<TagType>>(loc_copy_tags);
537 tv = utv.get();
538 m_fb_local_copy_handler[TheFB.m_id] = std::move(utv);
539 }
540
541 return tv;
542}
543
544template <class FAB>
545void
546FabArray<FAB>::FB_local_copy_gpu (const FB& TheFB, int scomp, int ncomp)
547{
548 auto const& LocTags = *(TheFB.m_LocTags);
549 int N_locs = LocTags.size();
550 if (N_locs == 0) { return; }
551 bool is_thread_safe = TheFB.m_threadsafe_loc;
552
553 using TagType = Array4CopyTag<value_type>;
554
555 if (is_thread_safe || amrex::IsStoreAtomic<value_type>::value)
556 {
557 auto* tv = FB_get_local_copy_tag_vector(TheFB);
558
559 detail::ParallelFor_doit(*tv,
560 [=] AMREX_GPU_DEVICE (
561 int icell, int ncells, int i, int j, int k, TagType const& tag) noexcept
562 {
563 if (icell < ncells) {
564 for (int n = 0; n < ncomp; ++n) {
565 tag.dfab(i,j,k,n+scomp) = tag.sfab(i+tag.offset.x,
566 j+tag.offset.y,
567 k+tag.offset.z,n+scomp);
568 }
569 }
570 });
571 }
572 else
573 {
574 Vector<TagType> loc_copy_tags;
575 loc_copy_tags.reserve(N_locs);
576
577 Vector<BaseFab<int>> maskfabs(this->local_size());
578 Vector<Array4Tag<int> > masks_unique;
579 masks_unique.reserve(this->local_size());
580 Vector<Array4Tag<int> > masks;
581 masks.reserve(N_locs);
582
583 for (int i = 0; i < N_locs; ++i)
584 {
585 const CopyComTag& tag = LocTags[i];
586
587 BL_ASSERT(distributionMap[tag.dstIndex] == ParallelDescriptor::MyProc());
588 BL_ASSERT(distributionMap[tag.srcIndex] == ParallelDescriptor::MyProc());
589
590 int li = this->localindex(tag.dstIndex);
591 loc_copy_tags.push_back
592 (TagType{.dfab = this->atLocalIdx(li).array(),
593 .dindex = tag.dstIndex,
594 .sfab = this->fabPtr(tag.srcIndex)->const_array(),
595 .dbox = tag.dbox,
596 .offset = (tag.sbox.smallEnd()-tag.dbox.smallEnd()).dim3()});
597
598 if (!maskfabs[li].isAllocated()) {
599 maskfabs[li].resize(this->atLocalIdx(li).box());
600 masks_unique.emplace_back(Array4Tag<int>{.dfab = maskfabs[li].array()});
601 }
602 masks.emplace_back(Array4Tag<int>{.dfab = maskfabs[li].array()});
603 }
604
605 amrex::ParallelFor(masks_unique,
606 [=] AMREX_GPU_DEVICE (int i, int j, int k, Array4Tag<int> const& msk) noexcept
607 {
608 msk.dfab(i,j,k) = 0;
609 });
610
611 detail::fab_to_fab_atomic_cpy<value_type, value_type>(
612 loc_copy_tags, scomp, scomp, ncomp, masks);
613 }
614}
615
616template <class FAB>
617void
618FabArray<FAB>::FB_local_add_gpu (const FB& TheFB, int scomp, int ncomp, bool deterministic)
619{
620 auto const& LocTags = *(TheFB.m_LocTags);
621 int N_locs = LocTags.size();
622 if (N_locs == 0) { return; }
623
624 using TagType = Array4CopyTag<value_type>;
625
626 Vector<TagType> loc_copy_tags_1, loc_copy_tags_2;
627 loc_copy_tags_1.reserve(N_locs);
628 loc_copy_tags_2.reserve(N_locs);
629
630 Vector<FAB> src_fabs(N_locs);
631 for (int itag = 0; itag < N_locs; ++itag) {
632 const CopyComTag& tag = LocTags[itag];
633 src_fabs[itag].resize(tag.sbox,ncomp);
634 loc_copy_tags_1.push_back(
635 TagType{.dfab = src_fabs[itag].array(), .dindex = -1,
636 .sfab = this->const_array(tag.srcIndex,scomp), .dbox = tag.sbox,
637 .offset = Dim3{.x = 0, .y = 0, .z = 0}});
638 loc_copy_tags_2.push_back(
639 TagType{.dfab = this->array(tag.dstIndex,scomp), .dindex = tag.dstIndex,
640 .sfab = src_fabs[itag].const_array(), .dbox = tag.dbox,
641 .offset = (tag.sbox.smallEnd()-tag.dbox.smallEnd()).dim3()});
642 }
643
644 // Note that we have shifted the starting component to zero in the code above.
645
646 // TODO: We could try to cache the tags like in FillBoundary.
647
648 detail::fab_to_fab(loc_copy_tags_1, 0, 0, ncomp,
649 detail::CellStore<value_type, value_type>{});
650 if (deterministic || ! amrex::HasAtomicAdd<value_type>::value) {
651 detail::deterministic_fab_to_fab(loc_copy_tags_2, 0, 0, ncomp,
652 detail::CellAdd<value_type,value_type>{});
653 } else {
655 detail::fab_to_fab(loc_copy_tags_2, 0, 0, ncomp,
656 detail::CellAtomicAdd<value_type, value_type>{});
657 }
658 ((void)0);
659 }
660
661 // Note that fab_to_fab is synchronous.
662}
663
664template <class FAB>
665void
667 const CommMetaData& thecmd, int scomp, int ncomp)
668{
669 auto const& LocTags = *(thecmd.m_LocTags);
670 int N_locs = LocTags.size();
671 if (N_locs == 0) { return; }
672 bool is_thread_safe = thecmd.m_threadsafe_loc;
673
674 using TagType = Array4BoxTag<value_type>;
675 Vector<TagType> loc_setval_tags;
676 loc_setval_tags.reserve(N_locs);
677
679
680 for (int i = 0; i < N_locs; ++i)
681 {
682 const CopyComTag& tag = LocTags[i];
683 BL_ASSERT(distributionMap[tag.dstIndex] == ParallelDescriptor::MyProc());
684 loc_setval_tags.push_back(TagType{.dfab = this->array(tag.dstIndex), .dbox = tag.dbox});
685 }
686
687 amrex::ParallelFor(loc_setval_tags, ncomp,
688 [x,scomp] AMREX_GPU_DEVICE (int i, int j, int k, int n, TagType const& tag) noexcept
689 {
690 tag.dfab(i,j,k,n+scomp) = x;
691 });
692}
693
694template <class FAB>
695void
697 const CommMetaData& thecmd, int scomp, int ncomp)
698{
699 auto const& RcvTags = *(thecmd.m_RcvTags);
700 bool is_thread_safe = thecmd.m_threadsafe_rcv;
701
702 using TagType = Array4BoxTag<value_type>;
703 Vector<TagType> rcv_setval_tags;
704
705 for (auto it = RcvTags.begin(); it != RcvTags.end(); ++it) {
706 for (auto const& tag: it->second) {
707 rcv_setval_tags.push_back(TagType{.dfab = this->array(tag.dstIndex), .dbox = tag.dbox});
708 }
709 }
710
711 if (rcv_setval_tags.empty()) { return; }
712
714
715 amrex::ParallelFor(rcv_setval_tags, ncomp,
716 [x,scomp] AMREX_GPU_DEVICE (int i, int j, int k, int n, TagType const& tag) noexcept
717 {
718 tag.dfab(i,j,k,n+scomp) = x;
719 });
720}
721
722#if defined(__CUDACC__) && defined (AMREX_USE_CUDA)
723template <class FAB>
724void
725FabArray<FAB>::FB_local_copy_cuda_graph_1 (const FB& TheFB, int scomp, int ncomp)
726{
727 const int N_locs = (*TheFB.m_LocTags).size();
729 for (int i = 0; i < N_locs; ++i)
730 {
731 const CopyComTag& tag = (*TheFB.m_LocTags)[i];
732
733 BL_ASSERT(distributionMap[tag.dstIndex] == ParallelDescriptor::MyProc());
734 BL_ASSERT(distributionMap[tag.srcIndex] == ParallelDescriptor::MyProc());
735
736 loc_copy_tags[tag.dstIndex].push_back
737 ({this->fabPtr(tag.srcIndex), tag.dbox, tag.sbox.smallEnd()-tag.dbox.smallEnd()});
738 }
739
740 // Create Graph if one is needed.
741 if ( !(TheFB.m_localCopy.ready()) )
742 {
743 const_cast<FB&>(TheFB).m_localCopy.resize(N_locs);
744
745 int idx = 0;
746 // Record the graph.
747 for (MFIter mfi(*this, MFItInfo().DisableDeviceSync()); mfi.isValid(); ++mfi)
748 {
749 amrex::Gpu::Device::startGraphRecording( (mfi.LocalIndex() == 0),
750 const_cast<FB&>(TheFB).m_localCopy.getHostPtr(0),
751 (TheFB).m_localCopy.getDevicePtr(0),
752 std::size_t(sizeof(CopyMemory)*N_locs) );
753
754 const auto& tags = loc_copy_tags[mfi];
755 for (auto const & tag : tags)
756 {
757 const auto offset = tag.offset.dim3();
758 CopyMemory* cmem = TheFB.m_localCopy.getDevicePtr(idx++);
759 AMREX_HOST_DEVICE_FOR_3D (tag.dbox, i, j, k,
760 {
761 // Build the Array4's.
762 auto const dst = cmem->getDst<value_type>();
763 auto const src = cmem->getSrc<value_type>();
764 for (int n = 0; n < cmem->ncomp; ++n) {
765 dst(i,j,k,(cmem->scomp)+n) = src(i+offset.x,j+offset.y,k+offset.z,(cmem->scomp)+n);
766 }
767 });
768 }
769
770 bool last_iter = mfi.LocalIndex() == (this->local_size()-1);
771 cudaGraphExec_t graphExec = amrex::Gpu::Device::stopGraphRecording(last_iter);
772 if (last_iter) { const_cast<FB&>(TheFB).m_localCopy.setGraph( graphExec ); }
773 }
774 }
775
776 // Setup Launch Parameters
777 // This is perfectly threadable, right?
778 // Additional optimization -> Check to see whether values need to be reset?
779 // Can then remove this setup and memcpy from CudaGraph::executeGraph.
780 int idx = 0;
781 for (MFIter mfi(*this); mfi.isValid(); ++mfi)
782 {
783 auto const dst_array = this->array(mfi);
784 const auto& tags = loc_copy_tags[mfi];
785 for (auto const & tag : tags)
786 {
787 const_cast<FB&>(TheFB).m_localCopy.setParams(idx++, makeCopyMemory(tag.sfab->array(),
788 dst_array,
789 scomp, ncomp));
790 }
791 }
792
793 // Launch Graph
794 TheFB.m_localCopy.executeGraph();
795}
796
797#ifdef AMREX_USE_MPI
798template <class FAB>
799void
800FabArray<FAB>::FB_local_copy_cuda_graph_n (const FB& TheFB, int scomp, int ncomp)
801{
802 const int N_locs = TheFB.m_LocTags->size();
803
804 int launches = 0; // Used for graphs only.
805 LayoutData<Vector<FabCopyTag<FAB> > > loc_copy_tags(boxArray(),DistributionMap());
806 for (int i = 0; i < N_locs; ++i)
807 {
808 const CopyComTag& tag = (*TheFB.m_LocTags)[i];
809
810 BL_ASSERT(ParallelDescriptor::sameTeam(distributionMap[tag.dstIndex]));
811 BL_ASSERT(ParallelDescriptor::sameTeam(distributionMap[tag.srcIndex]));
812
813 if (distributionMap[tag.dstIndex] == ParallelDescriptor::MyProc())
814 {
815 loc_copy_tags[tag.dstIndex].push_back
816 ({this->fabPtr(tag.srcIndex), tag.dbox, tag.sbox.smallEnd()-tag.dbox.smallEnd()});
817 launches++;
818 }
819 }
820
821 FillBoundary_test();
822
823 if ( !(TheFB.m_localCopy.ready()) )
824 {
825 const_cast<FB&>(TheFB).m_localCopy.resize(launches);
826
827 int idx = 0;
828 int cuda_stream = 0;
829 for (MFIter mfi(*this, MFItInfo().DisableDeviceSync()); mfi.isValid(); ++mfi)
830 {
831 const auto& tags = loc_copy_tags[mfi];
832 for (int t = 0; t<tags.size(); ++t)
833 {
834 Gpu::Device::setStreamIndex(cuda_stream++);
835 amrex::Gpu::Device::startGraphRecording( (idx == 0),
836 const_cast<FB&>(TheFB).m_localCopy.getHostPtr(0),
837 (TheFB).m_localCopy.getDevicePtr(0),
838 std::size_t(sizeof(CopyMemory)*launches) );
839
840 const auto& tag = tags[t];
841 const Dim3 offset = tag.offset.dim3();
842
843 CopyMemory* cmem = TheFB.m_localCopy.getDevicePtr(idx++);
844 AMREX_HOST_DEVICE_FOR_3D(tag.dbox, i, j, k,
845 {
846 auto const dst = cmem->getDst<value_type>();
847 auto const src = cmem->getSrc<value_type>();
848 for (int n = 0; n < cmem->ncomp; ++n) {
849 dst(i,j,k,(cmem->scomp)+n) = src(i+offset.x,j+offset.y,k+offset.z,(cmem->scomp)+n);
850 }
851 });
852
853 bool last_iter = idx == launches;
854 cudaGraphExec_t graphExec = Gpu::Device::stopGraphRecording(last_iter);
855 if (last_iter) { const_cast<FB&>(TheFB).m_localCopy.setGraph( graphExec ); }
856 }
857 }
858 }
859
860 // Setup Launch Parameters
861 // This is perfectly threadable, right?
862 int idx = 0;
863 for (MFIter mfi(*this); mfi.isValid(); ++mfi)
864 {
865 const auto& dst_array = this->array(mfi);
866 const auto& tags = loc_copy_tags[mfi];
867 for (auto const & tag : tags)
868 {
869 const_cast<FB&>(TheFB).m_localCopy.setParams(idx++, makeCopyMemory(tag.sfab->array(),
870 dst_array,
871 scomp, ncomp));
872 }
873 }
874
875 // Launch Graph without synch. Local work is entirely independent.
876 TheFB.m_localCopy.executeGraph(false);
877}
878#endif /* AMREX_USE_MPI */
879
880#endif /* __CUDACC__ */
881
882#endif /* AMREX_USE_GPU */
883
884#ifdef AMREX_USE_MPI
885
886#ifdef AMREX_USE_GPU
887
888#if defined(__CUDACC__) && defined(AMREX_USE_CUDA)
889
890template <class FAB>
891void
892FabArray<FAB>::FB_pack_send_buffer_cuda_graph (const FB& TheFB, int scomp, int ncomp,
893 Vector<char*>& send_data,
894 Vector<std::size_t> const& send_size,
895 Vector<typename FabArray<FAB>::CopyComTagsContainer const*> const& send_cctc)
896{
897 const int N_snds = send_data.size();
898 if (N_snds == 0) { return; }
899
900 if ( !(TheFB.m_copyToBuffer.ready()) )
901 {
902 // Set size of CudaGraph buffer.
903 // Is the conditional ever expected false?
904 int launches = 0;
905 for (int send = 0; send < N_snds; ++send) {
906 if (send_size[send] > 0) {
907 launches += send_cctc[send]->size();
908 }
909 }
910 const_cast<FB&>(TheFB).m_copyToBuffer.resize(launches);
911
912 // Record the graph.
913 int idx = 0;
914 for (Gpu::StreamIter sit(N_snds,Gpu::StreamItInfo().DisableDeviceSync());
915 sit.isValid(); ++sit)
916 {
917 amrex::Gpu::Device::startGraphRecording( (sit() == 0),
918 const_cast<FB&>(TheFB).m_copyToBuffer.getHostPtr(0),
919 (TheFB).m_copyToBuffer.getDevicePtr(0),
920 std::size_t(sizeof(CopyMemory)*launches) );
921
922 const int j = sit();
923 if (send_size[j] > 0)
924 {
925 auto const& cctc = *send_cctc[j];
926 for (auto const& tag : cctc)
927 {
928 const Box& bx = tag.sbox;
929 CopyMemory* cmem = TheFB.m_copyToBuffer.getDevicePtr(idx++);
930 AMREX_HOST_DEVICE_FOR_3D (bx, ii, jj, kk,
931 {
932 auto const pfab = cmem->getDst<value_type>();
933 auto const sfab = cmem->getSrc<value_type>();
934 for (int n = 0; n < cmem->ncomp; ++n)
935 {
936 pfab(ii,jj,kk,n) = sfab(ii,jj,kk,n+(cmem->scomp));
937 }
938 });
939 }
940 }
941
942 bool last_iter = sit() == (N_snds-1);
943 cudaGraphExec_t graphExec = amrex::Gpu::Device::stopGraphRecording(last_iter);
944 if (last_iter) { const_cast<FB&>(TheFB).m_copyToBuffer.setGraph( graphExec ); }
945 }
946 }
947
948 // Setup Launch Parameters
949 int idx = 0;
950 for (int send = 0; send < N_snds; ++send)
951 {
952 const int j = send;
953 if (send_size[j] > 0)
954 {
955 char* dptr = send_data[j];
956 auto const& cctc = *send_cctc[j];
957 for (auto const& tag : cctc)
958 {
959 const_cast<FB&>(TheFB).m_copyToBuffer.setParams(idx++, makeCopyMemory(this->array(tag.srcIndex),
960 amrex::makeArray4((value_type*)(dptr),
961 tag.sbox,
962 ncomp),
963 scomp, ncomp));
964
965 dptr += (tag.sbox.numPts() * ncomp * sizeof(value_type));
966 }
967 amrex::ignore_unused(send_size);
968 BL_ASSERT(dptr <= send_data[j] + send_size[j]);
969 }
970 }
971
972 // Launch Graph synched, so copyToBuffer is complete prior to posting sends.
973 TheFB.m_copyToBuffer.executeGraph();
974}
975
976template <class FAB>
977void
978FabArray<FAB>::FB_unpack_recv_buffer_cuda_graph (const FB& TheFB, int dcomp, int ncomp,
979 Vector<char*> const& recv_data,
980 Vector<std::size_t> const& recv_size,
981 Vector<CopyComTagsContainer const*> const& recv_cctc,
982 bool /*is_thread_safe*/)
983{
984 const int N_rcvs = recv_cctc.size();
985 if (N_rcvs == 0) { return; }
986
987 int launches = 0;
988 LayoutData<Vector<VoidCopyTag> > recv_copy_tags(boxArray(),DistributionMap());
989 for (int k = 0; k < N_rcvs; ++k)
990 {
991 if (recv_size[k] > 0)
992 {
993 const char* dptr = recv_data[k];
994 auto const& cctc = *recv_cctc[k];
995 for (auto const& tag : cctc)
996 {
997 recv_copy_tags[tag.dstIndex].push_back(VoidCopyTag{.p = dptr, .dbox = tag.dbox});
998 dptr += tag.dbox.numPts() * ncomp * sizeof(value_type);
999 launches++;
1000 }
1001 amrex::ignore_unused(recv_size);
1002 BL_ASSERT(dptr <= recv_data[k] + recv_size[k]);
1003 }
1004 }
1005
1006 if ( !(TheFB.m_copyFromBuffer.ready()) )
1007 {
1008 const_cast<FB&>(TheFB).m_copyFromBuffer.resize(launches);
1009
1010 int idx = 0;
1011 for (MFIter mfi(*this, MFItInfo().DisableDeviceSync()); mfi.isValid(); ++mfi)
1012 {
1013 amrex::Gpu::Device::startGraphRecording( (mfi.LocalIndex() == 0),
1014 const_cast<FB&>(TheFB).m_copyFromBuffer.getHostPtr(0),
1015 (TheFB).m_copyFromBuffer.getDevicePtr(0),
1016 std::size_t(sizeof(CopyMemory)*launches) );
1017
1018 const auto& tags = recv_copy_tags[mfi];
1019 for (auto const & tag : tags)
1020 {
1021 CopyMemory* cmem = TheFB.m_copyFromBuffer.getDevicePtr(idx++);
1022 AMREX_HOST_DEVICE_FOR_3D (tag.dbox, i, j, k,
1023 {
1024 auto const pfab = cmem->getSrc<value_type>();
1025 auto const dfab = cmem->getDst<value_type>();
1026 for (int n = 0; n < cmem->ncomp; ++n)
1027 {
1028 dfab(i,j,k,n+(cmem->scomp)) = pfab(i,j,k,n);
1029 }
1030 });
1031 }
1032
1033 bool last_iter = mfi.LocalIndex() == (this->local_size()-1);
1034 cudaGraphExec_t graphExec = amrex::Gpu::Device::stopGraphRecording(last_iter);
1035 if (last_iter) { const_cast<FB&>(TheFB).m_copyFromBuffer.setGraph( graphExec ); }
1036 }
1037 }
1038
1039 // Setup graph.
1040 int idx = 0;
1041 for (MFIter mfi(*this); mfi.isValid(); ++mfi)
1042 {
1043 auto dst_array = this->array(mfi);
1044 const auto & tags = recv_copy_tags[mfi];
1045 for (auto const & tag : tags)
1046 {
1047 const_cast<FB&>(TheFB).m_copyFromBuffer.setParams(idx++, makeCopyMemory(amrex::makeArray4((value_type*)(tag.p),
1048 tag.dbox,
1049 ncomp),
1050 dst_array,
1051 dcomp, ncomp));
1052 }
1053 }
1054
1055 // Launch Graph - synced because next action is freeing recv buffer.
1056 TheFB.m_copyFromBuffer.executeGraph();
1057}
1058
1059#endif /* __CUDACC__ */
1060
1061template <class FAB>
1062template <typename BUF>
1063auto
1065 Vector<std::size_t> const& send_size,
1066 Vector<CopyComTagsContainer const*> const& send_cctc,
1067 int ncomp, std::uint64_t id) const
1069{
1070 using TagType = CommSendBufTag<value_type>;
1071
1072 auto kit = std::find_if(send_cctc.begin(), send_cctc.end(),
1073 [] (CopyComTagsContainer const* p) { return p != nullptr; });
1074 if (kit == send_cctc.end()) {
1075 return nullptr;
1076 }
1077
1078 auto get_tags = [&] () -> Vector<TagType>
1079 {
1080 Vector<TagType> snd_copy_tags;
1081 char* pbuf = send_data[0];
1082 const int N_snds = send_data.size();
1083 for (int j = 0; j < N_snds; ++j)
1084 {
1085 if (send_size[j] > 0)
1086 {
1087 char* dptr = send_data[j];
1088 auto const& cctc = *send_cctc[j];
1089 for (auto const& tag : cctc)
1090 {
1091 snd_copy_tags.emplace_back
1092 (TagType{.sfab = this->const_array(tag.srcIndex), .poff = dptr-pbuf, .bx = tag.sbox});
1093 dptr += (tag.sbox.numPts() * ncomp * sizeof(BUF));
1094 }
1095 }
1096 }
1097 return snd_copy_tags;
1098 };
1099
1101 std::tuple<std::uint64_t,std::size_t,int> key{id, sizeof(BUF), ncomp};
1102
1103 if (auto it = m_send_copy_handler.find(key); it != m_send_copy_handler.end()) {
1104 tv = it->second.get();
1105 } else {
1106 if (m_send_copy_handler.size() > 32) {
1107 // Just in case. If this is used in ParallelCopy, it's possible
1108 // that the sending FabArray is the same, but the receiving
1109 // FabArray is different every time. Then the size of this map
1110 // could increase indefinitely.
1111 m_send_copy_handler.clear();
1112 }
1113 auto snd_copy_tags = get_tags();
1114 auto utv = std::make_unique<TagVector<TagType>>(snd_copy_tags);
1115 tv = utv.get();
1116 m_send_copy_handler[key] = std::move(utv);
1117 }
1118
1119 return tv;
1120}
1121
1122template <class FAB>
1123template <typename BUF>
1124void
1125FabArray<FAB>::pack_send_buffer_gpu (FabArray<FAB> const& src, int scomp, int ncomp,
1126 Vector<char*> const& send_data,
1127 Vector<std::size_t> const& send_size,
1128 Vector<CopyComTagsContainer const*> const& send_cctc,
1129 std::uint64_t id)
1130{
1131 const int N_snds = send_data.size();
1132 if (N_snds == 0) { return; }
1133
1134 using TagType = CommSendBufTag<value_type>;
1135
1136 auto* tv = src.template get_send_copy_tag_vector<BUF>
1137 (send_data, send_size, send_cctc, ncomp, id);
1138 if (tv == nullptr) { return; }
1139
1140 char* pbuffer = send_data[0];
1141
1142 detail::ParallelFor_doit(*tv,
1143 [=] AMREX_GPU_DEVICE (
1144 int icell, int ncells, int i, int j, int k, TagType const& tag) noexcept
1145 {
1146 if (icell < ncells) {
1147 Array4<BUF> dfab{(BUF*)(pbuffer+tag.poff),
1148 amrex::begin(tag.bx), amrex::end(tag.bx), ncomp};
1149 for (int n = 0; n < ncomp; ++n) {
1150 dfab(i,j,k,n) = (BUF)tag.sfab(i,j,k,n+scomp);
1151 }
1152 }
1153 });
1154
1155 Gpu::streamSynchronize();
1156}
1157
1158template <class FAB>
1159template <typename BUF>
1160auto
1162 Vector<std::size_t> const& recv_size,
1163 Vector<CopyComTagsContainer const*> const& recv_cctc,
1164 int ncomp, std::uint64_t id)
1166{
1167 using TagType = CommRecvBufTag<value_type>;
1168
1169 auto kit = std::find_if(recv_cctc.begin(), recv_cctc.end(),
1170 [] (CopyComTagsContainer const* p) { return p != nullptr; });
1171 if (kit == recv_cctc.end()) {
1172 return nullptr;
1173 }
1174
1175 auto get_tags = [&] () -> Vector<TagType>
1176 {
1177 Vector<TagType> recv_copy_tags;
1178 char* pbuf = recv_data[0];
1179 const int N_rcvs = recv_cctc.size();
1180 for (int k = 0; k < N_rcvs; ++k)
1181 {
1182 if (recv_size[k] > 0)
1183 {
1184 char* dptr = recv_data[k];
1185 auto const& cctc = *recv_cctc[k];
1186 for (auto const& tag : cctc)
1187 {
1188 const int li = this->localindex(tag.dstIndex);
1189 recv_copy_tags.emplace_back
1190 (TagType{.dfab = this->atLocalIdx(li).array(), .poff = dptr-pbuf, .bx = tag.dbox});
1191 dptr += tag.dbox.numPts() * ncomp * sizeof(BUF);
1192 }
1193 }
1194 }
1195 return recv_copy_tags;
1196 };
1197
1199 std::tuple<std::uint64_t,std::size_t,int> key{id, sizeof(BUF), ncomp};
1200
1201 if (auto it = m_recv_copy_handler.find(key); it != m_recv_copy_handler.end()) {
1202 tv = it->second.get();
1203 } else {
1204 if (m_recv_copy_handler.size() > 32) {
1205 // Just in case. If this is used in ParallelCopy, it's possible
1206 // that the receiving FabArray is the same, but the sending
1207 // FabArray is different every time. Then the size of this map
1208 // could increase indefinitely.
1209 m_recv_copy_handler.clear();
1210 }
1211 auto recv_copy_tags = get_tags();
1212 auto utv = std::make_unique<TagVector<TagType>>(recv_copy_tags);
1213 tv = utv.get();
1214 m_recv_copy_handler[key] = std::move(utv);
1215 }
1216
1217 return tv;
1218}
1219
1220template <class FAB>
1221template <typename BUF>
1222void
1224 Vector<char*> const& recv_data,
1225 Vector<std::size_t> const& recv_size,
1226 Vector<CopyComTagsContainer const*> const& recv_cctc,
1227 CpOp op, bool is_thread_safe, std::uint64_t id,
1228 bool deterministic)
1229{
1230 const int N_rcvs = recv_cctc.size();
1231 if (N_rcvs == 0) { return; }
1232
1233 bool use_mask = false;
1234 if (!is_thread_safe)
1235 {
1236 if ((op == FabArrayBase::COPY && !amrex::IsStoreAtomic<value_type>::value) ||
1237 (op == FabArrayBase::ADD && !amrex::HasAtomicAdd <value_type>::value))
1238 {
1239 use_mask = true;
1240 }
1241 }
1242
1243 if (deterministic)
1244 {
1245 AMREX_ALWAYS_ASSERT(op == FabArrayBase::ADD); // Only ADD for now
1246 using TagType = Array4CopyTag<value_type,BUF>;
1247 Vector<TagType> tags;
1248 tags.reserve(N_rcvs);
1249 for (int k = 0; k < N_rcvs; ++k) {
1250 if (recv_size[k] > 0) {
1251 char const* dptr = recv_data[k];
1252 auto const& cctc = *recv_cctc[k];
1253 for (auto const& tag : cctc) {
1254 tags.emplace_back(
1255 TagType{.dfab = dst.array(tag.dstIndex), .dindex = tag.dstIndex,
1256 .sfab = Array4<BUF const>((BUF const*)dptr,
1257 amrex::begin(tag.dbox),
1258 amrex::end(tag.dbox), ncomp),
1259 .dbox = tag.dbox,
1260 .offset = Dim3{.x = 0, .y = 0, .z = 0}});
1261 dptr += tag.dbox.numPts() * ncomp * sizeof(BUF);
1262 }
1263 }
1264 }
1266 detail::deterministic_fab_to_fab<value_type,BUF>
1267 (tags, 0, dcomp, ncomp, detail::CellAdd<value_type,BUF>{});
1268 } else {
1269 amrex::Abort("SumBoundary requires operator+=");
1270 }
1271 }
1272 else if (!use_mask)
1273 {
1274 using TagType = CommRecvBufTag<value_type>;
1275 auto* tv = dst.template get_recv_copy_tag_vector<BUF>
1276 (recv_data, recv_size, recv_cctc, ncomp, id);
1277 if (tv == nullptr) { return; }
1278
1279 char* pbuffer = recv_data[0];
1280
1281 if (op == FabArrayBase::COPY)
1282 {
1283 detail::ParallelFor_doit(*tv,
1284 [=] AMREX_GPU_DEVICE (
1285 int icell, int ncells, int i, int j, int k, TagType const& tag) noexcept
1286 {
1287 if (icell < ncells) {
1288 Array4<BUF const> sfab{(BUF const*)(pbuffer+tag.poff),
1289 amrex::begin(tag.bx), amrex::end(tag.bx), ncomp};
1290 for (int n = 0; n < ncomp; ++n) {
1291 tag.dfab(i,j,k,n+dcomp) = (value_type)sfab(i,j,k,n);
1292 }
1293 }
1294 });
1295 }
1296 else
1297 {
1298 if (is_thread_safe) {
1299 detail::ParallelFor_doit(*tv,
1300 [=] AMREX_GPU_DEVICE (
1301 int icell, int ncells, int i, int j, int k, TagType const& tag) noexcept
1302 {
1303 if (icell < ncells) {
1304 Array4<BUF const> sfab{(BUF const*)(pbuffer+tag.poff),
1305 amrex::begin(tag.bx), amrex::end(tag.bx), ncomp};
1306 for (int n = 0; n < ncomp; ++n) {
1307 tag.dfab(i,j,k,n+dcomp) += (value_type)sfab(i,j,k,n);
1308 }
1309 }
1310 });
1311 } else {
1312 detail::unpack_recv_buffer_gpu_atomic_add<BUF, value_type>
1313 (pbuffer, *tv, dcomp, ncomp);
1314 }
1315 }
1316 Gpu::streamSynchronize();
1317 }
1318 else
1319 {
1320 char* pbuffer = recv_data[0];
1321
1322 using TagType = Array4CopyTag<value_type, BUF>;
1323 Vector<TagType> recv_copy_tags;
1324 recv_copy_tags.reserve(N_rcvs);
1325
1326 Vector<BaseFab<int> > maskfabs(dst.local_size());
1327 Vector<Array4Tag<int> > masks_unique;
1328 masks_unique.reserve(dst.local_size());
1329 Vector<Array4Tag<int> > masks;
1330
1331 for (int k = 0; k < N_rcvs; ++k)
1332 {
1333 if (recv_size[k] > 0)
1334 {
1335 std::size_t offset = recv_data[k]-recv_data[0];
1336 const char* dptr = pbuffer + offset;
1337 auto const& cctc = *recv_cctc[k];
1338 for (auto const& tag : cctc)
1339 {
1340 const int li = dst.localindex(tag.dstIndex);
1341 recv_copy_tags.emplace_back(TagType{
1342 .dfab = dst.atLocalIdx(li).array(), .dindex = tag.dstIndex,
1343 .sfab = amrex::makeArray4((BUF const*)(dptr), tag.dbox, ncomp),
1344 .dbox = tag.dbox,
1345 .offset = Dim3{.x = 0, .y = 0, .z = 0}
1346 });
1347 dptr += tag.dbox.numPts() * ncomp * sizeof(BUF);
1348
1349 if (!maskfabs[li].isAllocated()) {
1350 maskfabs[li].resize(dst.atLocalIdx(li).box());
1351 masks_unique.emplace_back(Array4Tag<int>{.dfab = maskfabs[li].array()});
1352 }
1353 masks.emplace_back(Array4Tag<int>{.dfab = maskfabs[li].array()});
1354 }
1355 BL_ASSERT(dptr <= pbuffer + offset + recv_size[k]);
1356 }
1357 }
1358
1359 amrex::ParallelFor(masks_unique,
1360 [=] AMREX_GPU_DEVICE (int i, int j, int k, Array4Tag<int> const& msk) noexcept
1361 {
1362 msk.dfab(i,j,k) = 0;
1363 });
1364
1365 if (op == FabArrayBase::COPY)
1366 {
1367 detail::fab_to_fab_atomic_cpy<value_type, BUF>(
1368 recv_copy_tags, 0, dcomp, ncomp, masks);
1369 }
1370 else
1371 {
1372 detail::fab_to_fab_atomic_add<value_type, BUF>(
1373 recv_copy_tags, 0, dcomp, ncomp, masks);
1374 }
1375
1376 // There is Gpu::streamSynchronize in fab_to_fab.
1377 }
1378}
1379
1380#endif /* AMREX_USE_GPU */
1381
1382template <class FAB>
1383template <typename BUF>
1384void
1385FabArray<FAB>::pack_send_buffer_cpu (FabArray<FAB> const& src, int scomp, int ncomp,
1386 Vector<char*> const& send_data,
1387 Vector<std::size_t> const& send_size,
1388 Vector<CopyComTagsContainer const*> const& send_cctc)
1389{
1392 auto const N_snds = static_cast<int>(send_data.size());
1393 if (N_snds == 0) { return; }
1394
1395#ifdef AMREX_USE_OMP
1396#pragma omp parallel for
1397#endif
1398 for (int j = 0; j < N_snds; ++j)
1399 {
1400 if (send_size[j] > 0)
1401 {
1402 char* dptr = send_data[j];
1403 auto const& cctc = *send_cctc[j];
1404 for (auto const& tag : cctc)
1405 {
1406 const Box& bx = tag.sbox;
1407 auto const sfab = src.array(tag.srcIndex);
1408 auto pfab = amrex::makeArray4((BUF*)(dptr),bx,ncomp);
1409 amrex::LoopConcurrentOnCpu( bx, ncomp,
1410 [=] (int ii, int jj, int kk, int n) noexcept
1411 {
1412 pfab(ii,jj,kk,n) = static_cast<BUF>(sfab(ii,jj,kk,n+scomp));
1413 });
1414 dptr += (bx.numPts() * ncomp * sizeof(BUF));
1415 }
1416 BL_ASSERT(dptr <= send_data[j] + send_size[j]);
1417 }
1418 }
1419}
1421template <class FAB>
1422template <typename BUF>
1423void
1425 Vector<char*> const& recv_data,
1426 Vector<std::size_t> const& recv_size,
1428 CpOp op, bool is_thread_safe)
1429{
1430 amrex::ignore_unused(recv_size);
1431
1432 auto const N_rcvs = static_cast<int>(recv_cctc.size());
1433 if (N_rcvs == 0) { return; }
1434
1435 if (is_thread_safe)
1436 {
1437#ifdef AMREX_USE_OMP
1438#pragma omp parallel for
1439#endif
1440 for (int k = 0; k < N_rcvs; ++k)
1441 {
1442 if (recv_size[k] > 0)
1443 {
1444 const char* dptr = recv_data[k];
1445 auto const& cctc = *recv_cctc[k];
1446 for (auto const& tag : cctc)
1447 {
1448 const Box& bx = tag.dbox;
1449 FAB& dfab = dst[tag.dstIndex];
1450 if (op == FabArrayBase::COPY)
1452 dfab.template copyFromMem<RunOn::Host, BUF>(bx, dcomp, ncomp, dptr);
1453 }
1454 else
1455 {
1456 dfab.template addFromMem<RunOn::Host, BUF>(tag.dbox, dcomp, ncomp, dptr);
1458 dptr += bx.numPts() * ncomp * sizeof(BUF);
1459 }
1460 BL_ASSERT(dptr <= recv_data[k] + recv_size[k]);
1461 }
1462 }
1463 }
1464 else
1465 {
1466 LayoutData<Vector<VoidCopyTag> > recv_copy_tags;
1467 recv_copy_tags.define(dst.boxArray(),dst.DistributionMap());
1468 for (int k = 0; k < N_rcvs; ++k)
1469 {
1470 if (recv_size[k] > 0)
1471 {
1472 const char* dptr = recv_data[k];
1473 auto const& cctc = *recv_cctc[k];
1474 for (auto const& tag : cctc)
1475 {
1476 recv_copy_tags[tag.dstIndex].push_back(VoidCopyTag{.p = dptr, .dbox = tag.dbox});
1477 dptr += tag.dbox.numPts() * ncomp * sizeof(BUF);
1478 }
1479 BL_ASSERT(dptr <= recv_data[k] + recv_size[k]);
1480 }
1481 }
1482
1483#ifdef AMREX_USE_OMP
1484#pragma omp parallel
1485#endif
1486 for (MFIter mfi(dst); mfi.isValid(); ++mfi)
1487 {
1488 const auto& tags = recv_copy_tags[mfi];
1489 auto dfab = dst.array(mfi);
1490 for (auto const & tag : tags)
1491 {
1492 auto pfab = amrex::makeArray4((BUF*)(tag.p), tag.dbox, ncomp);
1493 if (op == FabArrayBase::COPY)
1494 {
1495 amrex::LoopConcurrentOnCpu(tag.dbox, ncomp,
1496 [=] (int i, int j, int k, int n) noexcept
1497 {
1498 dfab(i,j,k,n+dcomp) = pfab(i,j,k,n);
1499 });
1500 }
1501 else
1502 {
1503 amrex::LoopConcurrentOnCpu(tag.dbox, ncomp,
1504 [=] (int i, int j, int k, int n) noexcept
1505 {
1506 dfab(i,j,k,n+dcomp) += pfab(i,j,k,n);
1507 });
1508 }
1509 }
1510 }
1511 }
1512}
1513
1514#endif /* AMREX_USE_MPI */
1515
1516}
1517
1518#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
Array4< int const > offset
Definition AMReX_HypreMLABecLap.cpp:1129
#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
CopyComTag::CopyComTagsContainer CopyComTagsContainer
Definition AMReX_FabArrayBase.H:219
int localindex(int K) const noexcept
Return local index in the vector of FABs.
Definition AMReX_FabArrayBase.H:119
const DistributionMapping & DistributionMap() const noexcept
Return constant reference to associated DistributionMapping.
Definition AMReX_FabArrayBase.H:130
int local_size() const noexcept
Return the number of local FABs in the FabArray.
Definition AMReX_FabArrayBase.H:113
CpOp
parallel copy or add
Definition AMReX_FabArrayBase.H:393
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:95
An Array of FortranArrayBox(FAB)-like Objects.
Definition AMReX_FabArray.H:344
typename std::conditional_t< IsBaseFab< FAB >::value, FAB, FABType >::value_type value_type
Definition AMReX_FabArray.H:355
void CMD_remote_setVal_gpu(value_type x, const CommMetaData &thecmd, int scomp, int ncomp)
Definition AMReX_FBI.H:696
void FB_local_add_cpu(const FB &TheFB, int scomp, int ncomp)
Definition AMReX_FBI.H:455
void FB_local_add_gpu(const FB &TheFB, int scomp, int ncomp, bool deterministic)
Definition AMReX_FBI.H:618
void FB_local_copy_gpu(const FB &TheFB, int scomp, int ncomp)
Definition AMReX_FBI.H:546
void CMD_local_setVal_gpu(value_type x, const CommMetaData &thecmd, int scomp, int ncomp)
Definition AMReX_FBI.H:666
Array4< typename FabArray< FAB >::value_type const > array(const MFIter &mfi) const noexcept
Definition AMReX_FabArray.H:561
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:503
FAB & atLocalIdx(int L) noexcept
Return a reference to the FAB associated with local index L.
Definition AMReX_FabArray.H:532
a one-thingy-per-box distributed object
Definition AMReX_LayoutData.H:13
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:172
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:1378
__host__ __device__ Dim3 lbound(Array4< T > const &a) noexcept
Return the inclusive lower bounds of an Array4 in Dim3 form.
Definition AMReX_Array4.H:1350
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:310
gpuStream_t gpuStream() noexcept
Definition AMReX_GpuDevice.H:291
Definition AMReX_Amr.cpp:50
__host__ __device__ void ignore_unused(const Ts &...)
No-op helper that marks variables as intentionally unused.
Definition AMReX.H:259
__host__ __device__ Array4< T > makeArray4(T *p, Box const &bx, int ncomp) noexcept
Definition AMReX_BaseFab.H:123
DistributionMapping const & DistributionMap(FabArrayBase const &fa)
Definition AMReX_FabArrayBase.cpp:2867
__host__ __device__ Dim3 begin(BoxND< dim > const &box) noexcept
Return the iterator begin coordinate of box as Dim3.
Definition AMReX_Box.H:2239
void ParallelFor(TypeList< CTOs... > ctos, std::array< int, sizeof...(CTOs)> const &runtime_options, T N, F &&f)
Definition AMReX_CTOParallelForImpl.H:202
BoxND< 3 > Box
Box is an alias for amrex::BoxND instantiated with AMREX_SPACEDIM.
Definition AMReX_BaseFwd.H:35
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:241
const int[]
Definition AMReX_BLProfiler.cpp:1664
__host__ __device__ Dim3 end(BoxND< dim > const &box) noexcept
Return the iterator end coordinate of box as Dim3.
Definition AMReX_Box.H:2257
BoxArray const & boxArray(FabArrayBase const &fa)
Definition AMReX_FabArrayBase.cpp:2862
__host__ __device__ constexpr int get(IntVectND< dim > const &iv) noexcept
Get I'th element of IntVectND<dim>
Definition AMReX_IntVect.H:1334
Definition AMReX_TagParallelFor.H:58
Definition AMReX_TagParallelFor.H:26
Definition AMReX_TagParallelFor.H:50
Array4< T > dfab
Definition AMReX_TagParallelFor.H:51
A multidimensional array accessor.
Definition AMReX_Array4.H:288
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
int x
Definition AMReX_Dim3.H:24
Definition AMReX_FabArrayBase.H:471
bool m_threadsafe_loc
Definition AMReX_FabArrayBase.H:473
bool m_threadsafe_rcv
Definition AMReX_FabArrayBase.H:474
std::unique_ptr< MapOfCopyComTagContainers > m_RcvTags
Definition AMReX_FabArrayBase.H:477
std::unique_ptr< CopyComTagsContainer > m_LocTags
Definition AMReX_FabArrayBase.H:475
Used by a bunch of routines when communicating via MPI.
Definition AMReX_FabArrayBase.H:194
Box sbox
Definition AMReX_FabArrayBase.H:196
int srcIndex
Definition AMReX_FabArrayBase.H:198
Box dbox
Definition AMReX_FabArrayBase.H:195
int dstIndex
Definition AMReX_FabArrayBase.H:197
FillBoundary.
Definition AMReX_FabArrayBase.H:487
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_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