34# define NDEBUG_DISABLED
47#define MAX_MPI_int INT32_MAX
48#define MAX_MPI_Aint INT64_MAX
49 static_assert(
sizeof(MPI_Aint) == 8);
70 template <
class Tbasic>
73 static_assert(
sizeof(Tbasic) == 8 ||
sizeof(Tbasic) == 4,
"DNDS::Tbasic is not right size");
74 return sizeof(Tbasic) == 8 ? MPI_INT64_T : (
sizeof(Tbasic) == 4 ? MPI_INT32_T : MPI_DATATYPE_NULL);
82 template <
class Tbasic>
85 static_assert(
sizeof(Tbasic) == 8 ||
sizeof(Tbasic) == 4,
"DNDS::Tbasic is not right size");
86 return sizeof(Tbasic) == 8 ? MPI_REAL8 : (
sizeof(Tbasic) == 4 ? MPI_REAL4 : MPI_DATATYPE_NULL);
98 template <
typename T,
typename =
void>
103 template <
typename T>
109 template <
typename T,
typename =
void>
114 template <
typename T>
127 return std::make_pair(T::CommType(), T::CommMult());
129 return T::CommPair();
151 static const auto badReturn = std::make_pair(MPI_Datatype(MPI_DATATYPE_NULL),
MPI_int(-1));
152 if constexpr (std::is_scalar_v<T>)
154 if constexpr (std::is_same_v<T, float>)
155 return std::make_pair(MPI_Datatype(MPI_FLOAT),
MPI_int(1));
156 if constexpr (std::is_same_v<T, double>)
157 return std::make_pair(MPI_Datatype(MPI_DOUBLE),
MPI_int(1));
158 if constexpr (std::is_same_v<T, long double>)
159 return std::make_pair(MPI_Datatype(MPI_LONG_DOUBLE),
MPI_int(1));
161 if constexpr (std::is_same_v<T, int8_t>)
162 return std::make_pair(MPI_Datatype(MPI_INT8_T),
MPI_int(1));
163 if constexpr (std::is_same_v<T, int16_t>)
164 return std::make_pair(MPI_Datatype(MPI_INT16_T),
MPI_int(1));
165 if constexpr (std::is_same_v<T, int32_t>)
166 return std::make_pair(MPI_Datatype(MPI_INT32_T),
MPI_int(1));
167 if constexpr (std::is_same_v<T, int64_t>)
168 return std::make_pair(MPI_Datatype(MPI_INT64_T),
MPI_int(1));
170 if constexpr (
sizeof(T) == 1)
171 return std::make_pair(MPI_Datatype(MPI_UINT8_T),
MPI_int(1));
172 else if constexpr (
sizeof(T) == 2)
173 return std::make_pair(MPI_Datatype(MPI_UINT16_T),
MPI_int(1));
174 else if constexpr (
sizeof(T) == 4)
175 return std::make_pair(MPI_Datatype(MPI_UINT32_T),
MPI_int(1));
176 else if constexpr (
sizeof(T) == 8)
177 return std::make_pair(MPI_Datatype(MPI_UINT64_T),
MPI_int(1));
179 return BasicType_To_MPIIntType_Custom<T>();
181 else if constexpr (std::is_array_v<T>)
183 std::pair<MPI_Datatype, MPI_int> SizCom = BasicType_To_MPIIntType<std::remove_extent_t<T>>();
184 return std::make_pair(SizCom.first, SizCom.second * std::extent_v<T>);
186 else if constexpr (std::is_trivially_copyable_v<T>)
188 if constexpr (Meta::is_std_array_v<T>)
189 return std::make_pair(
190 BasicType_To_MPIIntType<typename T::value_type>().first,
191 BasicType_To_MPIIntType<typename T::value_type>().second * T().size());
193 return BasicType_To_MPIIntType_Custom<T>();
195 else if constexpr (Meta::is_fixed_data_real_eigen_matrix_v<T>)
198 return BasicType_To_MPIIntType_Custom<T>();
244 comm = MPI_COMM_WORLD;
275 std::unordered_map<
void *, std::function<void()>> cleaners;
302 MPI_Comm_size(MPI_COMM_WORLD, &ret);
310 MPI_Comm_rank(MPI_COMM_WORLD, &ret);
320#define DNDS_MPI_InsertCheck(mpi, info) \
321 InsertCheck(mpi, info, __FUNCTION__, __FILE__, __LINE__)
341 struct shared_ctor_guard
347 template <
typename... Args>
350 if (!(std::shared_ptr<tSelf>(
this, [](
tSelf *) {}).use_count() == 1))
351 throw std::runtime_error(
"tSelf must be created via shared_ptr");
357 template <
typename... Args>
360 return std::make_shared<MPITypePairHolder>(shared_ctor_guard{}, std::forward<Args>(args)...);
370 for (
auto &i : (*this))
371 if (i.first >= 0 && i.second != 0 && i.second != MPI_DATATYPE_NULL)
372 MPI_Type_free(&i.second);
373 this->tMPI_typePairVec::clear();
393 struct shared_ctor_guard
399 template <
typename... Args>
402 if (!(std::shared_ptr<tSelf>(
this, [](
tSelf *) {}).use_count() == 1))
403 throw std::runtime_error(
"tSelf must be created via shared_ptr");
409 template <
typename... Args>
412 return std::make_shared<MPIReqHolder>(shared_ctor_guard{}, std::forward<Args>(args)...);
422 for (
auto &i : (*this))
423 if (i != MPI_REQUEST_NULL)
424 MPI_Request_free(&i);
425 this->tMPI_reqVec::clear();
454# define DNDS_assert_info_mpi(expr, mpi, info) (void(0))
458# define DNDS_assert_info_mpi(expr, mpi, info) \
459 ((static_cast<bool>(expr)) \
461 : ::DNDS::assert_false_info_mpi(#expr, __FILE__, __LINE__, info, mpi))
478 ierr = MPI_Query_thread(&ret),
DNDS_assert(ierr == MPI_SUCCESS);
498 MPI_Initialized(&init_flag);
500 int provided_MPI_THREAD_LEVEL{0};
501 int needed_MPI_THREAD_LEVEL = MPI_THREAD_MULTIPLE;
503 auto *env = std::getenv(
"DNDS_DISABLE_ASYNC_MPI");
504 if (env != NULL && (std::stod(env) != 0))
506 int ienv =
static_cast<int>(std::stod(env));
508 needed_MPI_THREAD_LEVEL = MPI_THREAD_SERIALIZED;
510 needed_MPI_THREAD_LEVEL = MPI_THREAD_FUNNELED;
512 needed_MPI_THREAD_LEVEL = MPI_THREAD_SINGLE;
516 ret = MPI_Init_thread(argc, argv, needed_MPI_THREAD_LEVEL, &provided_MPI_THREAD_LEVEL);
520 if (provided_MPI_THREAD_LEVEL < needed_MPI_THREAD_LEVEL)
522 printf(
"ERROR: The MPI library does not have full thread support\n");
523 MPI_Abort(MPI_COMM_WORLD, 1);
535 int err = MPI_Finalized(&finalized);
537 err |= MPI_Finalize();
544#define MPIBufferHandler_REPORT_CHANGE
563 std::vector<uint8_t> buf;
576 MPI_Buffer_detach(
reinterpret_cast<void *
>(&obuf) , &osize);
578 buf.resize(1024ULL * 1024ULL);
579 MPI_Buffer_attach(buf.data(),
int(buf.size()));
595 void claim(MPI_Aint cs,
int reportRank = 0)
597 if (buf.size() - claimed <
static_cast<size_type>(cs))
602 MPI_Buffer_detach(
reinterpret_cast<void *
>(&obuf) , &osize);
603#ifdef MPIBufferHandler_REPORT_CHANGE
604 std::cout <<
"MPIBufferHandler: New BUf at " << reportRank << std::endl
605 << osize << std::endl;
608 buf.resize(claimed + cs);
609 MPI_Buffer_attach(buf.data(), size_t_to_signed<MPI_int>(buf.size()));
610#ifdef MPIBufferHandler_REPORT_CHANGE
611 std::cout <<
" -> " << buf.size() << std::endl;
620 DNDS_assert(size_t_to_signed<MPI_int>(claimed) >= cs);
626 return (
void *)(buf.data());
642 void *send,
MPI_int *sendSizes,
MPI_int *sendStarts, MPI_Datatype sendType,
643 void *recv,
MPI_int *recvSizes,
MPI_int *recvStarts, MPI_Datatype recvType, MPI_Comm comm);
647 MPI_Datatype datatype, MPI_Op op, MPI_Comm comm);
651 MPI_Datatype datatype, MPI_Op op, MPI_Comm comm);
655 void *recvbuf,
MPI_int recvcount,
656 MPI_Datatype recvtype, MPI_Comm comm);
700 for (
MPI_int i = 0; i < mpi.size; i++)
743 bool _use_strong_sync_wait =
false;
744 bool _use_async_one_by_one =
false;
745 double _use_lazy_wait = 0;
780 const std::string &FUNCTION =
"",
const std::string &FILE =
"",
int LINE = -1)
782#if !(defined(NDEBUG) || defined(NINSERT))
784 std::cout <<
"=== CHECK \"" << info <<
"\" RANK " << mpi.rank <<
" ==="
785 <<
" @ FName: " << FUNCTION
786 <<
" @ Place: " << FILE <<
":" << LINE << std::endl;
792#ifdef NDEBUG_DISABLED
794# undef NDEBUG_DISABLED
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 + ...
#define DISABLE_WARNING_PUSH
#define DISABLE_WARNING_UNUSED_VALUE
#define DISABLE_WARNING_POP
Process-singleton managing the buffer attached to MPI for MPI_Bsend (buffered sends).
decltype(buf)::size_type size_type
void unclaim(MPI_int cs)
Release cs previously-claim ed bytes (only updates accounting; does not shrink the buffer).
MPI_int size()
Current buffer size in bytes (fits in MPI_int; asserted).
static MPIBufferHandler & Instance()
Access the process-wide singleton.
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...
void * getBuf()
Direct pointer to the attached buffer (for diagnostics).
Process-wide singleton that selects how ArrayTransformer packs and waits for MPI messages.
static CommStrategy & Instance()
Access the process-wide singleton.
bool GetUseStrongSyncWait() const
Whether barriers are inserted around Waitall for profiling.
double GetUseLazyWait() const
Polling interval (ns) for MPI::WaitallLazy. 0 means use MPI_Waitall.
ArrayCommType GetArrayStrategy()
Current array-pack strategy.
bool GetUseAsyncOneByOne() const
Whether transformers should use one-by-one Isend/Irecv.
ArrayCommType
Which derived-type strategy ArrayTransformer should use.
@ InSituPack
Manually pack / unpack into contiguous buffers.
@ UnknownArrayCommType
Sentinel / uninitialised.
@ HIndexed
Use MPI_Type_create_hindexed derived types (default).
void SetArrayStrategy(ArrayCommType t)
Override the array-pack strategy (affects subsequently-created transformers).
Singleton that tracks and releases long-lived MPI resources at MPI_Finalize time.
static ResourceRecycler & Instance()
Access the process-wide singleton.
void RegisterCleaner(void *p, std::function< void()> nCleaner)
Register a cleanup callback keyed by p.
void clean()
Invoke all registered cleaners and drop them. Called by MPI::Finalize().
void RemoveCleaner(void *p)
Remove a previously-registered cleaner.
bool IsDebugged()
Whether the current process is running under a debugger. Implemented via /proc/self/status TracerPid ...
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...
bool isDebugging
Flag consulted by MPIDebugHold and assert_false_info_mpi.
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.
MPI_int Bcast(void *buf, MPI_int num, MPI_Datatype type, MPI_int source_rank, MPI_Comm comm)
dumb wrapper
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...
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...
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).
void AllreduceOneReal(real &v, MPI_Op op, const MPIInfo &mpi)
Single-scalar Allreduce helper for reals (in-place, count = 1).
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).
int Finalize()
Release DNDSR-registered MPI resources then call MPI_Finalize.
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).
MPI_int Allreduce(const void *sendbuf, void *recvbuf, MPI_int count, MPI_Datatype datatype, MPI_Op op, MPI_Comm comm)
Wrapper over MPI_Allreduce.
MPI_int Barrier(MPI_Comm comm)
Wrapper over MPI_Barrier.
MPI_int Init_thread(int *argc, char ***argv)
Initialise MPI with thread support, honouring the DNDS_DISABLE_ASYNC_MPI environment override.
bool isCudaAware()
Runtime probe: is the current MPI implementation configured with CUDA-aware support?...
MPI_int WaitallLazy(MPI_int count, MPI_Request *reqs, MPI_Status *statuses, uint64_t checkNanoSecs)
Like WaitallAuto but sleeps checkNanoSecs ns between polls.
int GetMPIThreadLevel()
Return the MPI thread-support level the current process was initialised with.
void AllreduceOneIndex(index &v, MPI_Op op, const MPIInfo &mpi)
Single-scalar Allreduce helper for indices (in-place, count = 1).
the host side operators are provided as implemented
MPI_Aint MPI_index
MPI-compatible address/offset type (= MPI_Aint, 64-bit on all supported platforms)....
std::vector< MPI_int > tMPI_sizeVec
Vector of MPI counts.
const MPI_Datatype DNDS_MPI_INDEX
MPI datatype matching index (= MPI_INT64_T).
tMPI_indexVec tMPI_AintVec
Alias for tMPI_indexVec to match MPI_Aint terminology.
ssp< MPITypePairHolder > tpMPITypePairHolder
Shared-pointer alias to MPITypePairHolder.
std::vector< MPI_index > tMPI_indexVec
Vector of MPI_Aint byte-offsets for hindexed datatypes.
void MPISerialDo(const MPIInfo &mpi, F f)
Execute f on each rank serially, in rank order.
std::vector< std::pair< MPI_int, MPI_Datatype > > tMPI_typePairVec
MPI_int MPIWorldRank()
Convenience: MPI_Comm_rank(MPI_COMM_WORLD).
constexpr T divide_ceil(T a, T b)
Integer ceiling division ceil(a / b). Correct for all signs.
std::pair< MPI_Datatype, MPI_int > BasicType_To_MPIIntType_Custom()
Dispatch to a user-provided CommPair / CommMult+ CommType pair on T.
MPI_int MPIWorldSize()
Convenience: MPI_Comm_size(MPI_COMM_WORLD).
constexpr MPI_Datatype __DNDSToMPITypeInt()
Map a DNDS integer type size to an MPI signed-integer datatype.
std::vector< MPI_Request > tMPI_reqVec
Vector of MPI_Request, for persistent / nonblocking calls.
int64_t index
Global row / DOF index type (signed 64-bit; handles multi-billion-cell meshes).
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.
tMPI_sizeVec tMPI_intVec
Alias for tMPI_sizeVec; used where the name "int vec" reads better.
constexpr MPI_Datatype __DNDSToMPITypeFloat()
Map a DNDS floating-point type size to an MPI datatype.
std::shared_ptr< T > ssp
Shortened alias for std::shared_ptr used pervasively in DNDSR.
double real
Canonical floating-point scalar used throughout DNDSR (double precision).
std::string getTimeStamp(const MPIInfo &mpi)
Format a human-readable timestamp using the calling rank as context.
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.
std::pair< MPI_Datatype, MPI_int > BasicType_To_MPIIntType()
Deduce an (MPI_Datatype, count) pair that represents a T value.
std::vector< MPI_Status > tMPI_statVec
Vector of MPI_Status, for MPI_Waitall / MPI_Testall.
std::mutex HDF_mutex
Global mutex serialising host-side HDF5 calls.
int MPI_int
MPI counterpart type for MPI_int (= C int). Used for counts and ranks in MPI calls.
const MPI_Datatype DNDS_MPI_REAL
MPI datatype matching real (= MPI_REAL8).
Lightweight bundle of an MPI communicator and the calling rank's coordinates.
int size
Number of ranks in comm (-1 until initialised).
MPIInfo(MPI_Comm nc, int r, int s)
Low-level constructor for callers that already know (rank, size).
MPIInfo(MPI_Comm ncomm)
Wrap an existing MPI communicator; queries rank and size.
int rank
This rank's 0-based index within comm (-1 until initialised).
MPI_Comm comm
The underlying MPI communicator handle.
void setWorld()
Initialise the object to MPI_COMM_WORLD. Requires MPI_Init to have run.
bool operator==(const MPIInfo &r) const
Exact triple equality.
RAII vector of MPI_Requests that frees each non-null handle when destroyed.
static ssp< MPIReqHolder > create(Args &&...args)
Only public path to construct an instance.
void clear()
Free every non-null request and empty the vector.
MPIReqHolder(shared_ctor_guard g, Args &&...args)
Perfect-forwarding factory; returns shared_ptr<MPIReqHolder>.
RAII vector of (count, MPI_Datatype) pairs that frees every committed datatype when destroyed.
MPITypePairHolder(shared_ctor_guard g, Args &&...args)
Perfect-forwarding factory; returns shared_ptr<MPITypePairHolder>.
void clear()
Free every committed datatype and empty the vector.
static ssp< MPITypePairHolder > create(Args &&...args)
Only public path to construct an instance; forwards to the private constructor.
here are some reasons to upgrade to C++20...
SFINAE trait detecting a static CommType member in T.
Eigen::Matrix< real, 5, 1 > v