15#ifndef PECLET_CORE_HALO_GRID_HALO_HPP
16#define PECLET_CORE_HALO_GRID_HALO_HPP
24#if __has_include(<mpi-ext.h>)
39#if defined(MPIX_CUDA_AWARE_SUPPORT)
40#if MPIX_CUDA_AWARE_SUPPORT
41 return MPIX_Query_cuda_support() == 1;
58#ifdef KOKKOS_ENABLE_SERIAL
59 if (std::is_same_v<ExecSpace, Kokkos::Serial>)
62#ifdef KOKKOS_ENABLE_OPENMP
63 if (std::is_same_v<ExecSpace, Kokkos::OpenMP>)
71 if (MPI_Comm_dup(MPI_COMM_SELF, &self) !=
MPI_SUCCESS)
73 MPI_Comm_set_errhandler(self, MPI_ERRORS_RETURN);
75 View<int> src(Kokkos::view_alloc(
"peclet::core::halo::probeSrc", Kokkos::WithoutInitializing),
77 View<int> dst(
"peclet::core::halo::probeDst", N);
79 "peclet::core::halo::probeFill", Kokkos::RangePolicy<ExecSpace>(0, N),
80 KOKKOS_LAMBDA(
const int i) { src(i) = 0x5EC0DE + i * 7919; });
82 const int rc = MPI_Sendrecv(src.data(), N,
MPI_INT, 0, 77, dst.data(), N,
MPI_INT, 0, 77, self,
88 Kokkos::parallel_reduce(
89 "peclet::core::halo::probeCheck", Kokkos::RangePolicy<ExecSpace>(0, N),
90 KOKKOS_LAMBDA(
const int i,
int& b) {
91 if (dst(i) != 0x5EC0DE + i * 7919)
107 static const bool v = [] {
108 const char* e = std::getenv(
"PECLET_CORE_GPU_AWARE_MPI");
110 e = std::getenv(
"PECLET_CORE_CUDA_AWARE_MPI");
112 if (std::getenv(
"PECLET_CORE_HALO_VERBOSE"))
113 std::fprintf(stderr,
"peclet.core halo: %s MPI buffers (%s)\n",
114 aware ?
"DEVICE" :
"host-staged", e ?
"env-forced" :
"auto");
135 sendRanks_ = t.sendRanks;
136 recvRanks_ = t.recvRanks;
137 sendCounts_ = t.sendCounts;
138 recvCounts_ = t.recvCounts;
140 sendOff_.assign(sendCounts_.size() + 1, 0);
141 for (std::size_t k = 0; k < sendCounts_.size(); ++k)
142 sendOff_[k + 1] = sendOff_[k] + sendCounts_[k];
143 recvOff_.assign(recvCounts_.size() + 1, 0);
144 for (std::size_t k = 0; k < recvCounts_.size(); ++k)
145 recvOff_[k + 1] = recvOff_[k] + recvCounts_[k];
147 nSend_ =
static_cast<Index>(t.sendIdx.size());
148 nRecv_ =
static_cast<Index>(t.recvIdx.size());
149 nSelf_ =
static_cast<Index>(t.selfSrc.size());
151 d_sendIdx_ =
toDevice(t.sendIdx,
"peclet::core::halo::sendIdx");
152 d_recvIdx_ =
toDevice(t.recvIdx,
"peclet::core::halo::recvIdx");
153 d_selfSrc_ =
toDevice(t.selfSrc,
"peclet::core::halo::selfSrc");
154 d_selfDst_ =
toDevice(t.selfDst,
"peclet::core::halo::selfDst");
156 View<T>(Kokkos::view_alloc(
"peclet::core::halo::sendBuf", Kokkos::WithoutInitializing),
157 static_cast<std::size_t
>(nSend_));
159 View<T>(Kokkos::view_alloc(
"peclet::core::halo::recvBuf", Kokkos::WithoutInitializing),
160 static_cast<std::size_t
>(nRecv_));
161 h_sendBuf_ = Kokkos::create_mirror_view(d_sendBuf_);
162 h_recvBuf_ = Kokkos::create_mirror_view(d_recvBuf_);
180 IndexView src = d_selfSrc_, dst = d_selfDst_;
181 Kokkos::parallel_for(
182 "peclet::core::halo::selfCopy", Kokkos::RangePolicy<ExecSpace>(0, nSelf_),
183 KOKKOS_LAMBDA(
const Index i) { f(dst(i)) = f(src(i)); });
190 Kokkos::parallel_for(
191 "peclet::core::halo::pack", Kokkos::RangePolicy<ExecSpace>(0, nSend_),
192 KOKKOS_LAMBDA(
const Index i) { buf(i) = f(idx(i)); });
194 Kokkos::deep_copy(h_sendBuf_, d_sendBuf_);
200 T* sendBase = aware ? d_sendBuf_.data() : h_sendBuf_.data();
201 T* recvBase = aware ? d_recvBuf_.data() : h_recvBuf_.data();
203 reqs_.reserve(recvRanks_.size() + sendRanks_.size());
204 for (std::size_t k = 0; k < recvRanks_.size(); ++k) {
205 reqs_.emplace_back();
206 MPI_Irecv(recvBase + recvOff_[k], recvCounts_[k] *
static_cast<int>(
sizeof(T)),
MPI_BYTE,
207 recvRanks_[k], tag, comm_, &reqs_.back());
209 for (std::size_t k = 0; k < sendRanks_.size(); ++k) {
210 reqs_.emplace_back();
211 MPI_Isend(sendBase + sendOff_[k], sendCounts_[k] *
static_cast<int>(
sizeof(T)),
MPI_BYTE,
212 sendRanks_[k], tag, comm_, &reqs_.back());
214 inFlightAware_ = aware;
225 Kokkos::deep_copy(d_recvBuf_, h_recvBuf_);
229 Kokkos::parallel_for(
230 "peclet::core::halo::unpack", Kokkos::RangePolicy<ExecSpace>(0, nRecv_),
231 KOKKOS_LAMBDA(
const Index i) { f(idx(i)) = buf(i); });
238 std::vector<int> sendRanks_, recvRanks_, sendCounts_, recvCounts_;
239 std::vector<int> sendOff_, recvOff_;
240 std::vector<MPI_Request> reqs_;
241 bool inFlightAware_ =
false;
242 Index nSend_ = 0, nRecv_ = 0, nSelf_ = 0;
243 IndexView d_sendIdx_, d_recvIdx_, d_selfSrc_, d_selfDst_;
244 View<T> d_sendBuf_, d_recvBuf_;
FlatTopology flatten() const
GPU ghost-layer exchange for a contiguous device field peclet::core::View<T> (one element per extende...
void exchangeEnd(const View< T > &field)
Complete the exchange started by exchangeBegin: wait, then scatter the received halo cells into field...
GridHalo & operator=(const GridHalo &)=delete
void exchange(const View< T > &field, int tag=0)
Exchange ghost layers of the device field field. Blocking (== exchangeBegin+exchangeEnd).
GridHalo(const GridHalo &)=delete
void init(const GridHaloTopology< Dim > &halo)
void exchangeBegin(const View< T > &field, int tag=0)
Start an exchange: periodic self-copy, device pack, post Isend/Irecv, return with the messages in fli...
int MPI_Waitall(int, MPI_Request *, MPI_Status *)
int MPI_Comm_free(MPI_Comm *)
#define MPI_STATUS_IGNORE
#define MPI_STATUSES_IGNORE
int MPI_Isend(const void *, int, MPI_Datatype, int, int, MPI_Comm, MPI_Request *)
int MPI_Initialized(int *f)
int MPI_Irecv(void *, int, MPI_Datatype, int, int, MPI_Comm, MPI_Request *)
bool mpiReportsCudaAware()
Does the MPI library itself claim CUDA awareness (OpenMPI's MPIX_Query_cuda_support)?...
bool gpuAwareMpi()
Whether to hand DEVICE pointers straight to MPI (GPU-aware MPI) instead of host-staging.
bool probeGpuAwareMpi()
Probe whether MPI actually accepts DEVICE pointers (GPU-aware MPI): checksummed loopback Sendrecv on ...
decltype(Kokkos::create_mirror_view(std::declval< View< T > >())) HostView
Host-accessible mirror of View<T> (identical to View<T> on host backends).
View< T > toDevice(const std::vector< T > &h, const std::string &label)
Upload a host std::vector into a freshly-sized device View (empty vector => empty view).
Kokkos::View< T *, MemSpace > View
1D device array.
Kokkos::View< Index *, MemSpace > IndexView
Device array of grid/particle indices (the matched send/recv/self-copy lists).
std::int64_t Index
Signed index type for grids and particles (supersedes block_decomposer's long int IndxT).