15#ifndef PECLET_CORE_HALO_GRID_HALO_HPP
16#define PECLET_CORE_HALO_GRID_HALO_HPP
25#if __has_include(<mpi-ext.h>)
40#if defined(MPIX_CUDA_AWARE_SUPPORT)
41#if MPIX_CUDA_AWARE_SUPPORT
59#ifdef KOKKOS_ENABLE_SERIAL
60 if (std::is_same_v<ExecSpace, Kokkos::Serial>)
63#ifdef KOKKOS_ENABLE_OPENMP
64 if (std::is_same_v<ExecSpace, Kokkos::OpenMP>)
67#ifdef KOKKOS_ENABLE_THREADS
68 if (std::is_same_v<ExecSpace, Kokkos::Threads>)
81 View<int> src(Kokkos::view_alloc(
"peclet::core::halo::probeSrc", Kokkos::WithoutInitializing),
N);
84 "peclet::core::halo::probeFill", Kokkos::RangePolicy<ExecSpace>(0,
N),
87 const int rc =
MPI_Sendrecv(
src.data(),
N,
MPI_INT, 0, 77,
dst.data(),
N,
MPI_INT, 0, 77,
self,
93 Kokkos::parallel_reduce(
94 "peclet::core::halo::probeCheck", Kokkos::RangePolicy<ExecSpace>(0,
N),
96 if (
dst(
i) != 0x5EC0DE +
i * 7919)
112 static const bool v = [] {
113 const char* e = std::getenv(
"PECLET_CORE_GPU_AWARE_MPI");
115 e = std::getenv(
"PECLET_CORE_CUDA_AWARE_MPI");
117 if (std::getenv(
"PECLET_CORE_HALO_VERBOSE"))
118 std::fprintf(
stderr,
"peclet.core halo: %s MPI buffers (%s)\n",
119 aware ?
"DEVICE" :
"host-staged", e ?
"env-forced" :
"auto");
128 static const double v = [] {
129 const char* e = std::getenv(
"PECLET_CORE_HALO_TIMEOUT");
130 return e ? std::atof(e) : 0.0;
150 sendRanks_ =
t.sendRanks;
151 recvRanks_ =
t.recvRanks;
152 sendCounts_ =
t.sendCounts;
153 recvCounts_ =
t.recvCounts;
155 sendOff_.assign(sendCounts_.size() + 1, 0);
156 for (std::size_t
k = 0;
k < sendCounts_.size(); ++
k)
157 sendOff_[
k + 1] = sendOff_[
k] + sendCounts_[
k];
158 recvOff_.assign(recvCounts_.size() + 1, 0);
159 for (std::size_t
k = 0;
k < recvCounts_.size(); ++
k)
160 recvOff_[
k + 1] = recvOff_[
k] + recvCounts_[
k];
162 nSend_ =
static_cast<Index>(
t.sendIdx.size());
163 nRecv_ =
static_cast<Index>(
t.recvIdx.size());
164 nSelf_ =
static_cast<Index>(
t.selfSrc.size());
166 d_sendIdx_ =
toDevice(
t.sendIdx,
"peclet::core::halo::sendIdx");
167 d_recvIdx_ =
toDevice(
t.recvIdx,
"peclet::core::halo::recvIdx");
168 d_selfSrc_ =
toDevice(
t.selfSrc,
"peclet::core::halo::selfSrc");
169 d_selfDst_ =
toDevice(
t.selfDst,
"peclet::core::halo::selfDst");
171 View<T>(Kokkos::view_alloc(
"peclet::core::halo::sendBuf", Kokkos::WithoutInitializing),
172 static_cast<std::size_t
>(nSend_));
174 View<T>(Kokkos::view_alloc(
"peclet::core::halo::recvBuf", Kokkos::WithoutInitializing),
175 static_cast<std::size_t
>(nRecv_));
176 h_sendBuf_ = Kokkos::create_mirror_view(d_sendBuf_);
177 h_recvBuf_ = Kokkos::create_mirror_view(d_recvBuf_);
182 void setLabel(std::string label) { label_ = std::move(label); }
200 Kokkos::parallel_for(
201 "peclet::core::halo::selfCopy", Kokkos::RangePolicy<ExecSpace>(0, nSelf_),
209 Kokkos::parallel_for(
210 "peclet::core::halo::pack", Kokkos::RangePolicy<ExecSpace>(0, nSend_),
213 Kokkos::deep_copy(h_sendBuf_, d_sendBuf_);
222 reqs_.reserve(recvRanks_.size() + sendRanks_.size());
223 for (std::size_t
k = 0;
k < recvRanks_.size(); ++
k) {
224 reqs_.emplace_back();
226 recvRanks_[
k],
tag, comm_, &reqs_.back());
228 for (std::size_t
k = 0;
k < sendRanks_.size(); ++
k) {
229 reqs_.emplace_back();
231 sendRanks_[
k],
tag, comm_, &reqs_.back());
233 inFlightAware_ =
aware;
239 if (!reqs_.empty()) {
248 Kokkos::deep_copy(d_recvBuf_, h_recvBuf_);
252 Kokkos::parallel_for(
253 "peclet::core::halo::unpack", Kokkos::RangePolicy<ExecSpace>(0, nRecv_),
261 std::vector<int> sendRanks_, recvRanks_, sendCounts_, recvCounts_;
262 std::vector<int> sendOff_, recvOff_;
263 std::vector<MPI_Request> reqs_;
264 bool inFlightAware_ =
false;
265 Index nSend_ = 0, nRecv_ = 0, nSelf_ = 0;
267 IndexView d_sendIdx_, d_recvIdx_, d_selfSrc_, d_selfDst_;
273 void waitAllDiagnosed(
double timeout) {
275 const int n =
static_cast<int>(reqs_.size());
281 int rank = -1, size = -1,
wrank = -1,
wsize = -1;
289 auto world = [&](
int r) {
294 std::string
msg =
"peclet.core halo TIMEOUT after " + std::to_string(
int(
timeout)) +
" s [" +
295 label_ +
"] world " + std::to_string(
wrank) +
"/" + std::to_string(
wsize) +
296 " comm " + std::to_string(rank) +
"/" + std::to_string(size) +
": " +
297 std::to_string(recvRanks_.size()) +
" recv + " +
298 std::to_string(sendRanks_.size()) +
" send partners\n";
299 const std::size_t
nr = recvRanks_.size();
300 for (std::size_t
k = 0;
k < reqs_.size(); ++
k) {
306 const int q =
recv ? recvRanks_[
k] : sendRanks_[
k -
nr];
307 const int c =
recv ? recvCounts_[
k] : sendCounts_[
k -
nr];
308 msg += std::string(
" pending ") + (
recv ?
"RECV from" :
"SEND to ") +
" comm " +
309 std::to_string(
q) +
" (world " + std::to_string(
world(
q)) +
") " +
310 std::to_string(c *
static_cast<long>(
sizeof(
T))) +
" bytes\n";
319 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 setLabel(std::string label)
Name printed by the PECLET_CORE_HALO_TIMEOUT diagnostic (e.g.
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_Comm_size(MPI_Comm, int *s)
int MPI_Testall(int, MPI_Request *, int *flag, MPI_Status *)
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_Abort(MPI_Comm, int code)
int MPI_Comm_rank(MPI_Comm, int *r)
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)?...
double haloTimeout()
PECLET_CORE_HALO_TIMEOUT=<seconds>: turn every exchangeEnd into a deadline wait that names the pendin...
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 ...
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).