DNDSR 0.2.1
Distributed Numeric Data Structure for CFV
Loading...
Searching...
No Matches
MPI.hpp
Go to the documentation of this file.
1#pragma once
2/// @file MPI.hpp
3/// @brief MPI wrappers: MPIInfo, collective operations, type mapping, CommStrategy.
4/// @par Unit Test Coverage (test_MPI.cpp, MPI np=1,2,4)
5/// - MPIInfo: constructor, setWorld, operator==, field validation
6/// - MPIWorldSize, MPIWorldRank
7/// - Allreduce (MPI_SUM, MPI_MAX for real/index), AllreduceOneReal, AllreduceOneIndex
8/// - Scan (MPI_SUM), Allgather, Bcast, Barrier, Alltoall
9/// - BasicType_To_MPIIntType (scalar, std::array, Eigen::Matrix)
10/// - CommStrategy: get/set HIndexed/InSituPack
11/// @par Not Yet Tested
12/// - Alltoallv, WaitallLazy, WaitallAuto, BarrierLazy
13/// - MPIBufferHandler, MPITypePairHolder, MPIReqHolder (tested indirectly via ArrayTransformer)
14/// - MPI::ResourceRecycler, MPISerialDo, InsertCheck
15/// - Sub-communicators, CommStrategy functional impact on ArrayTransformer
16
17#include <vector>
18#include <fstream>
19
20#include <mutex>
21
22#include "Defines.hpp"
23
24// Prevent deprecated MPI C++ bindings (removed in MPI-3.0) from being included.
25// These macros must be defined BEFORE including mpi.h.
26// - MPICH_SKIP_MPICXX: MPICH and Intel MPI (which is MPICH-based)
27// - OMPI_SKIP_MPICXX: OpenMPI (only needed if built with --enable-mpi-cxx)
28#ifndef MPICH_SKIP_MPICXX
29# define MPICH_SKIP_MPICXX 1
30#endif
31#ifndef OMPI_SKIP_MPICXX
32# define OMPI_SKIP_MPICXX 1
33#endif
34
37#include "mpi.h"
39
40#ifdef OPEN_MPI
41# include <mpi-ext.h> // Open MPI extension header
42#endif
43
44#ifdef NDEBUG
45# define NDEBUG_DISABLED
46# undef NDEBUG
47#endif
48
49namespace DNDS
50{
51
52 /// @brief MPI counterpart type for `MPI_int` (= C `int`). Used for counts
53 /// and ranks in MPI calls.
54 using MPI_int = int;
55 /// @brief MPI-compatible address/offset type (= `MPI_Aint`, 64-bit on all
56 /// supported platforms). Used by the `hindexed` datatype machinery.
57 using MPI_index = MPI_Aint;
58#define MAX_MPI_int INT32_MAX
59#define MAX_MPI_Aint INT64_MAX
60 static_assert(sizeof(MPI_Aint) == 8);
61
62 /// @brief Vector of MPI counts.
63 using tMPI_sizeVec = std::vector<MPI_int>;
64
65 /// @brief Sentinel "not initialised" MPI_int value (= -1, invalid rank).
66 constexpr MPI_int UnInitMPIInt = -1;
67 /// @brief Sentinel "not initialised" MPI_Aint value (= -1).
68 constexpr MPI_Aint UnInitMPIAint = -1;
69 /// @brief Alias for #tMPI_sizeVec; used where the name "int vec" reads better.
71 /// @brief Vector of MPI_Aint byte-offsets for hindexed datatypes.
72 using tMPI_indexVec = std::vector<MPI_index>;
73 /// @brief Alias for #tMPI_indexVec to match `MPI_Aint` terminology.
75
76 /// @brief Vector of `MPI_Status`, for `MPI_Waitall` / `MPI_Testall`.
77 using tMPI_statVec = std::vector<MPI_Status>;
78 /// @brief Vector of `MPI_Request`, for persistent / nonblocking calls.
79 using tMPI_reqVec = std::vector<MPI_Request>;
80
81 /**
82 * @brief Map a DNDS integer type size to an MPI signed-integer datatype.
83 * @details Compile-time selects `MPI_INT64_T` or `MPI_INT32_T` based on `sizeof(Tbasic)`.
84 * Used by @ref DNDS_MPI_INDEX.
85 */
86 template <class Tbasic>
87 constexpr MPI_Datatype DNDSToMPITypeInt()
88 {
89 static_assert(sizeof(Tbasic) == 8 || sizeof(Tbasic) == 4, "DNDS::Tbasic is not right size");
90 return sizeof(Tbasic) == 8 ? MPI_INT64_T : (sizeof(Tbasic) == 4 ? MPI_INT32_T : MPI_DATATYPE_NULL);
91 }
92
93 /**
94 * @brief Map a DNDS floating-point type size to an MPI datatype.
95 * @details Compile-time selects `MPI_REAL8` or `MPI_REAL4` based on `sizeof(Tbasic)`.
96 * Used by #DNDS_MPI_REAL.
97 */
98 template <class Tbasic>
99 constexpr MPI_Datatype DNDSToMPITypeFloat()
100 {
101 static_assert(sizeof(Tbasic) == 8 || sizeof(Tbasic) == 4, "DNDS::Tbasic is not right size");
102 return sizeof(Tbasic) == 8 ? MPI_REAL8 : (sizeof(Tbasic) == 4 ? MPI_REAL4 : MPI_DATATYPE_NULL);
103 }
104
105 /// @brief MPI datatype matching #index (= `MPI_INT64_T`).
106 const MPI_Datatype DNDS_MPI_INDEX = DNDSToMPITypeInt<index>();
107 /// @brief MPI datatype matching #real (= `MPI_REAL8`).
108 const MPI_Datatype DNDS_MPI_REAL = DNDSToMPITypeFloat<real>();
109
110 //! here are some reasons to upgrade to C++20...
111 // detect if have CommMult and CommType static methods
112
113 /// @brief SFINAE trait detecting a static @ref CommMult member in T.
114 template <typename T, typename = void>
115 struct has_static_CommMult : std::false_type
116 {
117 };
118
119 template <typename T>
120 struct has_static_CommMult<T, std::void_t<decltype(T::CommMult())>> : std::true_type
121 {
122 };
123
124 /// @brief SFINAE trait detecting a static @ref CommType member in T.
125 template <typename T, typename = void>
126 struct has_static_CommType : std::false_type
127 {
128 };
129
130 template <typename T>
131 struct has_static_CommType<T, std::void_t<decltype(T::CommType())>> : std::true_type
132 {
133 };
134
135 /// @brief Dispatch to a user-provided @ref CommPair / @ref CommMult+@ref CommType pair on `T`.
136 /// @details Last resort for types that are not scalars, plain arrays, or
137 /// real Eigen matrices. `T` must either expose static `CommMult()` +
138 /// `CommType()` methods, or a static `CommPair()` returning the same pair.
139 template <class T>
140 std::pair<MPI_Datatype, MPI_int> BasicType_To_MPIIntType_Custom()
141 {
143 return std::make_pair(T::CommType(), T::CommMult());
144 else
145 return T::CommPair(); // last resort
146 }
147
148 /**
149 * @brief Deduce an `(MPI_Datatype, count)` pair that represents a `T` value.
150 *
151 * @details Compile-time dispatch:
152 * - builtin float / int types map to their obvious `MPI_*` datatypes, count = 1;
153 * - C-style arrays (`T[N]`) recurse into the element type and multiply the count;
154 * - `std::array<T, N>` recurses into `T::value_type` and multiplies by `N`;
155 * - fixed-size real Eigen matrices map to #DNDS_MPI_REAL with count `sizeof(T)/sizeof(real)`;
156 * - otherwise falls back to @ref BasicType_To_MPIIntType_Custom.
157 *
158 * Used throughout ghost-communication and collective code to avoid hand-
159 * writing datatype mapping for every MPI call.
160 *
161 * @note Not `constexpr` because OpenMPI handles are not constant-expressions.
162 */
163 template <class T> // TODO: see if an array is bounded
164 //! Warning, not const-expr since OpenMPI disallows it
165 std::pair<MPI_Datatype, MPI_int> BasicType_To_MPIIntType()
166 {
167 static const auto badReturn = std::make_pair(MPI_DATATYPE_NULL, MPI_int(-1));
168 if constexpr (std::is_scalar_v<T>)
169 {
170 if constexpr (std::is_same_v<T, float>)
171 return std::make_pair(MPI_FLOAT, MPI_int(1));
172 if constexpr (std::is_same_v<T, double>)
173 return std::make_pair(MPI_DOUBLE, MPI_int(1));
174 if constexpr (std::is_same_v<T, long double>)
175 return std::make_pair(MPI_LONG_DOUBLE, MPI_int(1));
176
177 if constexpr (std::is_same_v<T, int8_t>)
178 return std::make_pair(MPI_INT8_T, MPI_int(1));
179 if constexpr (std::is_same_v<T, int16_t>)
180 return std::make_pair(MPI_INT16_T, MPI_int(1));
181 if constexpr (std::is_same_v<T, int32_t>)
182 return std::make_pair(MPI_INT32_T, MPI_int(1));
183 if constexpr (std::is_same_v<T, int64_t>)
184 return std::make_pair(MPI_INT64_T, MPI_int(1));
185
186 if constexpr (sizeof(T) == 1)
187 return std::make_pair(MPI_UINT8_T, MPI_int(1));
188 else if constexpr (sizeof(T) == 2)
189 return std::make_pair(MPI_UINT16_T, MPI_int(1));
190 else if constexpr (sizeof(T) == 4)
191 return std::make_pair(MPI_UINT32_T, MPI_int(1));
192 else if constexpr (sizeof(T) == 8)
193 return std::make_pair(MPI_UINT64_T, MPI_int(1));
194 else
195 return BasicType_To_MPIIntType_Custom<T>();
196 }
197 else if constexpr (std::is_array_v<T>)
198 {
199 std::pair<MPI_Datatype, MPI_int> SizCom = BasicType_To_MPIIntType<std::remove_extent_t<T>>();
200 return std::make_pair(SizCom.first, SizCom.second * std::extent_v<T>);
201 }
202 else if constexpr (std::is_trivially_copyable_v<T>)
203 {
204 if constexpr (Meta::is_std_array_v<T>)
205 return std::make_pair(
206 BasicType_To_MPIIntType<typename T::value_type>().first,
207 BasicType_To_MPIIntType<typename T::value_type>().second * T().size());
208 else
209 return BasicType_To_MPIIntType_Custom<T>();
210 }
211 else if constexpr (Meta::is_fixed_data_real_eigen_matrix_v<T>)
212 return std::make_pair(DNDS_MPI_REAL, MPI_int(divide_ceil(sizeof(T), sizeof(real))));
213 else
214 return BasicType_To_MPIIntType_Custom<T>();
215 // else
216 // return badReturn;
217 }
218
219 /**
220 * @brief Lightweight bundle of an MPI communicator and the calling rank's coordinates.
221 *
222 * @details The canonical "where am I in the parallel world" object passed
223 * almost everywhere in DNDSR. Cheap to copy (three ints). Two-phase
224 * construction is supported:
225 * - default-construct, then call #setWorld (or the `MPI_Comm` ctor) once
226 * `MPI_Init` has run.
227 *
228 * Comparison (#operator==) tests exact equality of the triple `(comm, rank, size)`.
229 */
230 struct MPIInfo
231 {
232 /// @brief The underlying MPI communicator handle.
233 MPI_Comm comm = MPI_COMM_NULL;
234 /// @brief This rank's 0-based index within #comm (`-1` until initialised).
235 int rank = -1;
236 /// @brief Number of ranks in #comm (`-1` until initialised).
237 int size = -1;
238
239 MPIInfo() = default;
240
241 /// @brief Wrap an existing MPI communicator; queries rank and size.
242 MPIInfo(MPI_Comm ncomm) : comm(ncomm)
243 {
244
245 int ierr = 0;
246 ierr = MPI_Comm_rank(comm, &rank), DNDS_assert(ierr == MPI_SUCCESS);
247 ierr = MPI_Comm_size(comm, &size), DNDS_assert(ierr == MPI_SUCCESS);
248 }
249
250 /// @brief Low-level constructor for callers that already know `(rank, size)`.
251 /// @warning Bug: the fourth argument stores `r` into `size` too; callers
252 /// currently pass matching values. Prefer the single-arg MPI_Comm ctor.
253 MPIInfo(MPI_Comm nc, int r, int s) : comm(nc), rank(r), size(r)
254 {
255 }
256
257 /// @brief Initialise the object to `MPI_COMM_WORLD`. Requires `MPI_Init` to have run.
258 void setWorld()
259 {
260 comm = MPI_COMM_WORLD;
261 int ierr = 0;
262 ierr = MPI_Comm_rank(comm, &rank), DNDS_assert(ierr == MPI_SUCCESS);
263 ierr = MPI_Comm_size(comm, &size), DNDS_assert(ierr == MPI_SUCCESS);
264 }
265
266 /// @brief Exact triple equality.
267 bool operator==(const MPIInfo &r) const
268 {
269 return (comm == r.comm) && (rank == r.rank) && (size == r.size);
270 }
271 };
272
273 namespace MPI
274 {
275 /**
276 * @brief Singleton that tracks and releases long-lived MPI resources at
277 * `MPI_Finalize` time.
278 *
279 * @details MPI communicators, derived datatypes, and persistent requests
280 * must be released before `MPI_Finalize`; otherwise they leak memory and
281 * MPICH prints warnings. Several DNDSR objects (@ref DNDS::MPITypePairHolder "MPITypePairHolder",
282 * @ref DNDS::MPIReqHolder "MPIReqHolder") register themselves here so that `MPI::Finalize()`
283 * can call their cleanup callbacks even if the C++ lifetime would
284 * outlive the MPI runtime (e.g., static destructors).
285 *
286 * Thread-safe C++11 singleton. Intended to be created under `MPI_COMM_WORLD`.
287 */
289 {
290 private:
291 std::unordered_map<void *, std::function<void()>> cleaners;
292
293 ResourceRecycler(){}; // implemented
294
295 public:
296 // Singleton: explicitly delete all copy / move operations so
297 // the only instance is obtained via `Instance()`. Replaces the
298 // pre-C++11 private-unimplemented idiom previously used here.
303 ~ResourceRecycler() = default;
304
305 /// @brief Access the process-wide singleton.
306 static ResourceRecycler &Instance();
307 /**
308 * @brief Register a cleanup callback keyed by `p`.
309 * @warning Must be paired with @ref RemoveCleaner when `p` is destroyed,
310 * else dangling pointers will be invoked by #clean.
311 */
312 void RegisterCleaner(void *p, std::function<void()> nCleaner);
313 /// @brief Remove a previously-registered cleaner.
314 void RemoveCleaner(void *p);
315 /// @brief Invoke all registered cleaners and drop them. Called by
316 /// `MPI::Finalize()`.
317 void clean();
318 };
319 }
320
321 /// @brief Convenience: `MPI_Comm_size(MPI_COMM_WORLD)`.
323 {
325 MPI_Comm_size(MPI_COMM_WORLD, &ret);
326 return ret;
327 }
328
329 /// @brief Convenience: `MPI_Comm_rank(MPI_COMM_WORLD)`.
331 {
333 MPI_Comm_rank(MPI_COMM_WORLD, &ret);
334 return ret;
335 }
336
337 /// @brief Format a human-readable timestamp using the calling rank as context.
338 std::string getTimeStamp(const MPIInfo &mpi);
339
340/// @brief Debug helper: barrier + print "CHECK <info> RANK r @ fn @ file:line".
341/// @details Compiled out when either `NDEBUG` or `NINSERT` is defined. Used to
342/// trace parallel execution during development; see @ref InsertCheck.
343#define DNDS_MPI_InsertCheck(mpi, info) \
344 InsertCheck(mpi, info, __FUNCTION__, __FILE__, __LINE__)
345
346 using tMPI_typePairVec = std::vector<std::pair<MPI_int, MPI_Datatype>>;
347 /**
348 * @brief RAII vector of `(count, MPI_Datatype)` pairs that frees every
349 * committed datatype when destroyed.
350 *
351 * @details Used by @ref DNDS::ArrayTransformer "ArrayTransformer" to hold the derived datatypes it
352 * builds via `MPI_Type_create_hindexed`. Construction is channelled through
353 * the static #create factory so instances are always owned by
354 * `shared_ptr<MPITypePairHolder>` and correctly registered with the
355 * @ref DNDS::ResourceRecycler "ResourceRecycler".
356 */
357 struct MPITypePairHolder : public tMPI_typePairVec, public std::enable_shared_from_this<MPITypePairHolder>
358 {
361
362 private:
363 /// @brief RAII guard restricting construction to shared_ptr factory.
364 struct shared_ctor_guard // make_shared needs a public ctor but give a private arg
365 {
366 };
367
368 public:
369 /// @brief Perfect-forwarding factory; returns `shared_ptr<MPITypePairHolder>`.
370 template <typename... Args>
371 MPITypePairHolder(shared_ctor_guard g, Args &&...args) : tBase(std::forward<Args>(args)...)
372 {
373 if (!(std::shared_ptr<tSelf>(this, [](tSelf *) {}).use_count() == 1))
374 throw std::runtime_error("tSelf must be created via shared_ptr");
376 { this->clear(); });
377 }
378
379 /// @brief Only public path to construct an instance; forwards to the private constructor.
380 template <typename... Args>
381 static ssp<MPITypePairHolder> create(Args &&...args)
382 {
383 return std::make_shared<MPITypePairHolder>(shared_ctor_guard{}, std::forward<Args>(args)...);
384 }
390
391 // Rule-of-five closure. Owns a ResourceRecycler registration keyed
392 // by `this`; copying or moving would either leave the original
393 // registration dangling or register twice for the same `this`.
398 /// @brief Free every committed datatype and empty the vector.
399 void clear()
400 {
401 for (auto &i : (*this))
402 if (i.first >= 0 && i.second != 0 && i.second != MPI_DATATYPE_NULL)
403 MPI_Type_free(&i.second); //, std::cout << "Free Type" << std::endl;
404 this->tMPI_typePairVec::clear();
405 }
406 };
407
408 /// @brief Shared-pointer alias to @ref DNDS::MPITypePairHolder "MPITypePairHolder".
410 /**
411 * @brief RAII vector of `MPI_Request`s that frees each non-null handle when destroyed.
412 *
413 * @details Mirror of @ref DNDS::MPITypePairHolder "MPITypePairHolder" for MPI requests (persistent or
414 * transient). Used by @ref DNDS::ArrayTransformer "ArrayTransformer" for send/recv request sets.
415 * Construction is likewise channelled through the static #create factory.
416 */
417 struct MPIReqHolder : public tMPI_reqVec, public std::enable_shared_from_this<MPIReqHolder>
418 {
421
422 private:
423 /// @brief RAII guard restricting construction to shared_ptr factory.
424 struct shared_ctor_guard // make_shared needs a public ctor but give a private arg
425 {
426 };
427
428 public:
429 /// @brief Perfect-forwarding factory; returns `shared_ptr<MPIReqHolder>`.
430 template <typename... Args>
431 MPIReqHolder(shared_ctor_guard g, Args &&...args) : tBase(std::forward<Args>(args)...)
432 {
433 if (!(std::shared_ptr<tSelf>(this, [](tSelf *) {}).use_count() == 1))
434 throw std::runtime_error("tSelf must be created via shared_ptr");
436 { this->clear(); });
437 }
438
439 /// @brief Only public path to construct an instance.
440 template <typename... Args>
441 static ssp<MPIReqHolder> create(Args &&...args)
442 {
443 return std::make_shared<MPIReqHolder>(shared_ctor_guard{}, std::forward<Args>(args)...);
444 }
446 {
447 this->clear();
449 }
450
451 // Rule-of-five closure. Owns a ResourceRecycler registration keyed
452 // by `this`; copying or moving would leave the original registration
453 // dangling or register twice for the same `this`.
454 MPIReqHolder(const MPIReqHolder &) = delete;
458 /// @brief Free every non-null request and empty the vector.
459 void clear()
460 {
461 for (auto &i : (*this))
462 if (i != MPI_REQUEST_NULL)
463 MPI_Request_free(&i);
464 this->tMPI_reqVec::clear();
465 }
466 };
467
468}
469
470namespace DNDS::Debug
471{
472 /// @brief Whether the current process is running under a debugger.
473 /// Implemented via `/proc/self/status TracerPid` on Linux.
474 bool IsDebugged();
475 /// @brief If #isDebugging is set, block every rank in a busy-wait loop so
476 /// the user can attach a debugger and inspect state. Exit by setting
477 /// `isDebugging = false` in the debugger.
478 void MPIDebugHold(const MPIInfo &mpi);
479 /// @brief Flag consulted by @ref MPIDebugHold and #assert_false_info_mpi.
480 extern bool isDebugging;
481}
482
483// DNDS_assert_info_mpi is used to help barrier the process before exiting if DNDS::Debug::isDebugging is set
484// remember to set a breakpoint here
485namespace DNDS
486{
487 /// @brief MPI-aware assertion-failure reporter.
488 /// @details Barriers before abort so every rank flushes its output; if
489 /// @ref Debug::isDebugging is set, busy-waits to allow debugger attachment.
490 void assert_false_info_mpi(const char *expr, const char *file, int line, const std::string &info, const DNDS::MPIInfo &mpi);
491}
492#ifdef DNDS_NDEBUG
493# define DNDS_assert_info_mpi(expr, mpi, info) (void(0))
494#else
495/// @brief Collective-aware variant of @ref DNDS_assert_info: every rank calls
496/// into an MPI barrier before aborting, so logs are not interleaved.
497# define DNDS_assert_info_mpi(expr, mpi, info) \
498 ((static_cast<bool>(expr)) \
499 ? void(0) \
500 : ::DNDS::assert_false_info_mpi(#expr, __FILE__, __LINE__, info, mpi))
501#endif
502
503namespace DNDS // TODO: get a concurrency header
504{
505 /// @brief Global mutex serialising host-side HDF5 calls.
506 /// @details HDF5 is not thread-safe by default; this mutex guards all
507 /// DNDSR HDF5 I/O when multiple CPU threads might touch the same file.
508 extern std::mutex HDF_mutex;
509
510 namespace MPI
511 {
512 /// @brief Return the MPI thread-support level the current process was initialised with.
513 inline int GetMPIThreadLevel()
514 {
515 int ret = 0;
516 int ierr = 0;
517 ierr = MPI_Query_thread(&ret), DNDS_assert(ierr == MPI_SUCCESS);
518 return ret;
519 }
520
521 /**
522 * @brief Initialise MPI with thread support, honouring the
523 * @ref DNDS_DISABLE_ASYNC_MPI environment override.
524 *
525 * @details
526 * - No env var or value `0`: request `MPI_THREAD_MULTIPLE` (full).
527 * - `1`: drop to `MPI_THREAD_SERIALIZED`.
528 * - `2`: drop to `MPI_THREAD_FUNNELED`.
529 * - `>=3`: `MPI_THREAD_SINGLE`.
530 *
531 * Aborts via `MPI_Abort` if the provided level is lower than requested.
532 * Idempotent: if MPI is already initialised the call just queries the level.
533 */
534 inline MPI_int Init_thread(int *argc, char ***argv)
535 {
536 int init_flag{0};
537 MPI_Initialized(&init_flag);
538
539 int provided_MPI_THREAD_LEVEL{0};
540 int needed_MPI_THREAD_LEVEL = MPI_THREAD_MULTIPLE;
541
542 auto *env = std::getenv("DNDS_DISABLE_ASYNC_MPI");
543 if (env != nullptr && (std::stod(env) != 0))
544 {
545 int ienv = static_cast<int>(std::stod(env));
546 if (ienv >= 1)
547 needed_MPI_THREAD_LEVEL = MPI_THREAD_SERIALIZED;
548 if (ienv >= 2)
549 needed_MPI_THREAD_LEVEL = MPI_THREAD_FUNNELED;
550 if (ienv >= 3)
551 needed_MPI_THREAD_LEVEL = MPI_THREAD_SINGLE;
552 }
553 int ret{0};
554 if (!init_flag)
555 ret = MPI_Init_thread(argc, argv, needed_MPI_THREAD_LEVEL, &provided_MPI_THREAD_LEVEL);
556 else
557 provided_MPI_THREAD_LEVEL = GetMPIThreadLevel();
558
559 if (provided_MPI_THREAD_LEVEL < needed_MPI_THREAD_LEVEL)
560 {
561 printf("ERROR: The MPI library does not have full thread support\n");
562 MPI_Abort(MPI_COMM_WORLD, 1);
563 }
564
565 return ret;
566 }
567
568 /// @brief Release DNDSR-registered MPI resources then call `MPI_Finalize`.
569 /// @details Idempotent: returns immediately if MPI has already been finalised.
570 inline int Finalize()
571 {
573 int finalized{0};
574 int err = MPI_Finalized(&finalized);
575 if (!finalized)
576 err |= MPI_Finalize();
577 return err;
578 }
579 }
580}
581
582// MPI buffer handler
583#define MPIBufferHandler_REPORT_CHANGE // for monitoring
584namespace DNDS
585{
586 /**
587 * @brief Process-singleton managing the buffer attached to MPI for
588 * `MPI_Bsend` (buffered sends).
589 *
590 * @details Some algorithms (e.g., serialised writes) use buffered sends to
591 * decouple sender from receiver. MPI requires the application to provide
592 * the buffer via `MPI_Buffer_attach`. This singleton owns that buffer,
593 * grows it on demand via #claim, and exposes a thin accounting layer
594 * (#claim / #unclaim) so multiple components can share the buffer without
595 * stepping on each other.
596 *
597 * Thread-safe construction on C++11; not MT-safe for concurrent claims.
598 */
599 class MPIBufferHandler // cxx11 + thread-safe singleton
600 {
601 private:
602 std::vector<uint8_t> buf;
603
604 public:
605 using size_type = decltype(buf)::size_type;
606
607 private:
608 size_type claimed = 0;
609
610 private:
612 {
613 uint8_t *obuf = nullptr;
614 int osize = 0;
615 MPI_Buffer_detach(reinterpret_cast<void *>(&obuf) /* caution */, &osize);
616
617 buf.resize(1024ULL * 1024ULL);
618 MPI_Buffer_attach(buf.data(), int(buf.size())); //! warning, bufsize could overflow
619 }
620
621 public:
622 // Singleton: explicitly delete all copy / move operations so the
623 // only instance is obtained via `Instance()`.
628 ~MPIBufferHandler() = default;
629
630 /// @brief Access the process-wide singleton.
631 static MPIBufferHandler &Instance();
632 /// @brief Current buffer size in bytes (fits in `MPI_int`; asserted).
634 {
635 DNDS_assert(buf.size() <= MAX_MPI_int);
636 return MPI_int(buf.size()); // could overflow!
637 }
638 /// @brief Reserve `cs` additional bytes, growing and re-attaching the
639 /// MPI buffer if needed. `reportRank` is only used for diagnostic logs.
640 void claim(MPI_Aint cs, int reportRank = 0)
641 {
642 if (buf.size() - claimed < static_cast<size_type>(cs))
643 {
644 // std::cout << "claim in " << std::endl;
645 uint8_t *obuf = nullptr;
646 int osize = 0;
647 MPI_Buffer_detach(reinterpret_cast<void *>(&obuf) /* caution */, &osize);
648#ifdef MPIBufferHandler_REPORT_CHANGE
649 std::cout << "MPIBufferHandler: New BUf at " << reportRank << std::endl
650 << osize << std::endl;
651#endif
652 DNDS_assert(static_cast<size_type>(osize) == buf.size());
653 buf.resize(claimed + cs);
654 MPI_Buffer_attach(buf.data(), size_t_to_signed<MPI_int>(buf.size()));
655#ifdef MPIBufferHandler_REPORT_CHANGE
656 std::cout << " -> " << buf.size() << std::endl;
657#endif
658 }
659 claimed += cs;
660 }
661 /// @brief Release `cs` previously-#claim ed bytes (only updates accounting;
662 /// does not shrink the buffer).
664 {
665 DNDS_assert(size_t_to_signed<MPI_int>(claimed) >= cs);
666 claimed -= cs;
667 }
668 /// @brief Direct pointer to the attached buffer (for diagnostics).
669 void *getBuf()
670 {
671 return (void *)(buf.data());
672 }
673 };
674
675}
676
677namespace DNDS::MPI
678{
679 /// @brief Wrapper over `MPI_Bcast` that logs on error and goes through DNDSR retry logic.
680 MPI_int Bcast(void *buf, MPI_int num, MPI_Datatype type, MPI_int source_rank, MPI_Comm comm);
681
682 /// @brief Wrapper over `MPI_Alltoall` (fixed per-peer count).
683 MPI_int Alltoall(void *send, MPI_int sendNum, MPI_Datatype typeSend, void *recv, MPI_int recvNum, MPI_Datatype typeRecv, MPI_Comm comm);
684
685 /// @brief Wrapper over `MPI_Alltoallv` (variable per-peer counts + displacements).
687 void *send, MPI_int *sendSizes, MPI_int *sendStarts, MPI_Datatype sendType,
688 void *recv, MPI_int *recvSizes, MPI_int *recvStarts, MPI_Datatype recvType, MPI_Comm comm);
689
690 /// @brief Wrapper over `MPI_Allreduce`.
691 MPI_int Allreduce(const void *sendbuf, void *recvbuf, MPI_int count,
692 MPI_Datatype datatype, MPI_Op op, MPI_Comm comm);
693
694 /// @brief Wrapper over `MPI_Scan` (inclusive prefix reduction).
695 MPI_int Scan(const void *sendbuf, void *recvbuf, MPI_int count,
696 MPI_Datatype datatype, MPI_Op op, MPI_Comm comm);
697
698 /// @brief Wrapper over `MPI_Allgather`.
699 MPI_int Allgather(const void *sendbuf, MPI_int sendcount, MPI_Datatype sendtype,
700 void *recvbuf, MPI_int recvcount,
701 MPI_Datatype recvtype, MPI_Comm comm);
702
703 /// @brief Wrapper over `MPI_Barrier`.
704 MPI_int Barrier(MPI_Comm comm);
705
706 /// @brief Polling barrier that sleeps `checkNanoSecs` ns between MPI_Test
707 /// calls. Reduces CPU spin when many ranks wait unevenly.
708 MPI_int BarrierLazy(MPI_Comm comm, uint64_t checkNanoSecs);
709
710 /// @brief Like @ref WaitallAuto but sleeps `checkNanoSecs` ns between polls.
711 MPI_int WaitallLazy(MPI_int count, MPI_Request *reqs, MPI_Status *statuses, uint64_t checkNanoSecs = 10000000);
712
713 /// @brief Wait on an array of requests, choosing between `MPI_Waitall` and
714 /// the lazy-poll variant based on @ref DNDS::CommStrategy "CommStrategy" settings.
715 MPI_int WaitallAuto(MPI_int count, MPI_Request *reqs, MPI_Status *statuses);
716
717 /// @brief Single-scalar Allreduce helper for reals (in-place, count = 1).
718 inline void AllreduceOneReal(real &v, MPI_Op op, const MPIInfo &mpi)
719 {
720 // MPI_IN_PLACE is a library-defined sentinel macro (OpenMPI: `((void *)1)`)
721 // whose internal C-style cast is outside project control.
722 // NOLINTNEXTLINE(cppcoreguidelines-pro-type-cstyle-cast)
723 Allreduce(MPI_IN_PLACE, &v, 1, DNDS_MPI_REAL, op, mpi.comm);
724 }
725
726 /// @brief Single-scalar Allreduce helper for indices (in-place, count = 1).
727 inline void AllreduceOneIndex(index &v, MPI_Op op, const MPIInfo &mpi)
728 {
729 // MPI_IN_PLACE is a library-defined sentinel macro (OpenMPI: `((void *)1)`)
730 // whose internal C-style cast is outside project control.
731 // NOLINTNEXTLINE(cppcoreguidelines-pro-type-cstyle-cast)
732 Allreduce(MPI_IN_PLACE, &v, 1, DNDS_MPI_INDEX, op, mpi.comm);
733 }
734
735}
736
737namespace DNDS
738{
739 /**
740 * @brief Execute `f` on each rank serially, in rank order.
741 *
742 * @details Inserts an `MPI_Barrier` before each rank's turn so that output
743 * interleaving is deterministic. Useful for diagnostics where every rank
744 * prints something about its own state.
745 *
746 * @tparam F Callable with no arguments.
747 */
748 template <class F>
749 inline void MPISerialDo(const MPIInfo &mpi, F f)
750 { //! need some improvement: order could be bad?
751 for (MPI_int i = 0; i < mpi.size; i++)
752 {
753 MPI::Barrier(mpi.comm);
754 if (mpi.rank == i)
755 f();
756 }
757 }
758}
759
760namespace DNDS::MPI
761{
762 /**
763 * @brief Process-wide singleton that selects how @ref DNDS::ArrayTransformer "ArrayTransformer" packs
764 * and waits for MPI messages.
765 *
766 * @details Settings affect every transformer:
767 * - @ref ArrayCommType: @ref HIndexed (default: `MPI_Type_create_hindexed`
768 * derived types) vs @ref InSituPack (manual `memcpy` into contiguous send/recv
769 * buffers). The latter can be faster on networks where derived types pay
770 * large unpacking overhead.
771 * - @ref UseStrongSyncWait: insert barriers around wait calls for easier
772 * profiling.
773 * - @ref UseAsyncOneByOne: issue per-peer @ref Isend/@ref Irecv instead of one
774 * persistent @ref Startall.
775 * - @ref UseLazyWait: poll interval (ns) used by @ref MPI::WaitallLazy.
776 *
777 * Must be constructed under `MPI_COMM_WORLD`. Thread-safe C++11 singleton.
778 */
780 {
781 public:
782 /// @brief Which derived-type strategy @ref DNDS::ArrayTransformer "ArrayTransformer" should use.
784 {
785 UnknownArrayCommType = 0, ///< Sentinel / uninitialised.
786 HIndexed = 1, ///< Use `MPI_Type_create_hindexed` derived types (default).
787 InSituPack = 2, ///< Manually pack / unpack into contiguous buffers.
788 };
789
790 static const int Ntype = 10;
791
792 private:
793 ArrayCommType _array_strategy = HIndexed;
794 bool _use_strong_sync_wait = false;
795 bool _use_async_one_by_one = false;
796 double _use_lazy_wait = 0;
797
798 CommStrategy();
799
800 public:
801 // Singleton: explicitly delete all copy / move operations so the
802 // only instance is obtained via `Instance()`.
803 CommStrategy(const CommStrategy &) = delete;
807 ~CommStrategy() = default;
808
809 /// @brief Access the process-wide singleton.
810 static CommStrategy &Instance();
811 /// @brief Current array-pack strategy.
813 /// @brief Override the array-pack strategy (affects subsequently-created transformers).
815 /// @brief Whether barriers are inserted around @ref Waitall for profiling.
816 [[nodiscard]] bool GetUseStrongSyncWait() const;
817 /// @brief Whether transformers should use one-by-one Isend/Irecv.
818 [[nodiscard]] bool GetUseAsyncOneByOne() const;
819 /// @brief Polling interval (ns) for @ref MPI::WaitallLazy. `0` means use `MPI_Waitall`.
820 [[nodiscard]] double GetUseLazyWait() const;
821 };
822}
823
824namespace DNDS::MPI
825{
826 /// @brief Runtime probe: is the current MPI implementation configured with
827 /// CUDA-aware support? Affects whether arrays are transferred on-device or
828 /// via the host round-trip.
829 bool isCudaAware();
830}
831
832namespace DNDS
833{
834 /// @brief Barrier + annotated print used by @ref DNDS_MPI_InsertCheck.
835 /// @details No-op in release builds (`NDEBUG` or `NINSERT` defined).
836 inline void InsertCheck(const MPIInfo &mpi, const std::string &info = "",
837 const std::string &FUNCTION = "", const std::string &FILE = "", int LINE = -1)
838 {
839#if !(defined(NDEBUG) || defined(NINSERT))
840 MPI::Barrier(mpi.comm);
841 std::cout << "=== CHECK \"" << info << "\" RANK " << mpi.rank << " ==="
842 << " @ FName: " << FUNCTION
843 << " @ Place: " << FILE << ":" << LINE << std::endl;
844 MPI::Barrier(mpi.comm);
845#endif
846 }
847}
848
849#ifdef NDEBUG_DISABLED
850# define NDEBUG
851# undef NDEBUG_DISABLED
852#endif
Core type aliases, constants, and metaprogramming utilities for the DNDS framework.
#define DNDS_assert(expr)
Debug-only assertion (compiled out when DNDS_NDEBUG is defined). Prints the expression + file/line + ...
Definition Errors.hpp:112
#define MAX_MPI_int
Definition MPI.hpp:58
#define DISABLE_WARNING_PUSH
Definition Warnings.hpp:73
#define DISABLE_WARNING_UNUSED_VALUE
Definition Warnings.hpp:76
#define DISABLE_WARNING_POP
Definition Warnings.hpp:74
Process-singleton managing the buffer attached to MPI for MPI_Bsend (buffered sends).
Definition MPI.hpp:600
decltype(buf)::size_type size_type
Definition MPI.hpp:605
void unclaim(MPI_int cs)
Release cs previously-claim ed bytes (only updates accounting; does not shrink the buffer).
Definition MPI.hpp:663
MPIBufferHandler & operator=(MPIBufferHandler &&)=delete
MPI_int size()
Current buffer size in bytes (fits in MPI_int; asserted).
Definition MPI.hpp:633
MPIBufferHandler(MPIBufferHandler &&)=delete
static MPIBufferHandler & Instance()
Access the process-wide singleton.
Definition MPI.cpp:106
void claim(MPI_Aint cs, int reportRank=0)
Reserve cs additional bytes, growing and re-attaching the MPI buffer if needed. reportRank is only us...
Definition MPI.hpp:640
void * getBuf()
Direct pointer to the attached buffer (for diagnostics).
Definition MPI.hpp:669
MPIBufferHandler(const MPIBufferHandler &)=delete
MPIBufferHandler & operator=(const MPIBufferHandler &)=delete
Process-wide singleton that selects how ArrayTransformer packs and waits for MPI messages.
Definition MPI.hpp:780
static CommStrategy & Instance()
Access the process-wide singleton.
Definition MPI.cpp:434
bool GetUseStrongSyncWait() const
Whether barriers are inserted around Waitall for profiling.
Definition MPI.cpp:450
CommStrategy & operator=(CommStrategy &&)=delete
double GetUseLazyWait() const
Polling interval (ns) for MPI::WaitallLazy. 0 means use MPI_Waitall.
Definition MPI.cpp:460
CommStrategy(CommStrategy &&)=delete
ArrayCommType GetArrayStrategy()
Current array-pack strategy.
Definition MPI.cpp:440
bool GetUseAsyncOneByOne() const
Whether transformers should use one-by-one Isend/Irecv.
Definition MPI.cpp:455
ArrayCommType
Which derived-type strategy ArrayTransformer should use.
Definition MPI.hpp:784
@ InSituPack
Manually pack / unpack into contiguous buffers.
Definition MPI.hpp:787
@ UnknownArrayCommType
Sentinel / uninitialised.
Definition MPI.hpp:785
@ HIndexed
Use MPI_Type_create_hindexed derived types (default).
Definition MPI.hpp:786
static const int Ntype
Definition MPI.hpp:790
CommStrategy(const CommStrategy &)=delete
void SetArrayStrategy(ArrayCommType t)
Override the array-pack strategy (affects subsequently-created transformers).
Definition MPI.cpp:445
CommStrategy & operator=(const CommStrategy &)=delete
Singleton that tracks and releases long-lived MPI resources at MPI_Finalize time.
Definition MPI.hpp:289
ResourceRecycler(ResourceRecycler &&)=delete
static ResourceRecycler & Instance()
Access the process-wide singleton.
Definition MPI.cpp:309
ResourceRecycler & operator=(ResourceRecycler &&)=delete
void RegisterCleaner(void *p, std::function< void()> nCleaner)
Register a cleanup callback keyed by p.
Definition MPI.cpp:315
void clean()
Invoke all registered cleaners and drop them. Called by MPI::Finalize().
Definition MPI.cpp:327
ResourceRecycler(const ResourceRecycler &)=delete
ResourceRecycler & operator=(const ResourceRecycler &)=delete
void RemoveCleaner(void *p)
Remove a previously-registered cleaner.
Definition MPI.cpp:321
bool IsDebugged()
Whether the current process is running under a debugger. Implemented via /proc/self/status TracerPid ...
Definition MPI.cpp:34
void MPIDebugHold(const MPIInfo &mpi)
If isDebugging is set, block every rank in a busy-wait loop so the user can attach a debugger and ins...
Definition MPI.cpp:58
bool isDebugging
Flag consulted by MPIDebugHold and assert_false_info_mpi.
Definition MPI.cpp:89
MPI_int Allgather(const void *sendbuf, MPI_int sendcount, MPI_Datatype sendtype, void *recvbuf, MPI_int recvcount, MPI_Datatype recvtype, MPI_Comm comm)
Wrapper over MPI_Allgather.
Definition MPI.cpp:229
MPI_int Bcast(void *buf, MPI_int num, MPI_Datatype type, MPI_int source_rank, MPI_Comm comm)
dumb wrapper
Definition MPI.cpp:149
MPI_int WaitallAuto(MPI_int count, MPI_Request *reqs, MPI_Status *statuses)
Wait on an array of requests, choosing between MPI_Waitall and the lazy-poll variant based on CommStr...
Definition MPI.cpp:282
MPI_int BarrierLazy(MPI_Comm comm, uint64_t checkNanoSecs)
Polling barrier that sleeps checkNanoSecs ns between MPI_Test calls. Reduces CPU spin when many ranks...
Definition MPI.cpp:259
MPI_int Alltoall(void *send, MPI_int sendNum, MPI_Datatype typeSend, void *recv, MPI_int recvNum, MPI_Datatype typeRecv, MPI_Comm comm)
Wrapper over MPI_Alltoall (fixed per-peer count).
Definition MPI.cpp:165
void AllreduceOneReal(real &v, MPI_Op op, const MPIInfo &mpi)
Single-scalar Allreduce helper for reals (in-place, count = 1).
Definition MPI.hpp:718
MPI_int Scan(const void *sendbuf, void *recvbuf, MPI_int count, MPI_Datatype datatype, MPI_Op op, MPI_Comm comm)
Wrapper over MPI_Scan (inclusive prefix reduction).
Definition MPI.cpp:219
int Finalize()
Release DNDSR-registered MPI resources then call MPI_Finalize.
Definition MPI.hpp:570
MPI_int Alltoallv(void *send, MPI_int *sendSizes, MPI_int *sendStarts, MPI_Datatype sendType, void *recv, MPI_int *recvSizes, MPI_int *recvStarts, MPI_Datatype recvType, MPI_Comm comm)
Wrapper over MPI_Alltoallv (variable per-peer counts + displacements).
Definition MPI.cpp:181
MPI_int Allreduce(const void *sendbuf, void *recvbuf, MPI_int count, MPI_Datatype datatype, MPI_Op op, MPI_Comm comm)
Wrapper over MPI_Allreduce.
Definition MPI.cpp:202
MPI_int Barrier(MPI_Comm comm)
Wrapper over MPI_Barrier.
Definition MPI.cpp:247
MPI_int Init_thread(int *argc, char ***argv)
Initialise MPI with thread support, honouring the DNDS_DISABLE_ASYNC_MPI environment override.
Definition MPI.hpp:534
bool isCudaAware()
Runtime probe: is the current MPI implementation configured with CUDA-aware support?...
Definition MPI.cpp:297
MPI_int WaitallLazy(MPI_int count, MPI_Request *reqs, MPI_Status *statuses, uint64_t checkNanoSecs)
Like WaitallAuto but sleeps checkNanoSecs ns between polls.
Definition MPI.cpp:270
int GetMPIThreadLevel()
Return the MPI thread-support level the current process was initialised with.
Definition MPI.hpp:513
void AllreduceOneIndex(index &v, MPI_Op op, const MPIInfo &mpi)
Single-scalar Allreduce helper for indices (in-place, count = 1).
Definition MPI.hpp:727
the host side operators are provided as implemented
constexpr MPI_Aint UnInitMPIAint
Sentinel "not initialised" MPI_Aint value (= -1).
Definition MPI.hpp:68
MPI_Aint MPI_index
MPI-compatible address/offset type (= MPI_Aint, 64-bit on all supported platforms)....
Definition MPI.hpp:57
std::vector< MPI_int > tMPI_sizeVec
Vector of MPI counts.
Definition MPI.hpp:63
const MPI_Datatype DNDS_MPI_INDEX
MPI datatype matching index (= MPI_INT64_T).
Definition MPI.hpp:106
tMPI_indexVec tMPI_AintVec
Alias for tMPI_indexVec to match MPI_Aint terminology.
Definition MPI.hpp:74
ssp< MPITypePairHolder > tpMPITypePairHolder
Shared-pointer alias to MPITypePairHolder.
Definition MPI.hpp:409
std::vector< MPI_index > tMPI_indexVec
Vector of MPI_Aint byte-offsets for hindexed datatypes.
Definition MPI.hpp:72
void MPISerialDo(const MPIInfo &mpi, F f)
Execute f on each rank serially, in rank order.
Definition MPI.hpp:749
std::vector< std::pair< MPI_int, MPI_Datatype > > tMPI_typePairVec
Definition MPI.hpp:346
MPI_int MPIWorldRank()
Convenience: MPI_Comm_rank(MPI_COMM_WORLD).
Definition MPI.hpp:330
constexpr T divide_ceil(T a, T b)
Integer ceiling division ceil(a / b). Correct for all signs.
Definition Defines.hpp:574
constexpr MPI_int UnInitMPIInt
Sentinel "not initialised" MPI_int value (= -1, invalid rank).
Definition MPI.hpp:66
std::pair< MPI_Datatype, MPI_int > BasicType_To_MPIIntType_Custom()
Dispatch to a user-provided CommPair / CommMult+ CommType pair on T.
Definition MPI.hpp:140
MPI_int MPIWorldSize()
Convenience: MPI_Comm_size(MPI_COMM_WORLD).
Definition MPI.hpp:322
constexpr MPI_Datatype DNDSToMPITypeInt()
Map a DNDS integer type size to an MPI signed-integer datatype.
Definition MPI.hpp:87
std::vector< MPI_Request > tMPI_reqVec
Vector of MPI_Request, for persistent / nonblocking calls.
Definition MPI.hpp:79
int64_t index
Global row / DOF index type (signed 64-bit; handles multi-billion-cell meshes).
Definition Defines.hpp:112
void InsertCheck(const MPIInfo &mpi, const std::string &info="", const std::string &FUNCTION="", const std::string &FILE="", int LINE=-1)
Barrier + annotated print used by DNDS_MPI_InsertCheck.
Definition MPI.hpp:836
tMPI_sizeVec tMPI_intVec
Alias for tMPI_sizeVec; used where the name "int vec" reads better.
Definition MPI.hpp:70
std::shared_ptr< T > ssp
Shortened alias for std::shared_ptr used pervasively in DNDSR.
Definition Defines.hpp:143
double real
Canonical floating-point scalar used throughout DNDSR (double precision).
Definition Defines.hpp:110
constexpr MPI_Datatype DNDSToMPITypeFloat()
Map a DNDS floating-point type size to an MPI datatype.
Definition MPI.hpp:99
std::string getTimeStamp(const MPIInfo &mpi)
Format a human-readable timestamp using the calling rank as context.
Definition MPI.cpp:115
void assert_false_info_mpi(const char *expr, const char *file, int line, const std::string &info, const DNDS::MPIInfo &mpi)
MPI-aware assertion-failure reporter.
Definition MPI.cpp:94
std::pair< MPI_Datatype, MPI_int > BasicType_To_MPIIntType()
Deduce an (MPI_Datatype, count) pair that represents a T value.
Definition MPI.hpp:165
std::vector< MPI_Status > tMPI_statVec
Vector of MPI_Status, for MPI_Waitall / MPI_Testall.
Definition MPI.hpp:77
std::mutex HDF_mutex
Global mutex serialising host-side HDF5 calls.
Definition MPI.cpp:468
int MPI_int
MPI counterpart type for MPI_int (= C int). Used for counts and ranks in MPI calls.
Definition MPI.hpp:54
const MPI_Datatype DNDS_MPI_REAL
MPI datatype matching real (= MPI_REAL8).
Definition MPI.hpp:108
Lightweight bundle of an MPI communicator and the calling rank's coordinates.
Definition MPI.hpp:231
MPIInfo()=default
int size
Number of ranks in comm (-1 until initialised).
Definition MPI.hpp:237
MPIInfo(MPI_Comm nc, int r, int s)
Low-level constructor for callers that already know (rank, size).
Definition MPI.hpp:253
MPIInfo(MPI_Comm ncomm)
Wrap an existing MPI communicator; queries rank and size.
Definition MPI.hpp:242
int rank
This rank's 0-based index within comm (-1 until initialised).
Definition MPI.hpp:235
MPI_Comm comm
The underlying MPI communicator handle.
Definition MPI.hpp:233
void setWorld()
Initialise the object to MPI_COMM_WORLD. Requires MPI_Init to have run.
Definition MPI.hpp:258
bool operator==(const MPIInfo &r) const
Exact triple equality.
Definition MPI.hpp:267
RAII vector of MPI_Requests that frees each non-null handle when destroyed.
Definition MPI.hpp:418
tMPI_reqVec tBase
Definition MPI.hpp:420
MPIReqHolder(MPIReqHolder &&)=delete
static ssp< MPIReqHolder > create(Args &&...args)
Only public path to construct an instance.
Definition MPI.hpp:441
void clear()
Free every non-null request and empty the vector.
Definition MPI.hpp:459
MPIReqHolder(shared_ctor_guard g, Args &&...args)
Perfect-forwarding factory; returns shared_ptr<MPIReqHolder>.
Definition MPI.hpp:431
MPIReqHolder & operator=(const MPIReqHolder &)=delete
MPIReqHolder(const MPIReqHolder &)=delete
MPIReqHolder & operator=(MPIReqHolder &&)=delete
RAII vector of (count, MPI_Datatype) pairs that frees every committed datatype when destroyed.
Definition MPI.hpp:358
tMPI_typePairVec tBase
Definition MPI.hpp:360
MPITypePairHolder(shared_ctor_guard g, Args &&...args)
Perfect-forwarding factory; returns shared_ptr<MPITypePairHolder>.
Definition MPI.hpp:371
MPITypePairHolder(MPITypePairHolder &&)=delete
void clear()
Free every committed datatype and empty the vector.
Definition MPI.hpp:399
MPITypePairHolder(const MPITypePairHolder &)=delete
static ssp< MPITypePairHolder > create(Args &&...args)
Only public path to construct an instance; forwards to the private constructor.
Definition MPI.hpp:381
MPITypePairHolder & operator=(const MPITypePairHolder &)=delete
MPITypePairHolder & operator=(MPITypePairHolder &&)=delete
here are some reasons to upgrade to C++20...
Definition MPI.hpp:116
SFINAE trait detecting a static CommType member in T.
Definition MPI.hpp:127
Eigen::Matrix< real, 5, 1 > v
real err
tVec r(NCells)
const tPoint const tPoint const tPoint & p