ENH: extend mpiAllGather to include integer and float types

- was previously limited to 'char' whereas gatherv/scatterv
  already supported various integer and float types

STYLE: rebundle allToAll declarations with macros

ENH: provide a version of allToAllConsensus returning the Map

- simplifies use and avoids ambiguities in the send/recv parameters

- the Map version will now also transmit zero value data if they exist
  in the Map. Unlike the List version, zero values are not necessary to
  signal connectivity with a Map.

COMP: forwarding template parameters for NBX routines

ENH: consolidate PstreamBuffers size exchange options

- had a variety of nearly identical backends for all-to-all,
  gather/scatter. Now combined internally with a dispatch enumeration
  which provides better control over which size exchange algorithm
  is used.

DEFEATURE: remove experimental full-NBX PstreamBuffers variant

- no advantages seen compared to the hybrid NBX/PEX approach.
  Removal reduces some code cruft.

DEFEATURE: remove experimental "double non-blocking" NBX version

- the idea was to avoid blocking receives for very large data transfers,
  but that is usually better accomplished with a hybrid NBX/PEX approach
  like PstreamBuffers allows
This commit is contained in:
Mark Olesen
2023-11-17 09:08:21 +01:00
parent 3fd1b74b26
commit 8818201196
9 changed files with 514 additions and 495 deletions

View File

@ -1,7 +1,7 @@
/*--------------------------------*- C++ -*----------------------------------*\ /*--------------------------------*- C++ -*----------------------------------*\
| ========= | | | ========= | |
| \\ / F ield | OpenFOAM: The Open Source CFD Toolbox | | \\ / F ield | OpenFOAM: The Open Source CFD Toolbox |
| \\ / O peration | Version: v2306 | | \\ / O peration | Version: v2312 |
| \\ / A nd | Website: www.openfoam.com | | \\ / A nd | Website: www.openfoam.com |
| \\/ M anipulation | | | \\/ M anipulation | |
\*---------------------------------------------------------------------------*/ \*---------------------------------------------------------------------------*/
@ -174,12 +174,13 @@ OptimisationSwitches
nbx.tuning 0; nbx.tuning 0;
// Additional PstreamBuffers tuning parameters (experimental) // Additional PstreamBuffers tuning parameters (experimental)
// -1 : PEX with all-to-all for buffer sizes and point-to-point // 0 : (legacy PEX)
// for contents (legacy approach) // * all-to-all for buffer sizes [legacy approach]
// 0 : hybrid PEX with NBX for buffer sizes and point-to-point // * point-to-point for contents
// for contents (proposed new approach) // 1 : (hybrid PEX)
// 1 : full NBX for buffer sizes and contents (very experimental) // * NBX for buffer sizes [new approach]
pbufs.tuning -1; // * point-to-point for contents
pbufs.tuning 0;
// ===== // =====

View File

@ -599,7 +599,7 @@ public:
// Non-blocking exchange // Non-blocking exchange
//- Exchange \em contiguous data using non-blocking consensus //- Exchange \em contiguous data using non-blocking consensus (NBX)
//- Sends sendData, receives into recvData. //- Sends sendData, receives into recvData.
// //
// Each entry of the recvBufs list is cleared before receipt. // Each entry of the recvBufs list is cleared before receipt.
@ -614,10 +614,10 @@ public:
List<Container>& recvBufs, List<Container>& recvBufs,
const int tag, const int tag,
const label comm, const label comm,
const bool wait = true //!< Wait for requests to complete const bool wait = true //!< (ignored)
); );
//- Exchange \em contiguous data using non-blocking consensus //- Exchange \em contiguous data using non-blocking consensus (NBX)
//- Sends sendData, receives into recvData. //- Sends sendData, receives into recvData.
// //
// Each \em entry of the recvBufs map is cleared before receipt, // Each \em entry of the recvBufs map is cleared before receipt,
@ -636,7 +636,23 @@ public:
Map<Container>& recvBufs, Map<Container>& recvBufs,
const int tag, const int tag,
const label comm, const label comm,
const bool wait = true //!< Wait for requests to complete const bool wait = true //!< (ignored)
);
//- Exchange \em contiguous data using non-blocking consensus (NBX)
//- Sends sendData returns receive information.
//
// For \b non-parallel : copy own rank (if it exists and non-empty)
//
// \note The message tag should be chosen to be a unique value
// since the implementation uses probing with ANY_SOURCE !!
template<class Container, class Type>
static Map<Container> exchangeConsensus
(
const Map<Container>& sendBufs,
const int tag,
const label comm,
const bool wait = true //!< (ignored)
); );
}; };

View File

@ -36,7 +36,7 @@ License
int Foam::PstreamBuffers::algorithm int Foam::PstreamBuffers::algorithm
( (
// Name may change in the future (JUN-2023) // Name may change in the future (JUN-2023)
Foam::debug::optimisationSwitch("pbufs.tuning", -1) Foam::debug::optimisationSwitch("pbufs.tuning", 0)
); );
registerOptSwitch registerOptSwitch
( (
@ -46,20 +46,19 @@ registerOptSwitch
); );
// Simple enumerations
// -------------------
static constexpr int algorithm_PEX_allToAll = -1; // Traditional PEX
//static constexpr int algorithm_PEX_hybrid = 0; // Possible new default?
static constexpr int algorithm_full_NBX = 1; // Very experimental
// * * * * * * * * * * * * * Private Member Functions * * * * * * * * * * * // // * * * * * * * * * * * * * Private Member Functions * * * * * * * * * * * //
inline void Foam::PstreamBuffers::setFinished(bool on) noexcept
{
finishedSendsCalled_ = on;
}
inline void Foam::PstreamBuffers::initFinalExchange() inline void Foam::PstreamBuffers::initFinalExchange()
{ {
// Could also check that it is not called twice // Could also check that it is not called twice
// but that is used for overlapping send/recv (eg, overset) // but that is used for overlapping send/recv (eg, overset)
finishedSendsCalled_ = true; setFinished(true);
clearUnregistered(); clearUnregistered();
} }
@ -67,56 +66,121 @@ inline void Foam::PstreamBuffers::initFinalExchange()
void Foam::PstreamBuffers::finalExchange void Foam::PstreamBuffers::finalExchange
( (
enum modeOption mode,
const bool wait, const bool wait,
labelList& recvSizes labelList& recvSizes
) )
{ {
initFinalExchange(); initFinalExchange();
if (commsType_ == UPstream::commsTypes::nonBlocking) // Pre-flight checks
switch (mode)
{ {
if case modeOption::DEFAULT :
(
wait
&& (algorithm >= algorithm_full_NBX)
&& (UPstream::maxCommsSize <= 0)
)
{ {
// NBX algorithm (nonblocking exchange) // Choose (ALL_TO_ALL | NBX_PEX) from static settings
// - when requested and waiting, no data chunking etc mode =
PstreamDetail::exchangeConsensus<DynamicList<char>, char>
( (
sendBuffers_, (algorithm <= 0)
recvBuffers_, ? modeOption::ALL_TO_ALL
recvSizes, : modeOption::NBX_PEX
(tag_ + 271828), // some unique tag?
comm_,
wait
); );
break;
}
return; case modeOption::GATHER :
{
// gather mode (all-to-one) : master [0] <- everyone
// - only send to master [0]
// note: master [0] is also allowed to 'send' to itself
for (label proci = 1; proci < sendBuffers_.size(); ++proci)
{
sendBuffers_[proci].clear();
}
break;
}
case modeOption::SCATTER :
{
// scatter mode (one-to-all) : master [0] -> everyone
if (!UPstream::master(comm_))
{
// Non-master: has no sends
clearSends();
}
break;
}
default :
break;
} }
// PEX algorithm with two different flavours of exchanging sizes if (commsType_ == UPstream::commsTypes::nonBlocking)
{
// PEX algorithm with different flavours of exchanging sizes
// PEX stage 1: exchange sizes
labelList sendSizes; // Not used by gather/scatter
switch (mode)
{
case modeOption::GATHER :
{
// gather mode (all-to-one): master [0] <- everyone
// - presumed that MPI_Gather will be the most efficient
recvSizes =
UPstream::listGatherValues(sendBuffers_[0].size(), comm_);
if (!UPstream::master(comm_))
{
recvSizes.resize_nocopy(nProcs_);
recvSizes = Zero;
}
break;
}
case modeOption::SCATTER :
{
// scatter mode (one-to-all): master [0] -> everyone
// - presumed that MPI_Scatter will be the most efficient
recvSizes.resize_nocopy(nProcs_);
if (UPstream::master(comm_))
{
forAll(sendBuffers_, proci)
{
recvSizes[proci] = sendBuffers_[proci].size();
}
}
const label myRecv
(
UPstream::listScatterValues(recvSizes, comm_)
);
recvSizes = Zero;
recvSizes[0] = myRecv;
break;
}
case modeOption::NBX_PEX :
{
// Assemble the send sizes (cf. Pstream::exchangeSizes) // Assemble the send sizes (cf. Pstream::exchangeSizes)
labelList sendSizes(nProcs_); sendSizes.resize_nocopy(nProcs_);
forAll(sendBuffers_, proci) forAll(sendBuffers_, proci)
{ {
sendSizes[proci] = sendBuffers_[proci].size(); sendSizes[proci] = sendBuffers_[proci].size();
} }
recvSizes.resize_nocopy(nProcs_); recvSizes.resize_nocopy(nProcs_);
if (algorithm == algorithm_PEX_allToAll) // Exchange sizes (non-blocking consensus)
{
// PEX stage 1: exchange sizes (all-to-all)
UPstream::allToAll(sendSizes, recvSizes, comm_);
}
else
{
// PEX stage 1: exchange sizes (non-blocking consensus)
UPstream::allToAllConsensus UPstream::allToAllConsensus
( (
sendSizes, sendSizes,
@ -124,8 +188,27 @@ void Foam::PstreamBuffers::finalExchange
(tag_ + 314159), // some unique tag? (tag_ + 314159), // some unique tag?
comm_ comm_
); );
break;
} }
case modeOption::DEFAULT :
case modeOption::ALL_TO_ALL :
{
// Assemble the send sizes (cf. Pstream::exchangeSizes)
sendSizes.resize_nocopy(nProcs_);
forAll(sendBuffers_, proci)
{
sendSizes[proci] = sendBuffers_[proci].size();
}
recvSizes.resize_nocopy(nProcs_);
// Exchange sizes (all-to-all)
UPstream::allToAll(sendSizes, recvSizes, comm_);
break;
}
}
// PEX stage 2: point-to-point data exchange // PEX stage 2: point-to-point data exchange
Pstream::exchange<DynamicList<char>, char> Pstream::exchange<DynamicList<char>, char>
( (
@ -200,93 +283,6 @@ void Foam::PstreamBuffers::finalExchange
} }
void Foam::PstreamBuffers::finalGatherScatter
(
const bool isGather,
const bool wait,
labelList& recvSizes
)
{
initFinalExchange();
if (isGather)
{
// gather mode (all-to-one)
// Only send to master [0]. Master is also allowed to 'send' to itself
for (label proci=1; proci < sendBuffers_.size(); ++proci)
{
sendBuffers_[proci].clear();
}
}
else
{
// scatter mode (one-to-all)
if (!UPstream::master(comm_))
{
// Non-master: has no sends
clearSends();
}
}
if (commsType_ == UPstream::commsTypes::nonBlocking)
{
// Use PEX algorithm
// - for a non-sparse gather/scatter, it is presumed that
// MPI_Gather/MPI_Scatter will be the most efficient way to
// communicate the sizes.
// PEX stage 1: exchange sizes (gather or scatter)
if (isGather)
{
// gather mode (all-to-one): master [0] <- everyone
recvSizes =
UPstream::listGatherValues(sendBuffers_[0].size(), comm_);
if (!UPstream::master(comm_))
{
recvSizes.resize_nocopy(nProcs_);
recvSizes = Zero;
}
}
else
{
// scatter mode (one-to-all): master [0] -> everyone
recvSizes.resize_nocopy(nProcs_);
if (UPstream::master(comm_))
{
forAll(sendBuffers_, proci)
{
recvSizes[proci] = sendBuffers_[proci].size();
}
}
const label myRecv(UPstream::listScatterValues(recvSizes, comm_));
recvSizes = Zero;
recvSizes[0] = myRecv;
}
// PEX stage 2: point-to-point data exchange
Pstream::exchange<DynamicList<char>, char>
(
sendBuffers_,
recvSizes,
recvBuffers_,
tag_,
comm_,
wait
);
}
}
// * * * * * * * * * * * * * * * * Constructor * * * * * * * * * * * * * * * // // * * * * * * * * * * * * * * * * Constructor * * * * * * * * * * * * * * * //
Foam::PstreamBuffers::PstreamBuffers Foam::PstreamBuffers::PstreamBuffers
@ -382,7 +378,7 @@ void Foam::PstreamBuffers::clear()
{ {
clearSends(); clearSends();
clearRecvs(); clearRecvs();
finishedSendsCalled_ = false; setFinished(false);
} }
@ -431,13 +427,13 @@ void Foam::PstreamBuffers::clearStorage()
} }
recvPositions_ = Zero; recvPositions_ = Zero;
finishedSendsCalled_ = false; setFinished(false);
} }
void Foam::PstreamBuffers::initRegisterSend() void Foam::PstreamBuffers::initRegisterSend()
{ {
if (!finishedSendsCalled_) if (!finished())
{ {
for (label proci = 0; proci < nProcs_; ++proci) for (label proci = 0; proci < nProcs_; ++proci)
{ {
@ -474,7 +470,7 @@ bool Foam::PstreamBuffers::hasSendData() const
bool Foam::PstreamBuffers::hasRecvData() const bool Foam::PstreamBuffers::hasRecvData() const
{ {
if (finishedSendsCalled_) if (finished())
{ {
forAll(recvBuffers_, proci) forAll(recvBuffers_, proci)
{ {
@ -504,7 +500,7 @@ Foam::label Foam::PstreamBuffers::sendDataCount(const label proci) const
Foam::label Foam::PstreamBuffers::recvDataCount(const label proci) const Foam::label Foam::PstreamBuffers::recvDataCount(const label proci) const
{ {
if (finishedSendsCalled_) if (finished())
{ {
const label len(recvBuffers_[proci].size() - recvPositions_[proci]); const label len(recvBuffers_[proci].size() - recvPositions_[proci]);
@ -529,7 +525,7 @@ Foam::labelList Foam::PstreamBuffers::recvDataCounts() const
{ {
labelList counts(nProcs_, Zero); labelList counts(nProcs_, Zero);
if (finishedSendsCalled_) if (finished())
{ {
forAll(recvBuffers_, proci) forAll(recvBuffers_, proci)
{ {
@ -560,7 +556,7 @@ Foam::label Foam::PstreamBuffers::maxNonLocalRecvCount
{ {
label maxLen = 0; label maxLen = 0;
if (finishedSendsCalled_) if (finished())
{ {
forAll(recvBuffers_, proci) forAll(recvBuffers_, proci)
{ {
@ -599,7 +595,7 @@ Foam::label Foam::PstreamBuffers::maxNonLocalRecvCount() const
const Foam::UList<char> const Foam::UList<char>
Foam::PstreamBuffers::peekRecvData(const label proci) const Foam::PstreamBuffers::peekRecvData(const label proci) const
{ {
if (finishedSendsCalled_) if (finished())
{ {
const label pos = recvPositions_[proci]; const label pos = recvPositions_[proci];
const label len = recvBuffers_[proci].size(); const label len = recvBuffers_[proci].size();
@ -625,18 +621,17 @@ Foam::PstreamBuffers::peekRecvData(const label proci) const
} }
bool Foam::PstreamBuffers::allowClearRecv(bool on) noexcept
{
bool old(allowClearRecv_);
allowClearRecv_ = on;
return old;
}
void Foam::PstreamBuffers::finishedSends(const bool wait) void Foam::PstreamBuffers::finishedSends(const bool wait)
{ {
labelList recvSizes; labelList recvSizes;
finalExchange(wait, recvSizes); finalExchange(modeOption::DEFAULT, wait, recvSizes);
}
void Foam::PstreamBuffers::finishedSendsNBX(const bool wait)
{
labelList recvSizes;
finalExchange(modeOption::NBX_PEX, wait, recvSizes);
} }
@ -649,7 +644,7 @@ void Foam::PstreamBuffers::finishedSends
// Resize for copying back // Resize for copying back
recvSizes.resize_nocopy(sendBuffers_.size()); recvSizes.resize_nocopy(sendBuffers_.size());
finalExchange(wait, recvSizes); finalExchange(modeOption::DEFAULT, wait, recvSizes);
if (commsType_ != UPstream::commsTypes::nonBlocking) if (commsType_ != UPstream::commsTypes::nonBlocking)
{ {
@ -717,8 +712,9 @@ bool Foam::PstreamBuffers::finishedSends
if (changed) if (changed)
{ {
// Update send/recv topology // Update send/recv topology
labelList recvSizes; labelList recvSizes;
finishedSends(recvSizes, wait); // eg, using all-to-all finishedSends(recvSizes, wait); // modeOption::DEFAULT (eg all-to-all)
// The send ranks // The send ranks
sendProcs.clear(); sendProcs.clear();
@ -754,14 +750,14 @@ bool Foam::PstreamBuffers::finishedSends
void Foam::PstreamBuffers::finishedGathers(const bool wait) void Foam::PstreamBuffers::finishedGathers(const bool wait)
{ {
labelList recvSizes; labelList recvSizes;
finalGatherScatter(true, wait, recvSizes); finalExchange(modeOption::GATHER, wait, recvSizes);
} }
void Foam::PstreamBuffers::finishedScatters(const bool wait) void Foam::PstreamBuffers::finishedScatters(const bool wait)
{ {
labelList recvSizes; labelList recvSizes;
finalGatherScatter(false, wait, recvSizes); finalExchange(modeOption::SCATTER, wait, recvSizes);
} }
@ -771,7 +767,7 @@ void Foam::PstreamBuffers::finishedGathers
const bool wait const bool wait
) )
{ {
finalGatherScatter(true, wait, recvSizes); finalExchange(modeOption::GATHER, wait, recvSizes);
if (commsType_ != UPstream::commsTypes::nonBlocking) if (commsType_ != UPstream::commsTypes::nonBlocking)
{ {
@ -793,7 +789,7 @@ void Foam::PstreamBuffers::finishedScatters
const bool wait const bool wait
) )
{ {
finalGatherScatter(false, wait, recvSizes); finalExchange(modeOption::SCATTER, wait, recvSizes);
if (commsType_ != UPstream::commsTypes::nonBlocking) if (commsType_ != UPstream::commsTypes::nonBlocking)
{ {
@ -809,4 +805,27 @@ void Foam::PstreamBuffers::finishedScatters
} }
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
// Controls
bool Foam::PstreamBuffers::finished() const noexcept
{
return finishedSendsCalled_;
}
bool Foam::PstreamBuffers::allowClearRecv() const noexcept
{
return allowClearRecv_;
}
bool Foam::PstreamBuffers::allowClearRecv(bool on) noexcept
{
bool old(allowClearRecv_);
allowClearRecv_ = on;
return old;
}
// ************************************************************************* // // ************************************************************************* //

View File

@ -32,7 +32,7 @@ Description
Use UOPstream to stream data into buffers, call finishedSends() to Use UOPstream to stream data into buffers, call finishedSends() to
notify that data is in buffers and then use IUPstream to get data out notify that data is in buffers and then use IUPstream to get data out
of received buffers. Works with both blocking and nonBlocking. Does of received buffers. Works with both blocking and non-blocking. Does
not make much sense with scheduled since there you would not need these not make much sense with scheduled since there you would not need these
explicit buffers. explicit buffers.
@ -151,6 +151,19 @@ class bitSet;
class PstreamBuffers class PstreamBuffers
{ {
// Private Data Types
//- Private enumeration for handling PEX stage 1 (sizing) modes
enum class modeOption : unsigned char
{
DEFAULT, //!< Use statically configured algorithm
GATHER, //!< Use all-to-one (gather) of sizes
SCATTER, //!< Use one-to-all (scatter) of sizes
ALL_TO_ALL, //!< Use allToAll to obtain sizes
NBX_PEX //!< Use consensus exchange (NBX) to obtain sizes
};
// Private Data // Private Data
//- Track if sends are complete //- Track if sends are complete
@ -190,20 +203,24 @@ class PstreamBuffers
// Private Member Functions // Private Member Functions
//- Change status of finished sends called
inline void setFinished(bool on) noexcept;
//- Clear 'unregistered' send buffers, tag as being send-ready //- Clear 'unregistered' send buffers, tag as being send-ready
inline void initFinalExchange(); inline void initFinalExchange();
//- Mark all sends as having been done. //- Mark all sends as having been done.
// This will start receives (nonBlocking comms). // This will start receives (non-blocking comms).
void finalExchange void finalExchange
( (
enum modeOption mode,
const bool wait, const bool wait,
labelList& recvSizes labelList& recvSizes
); );
//- Mark sends as done. //- Mark sends as done.
// Only exchange sizes using the neighbour ranks // Only exchange sizes using the neighbour ranks
// (nonBlocking comms). // (non-blocking comms).
void finalExchange void finalExchange
( (
const labelUList& sendProcs, const labelUList& sendProcs,
@ -212,14 +229,6 @@ class PstreamBuffers
labelList& recvSizes labelList& recvSizes
); );
//- For all-to-one or one-to-all
void finalGatherScatter
(
const bool isGather,
const bool wait,
labelList& recvSizes
);
// Friendship Access // Friendship Access
@ -343,17 +352,11 @@ public:
// Queries // Queries
//- True if finishedSends() or finishedNeighbourSends() has been called //- True if finishedSends() or finishedNeighbourSends() has been called
bool finished() const noexcept bool finished() const noexcept;
{
return finishedSendsCalled_;
}
//- Is clearStorage of individual receive buffer by external hooks //- Is clearStorage of individual receive buffer by external hooks
//- allowed? (default: true) //- allowed? (default: true)
bool allowClearRecv() const noexcept bool allowClearRecv() const noexcept;
{
return allowClearRecv_;
}
//- True if any (local) send buffers have data //- True if any (local) send buffers have data
bool hasSendData() const; bool hasSendData() const;
@ -436,74 +439,96 @@ public:
// Regular Functions // Regular Functions
//- Mark sends as done //- Mark the send phase as being finished.
// //
// Non-blocking mode: populates receive buffers (all-to-all). // Non-blocking mode: populates receive buffers using all-to-all
// \param wait wait for requests to complete (in nonBlocking mode) // or NBX (depending on tuning parameters).
// \param wait wait for requests to complete (in non-blocking mode)
void finishedSends(const bool wait = true); void finishedSends(const bool wait = true);
//- Mark sends as done. //- Mark the send phase as being finished.
//
// Non-blocking mode: populates receive buffers using NBX.
// \param wait wait for requests to complete (in non-blocking mode)
void finishedSendsNBX(const bool wait = true);
//- Mark the send phase as being finished.
//- Recovers the sizes (bytes) received. //- Recovers the sizes (bytes) received.
// //
// Non-blocking mode: populates receive buffers (all-to-all). // Non-blocking mode: populates receive buffers using all-to-all
// \param[out] recvSizes the sizes (bytes) received // or NBX (depending on tuning parameters).
// \param wait wait for requests to complete (in nonBlocking mode) // \warning currently only valid for non-blocking comms.
void finishedSends
(
//! [out] the sizes (bytes) received
labelList& recvSizes,
//! wait for requests to complete (in non-blocking mode)
const bool wait = true
);
//- Mark the send phase as being finished.
//- Recovers the sizes (bytes) received.
// //
// \warning currently only valid for nonBlocking comms. // Non-blocking mode: populates receive buffers using NBX.
void finishedSends(labelList& recvSizes, const bool wait = true); // \warning currently only valid for non-blocking comms.
void finishedSendsNBX
(
//! [out] the sizes (bytes) received
labelList& recvSizes,
//! wait for requests to complete (in non-blocking mode)
const bool wait = true
);
// Functions with restricted neighbours // Functions with restricted neighbours
//- Mark sends as done using subset of send/recv ranks //- Mark the send phase as being finished, with communication
//- and recover the sizes (bytes) received. //- being limited to a known subset of send/recv ranks.
// //
// Non-blocking mode: populates receive buffers. // Non-blocking mode: populates receive buffers.
// //
// \param neighProcs ranks used for sends/recvs // \warning currently only valid for non-blocking comms.
// \param wait wait for requests to complete (in nonBlocking mode)
//
// \warning currently only valid for nonBlocking comms.
// \note Same as finishedSends with identical sendProcs/recvProcs // \note Same as finishedSends with identical sendProcs/recvProcs
void finishedNeighbourSends void finishedNeighbourSends
( (
//! ranks used for sends/recvs
const labelUList& neighProcs, const labelUList& neighProcs,
//! wait for requests to complete (in non-blocking mode)
const bool wait = true const bool wait = true
); );
//- Mark sends as done using subset of send/recv ranks //- Mark the send phase as being finished, with communication
//- and recover the sizes (bytes) received. //- being limited to a known subset of send/recv ranks.
//- Recovers the sizes (bytes) received.
// //
// Non-blocking mode: it will populate receive buffers. // Non-blocking mode: it will populate receive buffers.
// //
// \param neighProcs ranks used for sends/recvs // \warning currently only valid for non-blocking mode.
// \param[out] recvSizes the sizes (bytes) received
// \param wait wait for requests to complete (in nonBlocking mode)
//
// \warning currently only valid for nonBlocking mode.
void finishedNeighbourSends void finishedNeighbourSends
( (
//! ranks used for sends/recvs
const labelUList& neighProcs, const labelUList& neighProcs,
//! [out] the sizes (bytes) received
labelList& recvSizes, labelList& recvSizes,
//! wait for requests to complete (in non-blocking mode)
const bool wait = true const bool wait = true
); );
//- A caching version that uses a limited send/recv connectivity. //- A caching version that uses a limited send/recv connectivity.
// //
// Non-blocking mode: populates receive buffers. // Non-blocking mode: populates receive buffers.
// \param sendConnections on/off for sending ranks
// \param sendProcs ranks used for sends
// \param recvProcs ranks used for recvs
// \param wait wait for requests to complete (in nonBlocking mode)
//
// \return True if the send/recv connectivity changed // \return True if the send/recv connectivity changed
// //
// \warning currently only valid for nonBlocking comms. // \warning currently only valid for non-blocking comms.
bool finishedSends bool finishedSends
( (
//! inter-rank connections (on/off) for sending ranks
bitSet& sendConnections, bitSet& sendConnections,
//! ranks used for sends
DynamicList<label>& sendProcs, DynamicList<label>& sendProcs,
//! ranks used for recvs
DynamicList<label>& recvProcs, DynamicList<label>& recvProcs,
//! wait for requests to complete (in non-blocking mode)
const bool wait = true const bool wait = true
); );
@ -515,40 +540,46 @@ public:
// Non-blocking mode: populates receive buffers. // Non-blocking mode: populates receive buffers.
// Can use recvDataCount, maxRecvCount etc to recover sizes received. // Can use recvDataCount, maxRecvCount etc to recover sizes received.
// //
// \param wait wait for requests to complete (in nonBlocking mode) // \param wait wait for requests to complete (in non-blocking mode)
// //
// \warning currently only valid for nonBlocking comms. // \warning currently only valid for non-blocking comms.
void finishedGathers(const bool wait = true); void finishedGathers(const bool wait = true);
//- Mark all sends to master as done. //- Mark all sends to master as done.
//- Recovers the sizes (bytes) received. //- Recovers the sizes (bytes) received.
// //
// Non-blocking mode: populates receive buffers (all-to-one). // Non-blocking mode: populates receive buffers (all-to-one).
// \param[out] recvSizes the sizes (bytes) received // \warning currently only valid for non-blocking comms.
// \param wait wait for requests to complete (in nonBlocking mode) void finishedGathers
// (
// \warning currently only valid for nonBlocking comms. //! [out] the sizes (bytes) received
void finishedGathers(labelList& recvSizes, const bool wait = true); labelList& recvSizes,
//! wait for requests to complete (in non-blocking mode)
const bool wait = true
);
//- Mark all sends to sub-procs as done. //- Mark all sends to sub-procs as done.
// //
// Non-blocking mode: populates receive buffers. // Non-blocking mode: populates receive buffers.
// Can use recvDataCount, maxRecvCount etc to recover sizes received. // Can use recvDataCount, maxRecvCount etc to recover sizes received.
// //
// \param wait wait for requests to complete (in nonBlocking mode) // \param wait wait for requests to complete (in non-blocking mode)
// //
// \warning currently only valid for nonBlocking comms. // \warning currently only valid for non-blocking comms.
void finishedScatters(const bool wait = true); void finishedScatters(const bool wait = true);
//- Mark all sends to sub-procs as done. //- Mark all sends to sub-procs as done.
//- Recovers the sizes (bytes) received. //- Recovers the sizes (bytes) received.
// //
// Non-blocking mode: populates receive buffers (all-to-one). // Non-blocking mode: populates receive buffers (all-to-one).
// \param[out] recvSizes the sizes (bytes) received // \warning currently only valid for non-blocking comms.
// \param wait wait for requests to complete (in nonBlocking mode) void finishedScatters
// (
// \warning currently only valid for nonBlocking comms. //! [out] the sizes (bytes) received
void finishedScatters(labelList& recvSizes, const bool wait = true); labelList& recvSizes,
//! wait for requests to complete (in non-blocking mode)
const bool wait = true
);
}; };

View File

@ -46,21 +46,19 @@ namespace Foam
namespace PstreamDetail namespace PstreamDetail
{ {
//- Exchange \em contiguous data using non-blocking consensus exchange //- Exchange \em contiguous data using non-blocking consensus exchange (NBX)
//- with optional tracking of the receive sizes. //- with optional tracking of the receive sizes.
// //
// No internal guards or resizing - data containers are all properly // No internal guards or resizing - data containers are all properly
// sized before calling. // sized before calling.
// //
// \param[in] sendBufs The send buffers list (size: numProcs) // \param[in] sendBufs The send buffers list (size: numProc)
// \param[out] recvBufs The recv buffers list (size: numProcs) // \param[out] recvBufs The recv buffers list (size: numProc)
// \param[out] recvSizes The recv sizes (size: 0 or numProcs). // \param[out] recvSizes The recv sizes (size: 0 or numProc).
// This parameter can be an empty list, in which case the receive sizes // This parameter can be an empty list, in which case the receive sizes
// are not returned. // are not returned.
// \param tag The message tag // \param tag The message tag
// \param comm The communicator // \param comm The communicator
// \param wait Wait for non-blocking receives to complete
// \param recvCommType If blocking or (default) non-blocking
template<class Container, class Type> template<class Container, class Type>
void exchangeConsensus void exchangeConsensus
@ -69,20 +67,17 @@ void exchangeConsensus
UList<Container>& recvBufs, UList<Container>& recvBufs,
labelUList& recvSizes, labelUList& recvSizes,
const int tag, const int tag,
const label comm, const label comm
const bool wait = true,
const UPstream::commsTypes recvCommType = UPstream::commsTypes::nonBlocking
) )
{ {
static_assert(is_contiguous<Type>::value, "Contiguous data only!"); static_assert(is_contiguous<Type>::value, "Contiguous data only!");
const bool initialBarrier = (UPstream::tuning_NBX_ > 0); const bool initialBarrier = (UPstream::tuning_NBX_ > 0);
const label startOfRequests = UPstream::nRequests();
const label myProci = UPstream::myProcNo(comm); const label myProci = UPstream::myProcNo(comm);
const label numProc = UPstream::nProcs(comm); const label numProc = UPstream::nProcs(comm);
// Initial: clear all receive information // Initial: clear all receive locations
for (auto& buf : recvBufs) for (auto& buf : recvBufs)
{ {
buf.clear(); buf.clear();
@ -98,28 +93,37 @@ void exchangeConsensus
if (sendBufs.size() > numProc) if (sendBufs.size() > numProc)
{ {
FatalErrorInFunction FatalErrorInFunction
<< "Send buffers:" << sendBufs.size() << " > numProcs:" << numProc << "Send buffers:" << sendBufs.size() << " > numProc:" << numProc
<< Foam::abort(FatalError); << Foam::abort(FatalError);
} }
if (recvBufs.size() < numProc) if (recvBufs.size() < numProc)
{ {
FatalErrorInFunction FatalErrorInFunction
<< "Recv buffers:" << recvBufs.size() << " < numProcs:" << numProc << "Recv buffers:" << recvBufs.size() << " < numProc:" << numProc
<< Foam::abort(FatalError); << Foam::abort(FatalError);
} }
// #endif // #endif
if (!UPstream::is_parallel(comm)) // Fake send/recv for myself - parallel or non-parallel
{ {
// Do myself
recvBufs[myProci] = sendBufs[myProci]; recvBufs[myProci] = sendBufs[myProci];
if (myProci < recvSizes.size()) if (myProci < recvSizes.size())
{ {
recvSizes[myProci] = recvBufs.size(); recvSizes[myProci] = recvBufs.size();
} }
}
if (!UPstream::is_parallel(comm))
{
// Nothing left to do
return; return;
} }
// ------------------------------------------------------------------------
// Setup sends
// ------------------------------------------------------------------------
// An initial barrier may help to avoid synchronisation problems // An initial barrier may help to avoid synchronisation problems
// caused elsewhere // caused elsewhere
if (initialBarrier) if (initialBarrier)
@ -127,11 +131,12 @@ void exchangeConsensus
UPstream::barrier(comm); UPstream::barrier(comm);
} }
// Algorithm NBX: Nonblocking consensus with List containers // Algorithm NBX: Nonblocking consensus with List containers
DynamicList<UPstream::Request> sendRequests(sendBufs.size()); DynamicList<UPstream::Request> sendRequests(sendBufs.size());
// Start nonblocking synchronous send to processor dest // Start nonblocking synchronous send to destination ranks
for (label proci = 0; proci < numProc; ++proci) for (label proci = 0; proci < numProc; ++proci)
{ {
const auto& sendData = sendBufs[proci]; const auto& sendData = sendBufs[proci];
@ -140,19 +145,8 @@ void exchangeConsensus
{ {
// Do not send/recv empty data // Do not send/recv empty data
} }
else if (proci == myProci) else if (proci != myProci)
{ {
// Do myself
recvBufs[proci] = sendData;
if (proci < recvSizes.size())
{
recvSizes[proci] = sendData.size();
}
}
else
{
// Has data to send.
// The MPI send requests are tracked on a local list
UOPstream::write UOPstream::write
( (
sendRequests.emplace_back(), sendRequests.emplace_back(),
@ -167,7 +161,15 @@ void exchangeConsensus
} }
// ------------------------------------------------------------------------
// Probe and receive // Probe and receive
// ------------------------------------------------------------------------
//
// When receiving can use resize() instead of resize_nocopy() since the
// slots were already initially cleared.
// The resize() also works fine with FixedList since it will
// corresponds to a no-op: send and recv sizes will always be
// identical to its fixed size() / max_size()
UPstream::Request barrierRequest; UPstream::Request barrierRequest;
@ -191,17 +193,16 @@ void exchangeConsensus
const label count = (probed.second / sizeof(Type)); const label count = (probed.second / sizeof(Type));
auto& recvData = recvBufs[proci]; auto& recvData = recvBufs[proci];
recvData.resize_nocopy(count); recvData.resize(count); // OK with resize() instead of _nocopy()
if (proci < recvSizes.size()) if (proci < recvSizes.size())
{ {
recvSizes[proci] = count; recvSizes[proci] = count;
} }
// Any non-blocking MPI recv requests are tracked on internal stack
UIPstream::read UIPstream::read
( (
recvCommType, UPstream::commsTypes::blocking,
proci, proci,
recvData.data_bytes(), recvData.data_bytes(),
recvData.size_bytes(), recvData.size_bytes(),
@ -229,26 +230,18 @@ void exchangeConsensus
} }
} }
} }
// Wait for non-blocking receives to finish
if (wait && recvCommType == UPstream::commsTypes::nonBlocking)
{
UPstream::waitRequests(startOfRequests);
}
} }
//- Exchange \em contiguous data using non-blocking consensus exchange. //- Exchange \em contiguous data using non-blocking consensus exchange (NBX)
// //
// No internal guards - the sending Map corresponds to a segment of // No internal guards - the sending Map corresponds to a segment of
// 0-numProcs. // 0-numProc.
// //
// \param[in] sendBufs The send buffers map (addr: 0-numProcs) // \param[in] sendBufs The send buffers map (addr: 0-numProc)
// \param[out] recvBufs The recv buffers map // \param[out] recvBufs The recv buffers map
// \param tag The message tag // \param tag The message tag
// \param comm The communicator // \param comm The communicator
// \param wait Wait for non-blocking receives to complete
// \param recvCommType If blocking or (default) non-blocking
template<class Container, class Type> template<class Container, class Type>
void exchangeConsensus void exchangeConsensus
@ -256,17 +249,17 @@ void exchangeConsensus
const Map<Container>& sendBufs, const Map<Container>& sendBufs,
Map<Container>& recvBufs, Map<Container>& recvBufs,
const int tag, const int tag,
const label comm, const label comm
const bool wait = true,
const UPstream::commsTypes recvCommType = UPstream::commsTypes::nonBlocking
) )
{ {
static_assert(is_contiguous<Type>::value, "Contiguous data only!"); static_assert(is_contiguous<Type>::value, "Contiguous data only!");
const label startOfRequests = UPstream::nRequests(); // TDB: const bool initialBarrier = (UPstream::tuning_NBX_ > 0);
const label myProci = UPstream::myProcNo(comm);
// Initial: clear out receive 'slots' const label myProci = UPstream::myProcNo(comm);
const label numProc = UPstream::myProcNo(comm);
// Initial: clear all receive locations
// Preferrable to clear out the map entries instead of the map itself // Preferrable to clear out the map entries instead of the map itself
// since this can potentially preserve allocated space // since this can potentially preserve allocated space
// (eg DynamicList entries) between calls // (eg DynamicList entries) between calls
@ -276,9 +269,13 @@ void exchangeConsensus
iter.val().clear(); iter.val().clear();
} }
if (!UPstream::is_parallel(comm)) if (!UPstream::is_rank(comm))
{
return; // Process not in communicator
}
// Fake send/recv for myself - parallel or non-parallel
{ {
// Do myself
const auto iter = sendBufs.find(myProci); const auto iter = sendBufs.find(myProci);
if (iter.good()) if (iter.good())
{ {
@ -290,43 +287,38 @@ void exchangeConsensus
recvBufs(iter.key()) = sendData; recvBufs(iter.key()) = sendData;
} }
} }
}
if (!UPstream::is_parallel(comm))
{
// Nothing left to do
return; return;
} }
// ------------------------------------------------------------------------
// Setup sends
// ------------------------------------------------------------------------
// TDB: initialBarrier ...
// Algorithm NBX: Nonblocking consensus with Map (HashTable) containers // Algorithm NBX: Nonblocking consensus with Map (HashTable) containers
DynamicList<UPstream::Request> sendRequests(sendBufs.size()); DynamicList<UPstream::Request> sendRequests(sendBufs.size());
// Start nonblocking synchronous send to process dest // Start nonblocking synchronous send to destination ranks
forAllConstIters(sendBufs, iter) forAllConstIters(sendBufs, iter)
{ {
const label proci = iter.key(); const label proci = iter.key();
const auto& sendData = iter.val(); const auto& sendData = iter.val();
#ifdef FULLDEBUG if (sendData.empty() || proci < 0 || proci >= numProc)
if (proci >= UPstream::nProcs(comm))
{ {
FatalErrorInFunction // Do not send/recv empty data or invalid destinations
<< "Send buffer:" << proci << " >= numProcs:"
<< UPstream::nProcs(comm)
<< Foam::abort(FatalError);
} }
#endif else if (proci != myProci)
if (sendData.empty())
{ {
// Do not send/recv empty data
}
else if (proci == myProci)
{
// Do myself: insert_or_assign
recvBufs(proci) = sendData;
}
else
{
// Has data to send.
// The MPI send requests are tracked on a local list
UOPstream::write UOPstream::write
( (
sendRequests.emplace_back(), sendRequests.emplace_back(),
@ -341,7 +333,15 @@ void exchangeConsensus
} }
// ------------------------------------------------------------------------
// Probe and receive // Probe and receive
// ------------------------------------------------------------------------
//
// When receiving can use resize() instead of resize_nocopy() since the
// slots were already initially cleared.
// The resize() also works fine with FixedList since it will
// corresponds to a no-op: send and recv sizes will always be
// identical to its fixed size() / max_size()
UPstream::Request barrierRequest; UPstream::Request barrierRequest;
@ -365,12 +365,11 @@ void exchangeConsensus
const label count = (probed.second / sizeof(Type)); const label count = (probed.second / sizeof(Type));
auto& recvData = recvBufs(proci); auto& recvData = recvBufs(proci);
recvData.resize_nocopy(count); recvData.resize(count); // OK with resize() instead of _nocopy()
// Any non-blocking MPI recv requests are tracked on internal stack
UIPstream::read UIPstream::read
( (
recvCommType, UPstream::commsTypes::blocking,
proci, proci,
recvData.data_bytes(), recvData.data_bytes(),
recvData.size_bytes(), recvData.size_bytes(),
@ -397,12 +396,6 @@ void exchangeConsensus
} }
} }
} }
// Wait for non-blocking receives to finish
if (wait && recvCommType == UPstream::commsTypes::nonBlocking)
{
UPstream::waitRequests(startOfRequests);
}
} }
} // namespace PstreamDetail } // namespace PstreamDetail
@ -418,7 +411,7 @@ void Foam::Pstream::exchangeConsensus
List<Container>& recvBufs, List<Container>& recvBufs,
const int tag, const int tag,
const label comm, const label comm,
const bool wait const bool /* wait (ignored) */
) )
{ {
static_assert(is_contiguous<Type>::value, "Contiguous data only!"); static_assert(is_contiguous<Type>::value, "Contiguous data only!");
@ -427,7 +420,7 @@ void Foam::Pstream::exchangeConsensus
{ {
FatalErrorInFunction FatalErrorInFunction
<< "Send buffers size:" << sendBufs.size() << "Send buffers size:" << sendBufs.size()
<< " != numProcs:" << UPstream::nProcs(comm) << " != numProc:" << UPstream::nProcs(comm)
<< Foam::abort(FatalError); << Foam::abort(FatalError);
} }
@ -435,14 +428,13 @@ void Foam::Pstream::exchangeConsensus
recvBufs.resize_nocopy(sendBufs.size()); recvBufs.resize_nocopy(sendBufs.size());
labelList dummyRecvSizes; labelList dummyRecvSizes;
PstreamDetail::exchangeConsensus PstreamDetail::exchangeConsensus<Container, Type>
( (
sendBufs, sendBufs,
recvBufs, recvBufs,
dummyRecvSizes, dummyRecvSizes,
tag, tag,
comm, comm
wait
); );
} }
@ -454,20 +446,45 @@ void Foam::Pstream::exchangeConsensus
Map<Container>& recvBufs, Map<Container>& recvBufs,
const int tag, const int tag,
const label comm, const label comm,
const bool wait const bool /* wait (ignored) */
) )
{ {
static_assert(is_contiguous<Type>::value, "Contiguous data only!"); static_assert(is_contiguous<Type>::value, "Contiguous data only!");
PstreamDetail::exchangeConsensus PstreamDetail::exchangeConsensus<Container, Type>
( (
sendBufs, sendBufs,
recvBufs, recvBufs,
tag, tag,
comm, comm
wait
); );
} }
template<class Container, class Type>
Foam::Map<Container>
Foam::Pstream::exchangeConsensus
(
const Map<Container>& sendBufs,
const int tag,
const label comm,
const bool /* wait (ignored) */
)
{
Map<Container> recvBufs;
static_assert(is_contiguous<Type>::value, "Contiguous data only!");
PstreamDetail::exchangeConsensus<Container, Type>
(
sendBufs,
recvBufs,
tag,
comm
);
return recvBufs;
}
// ************************************************************************* // // ************************************************************************* //

View File

@ -44,6 +44,7 @@ SourceFiles
#include "labelList.H" #include "labelList.H"
#include "DynamicList.H" #include "DynamicList.H"
#include "HashTable.H" #include "HashTable.H"
#include "Map.H"
#include "Enum.H" #include "Enum.H"
#include "ListOps.H" #include "ListOps.H"
@ -55,9 +56,6 @@ namespace Foam
//- Implementation details for UPstream/Pstream/MPI etc. //- Implementation details for UPstream/Pstream/MPI etc.
namespace PstreamDetail {} namespace PstreamDetail {}
// Forward Declarations
template<class T> class Map;
/*---------------------------------------------------------------------------*\ /*---------------------------------------------------------------------------*\
Class UPstream Declaration Class UPstream Declaration
\*---------------------------------------------------------------------------*/ \*---------------------------------------------------------------------------*/
@ -968,119 +966,76 @@ public:
//- Shutdown (finalize) MPI as required and exit program with errNo. //- Shutdown (finalize) MPI as required and exit program with errNo.
static void exit(int errNo = 1); static void exit(int errNo = 1);
//- Exchange integer data with all processors (in the communicator).
// \c sendData[proci] is the value to send to proci.
// After return recvData contains the data from the other processors.
// \n
// For \b non-parallel : does a simple copy of sendData to recvData
static void allToAll
(
const UList<int32_t>& sendData,
UList<int32_t>& recvData,
const label communicator = worldComm
);
//- Exchange integer data with all processors (in the communicator). #undef Pstream_CommonRoutines
// \c sendData[proci] is the value to send to proci. #define Pstream_CommonRoutines(Native) \
// After return recvData contains the data from the other processors. \
// \n /*!\brief Exchange \c Native data with all ranks in communicator */ \
// For \b non-parallel : does a simple copy of sendData to recvData /*! \em non-parallel : simple copy of \p sendData to \p recvData */ \
static void allToAll static void allToAll \
( ( \
const UList<int64_t>& sendData, /*! [in] The value at [proci] is sent to proci */ \
UList<int64_t>& recvData, const UList<Native>& sendData, \
const label communicator = worldComm /*! [out] The data received from the other ranks */ \
); UList<Native>& recvData, \
const label communicator = worldComm \
); \
\
/*!\brief Exchange \em non-zero \c Native data between ranks [NBX] */ \
/*! \p recvData is always initially assigned zero and no non-zero */ \
/*! values are sent/received from other ranks. */ \
/*! \em non-parallel : simple copy of \p sendData to \p recvData */ \
/*! \note The message tag should be chosen to be a unique value */ \
/*! since the implementation uses probing with ANY_SOURCE !! */ \
/*! An initial barrier may help to avoid synchronisation problems */ \
/*! caused elsewhere (See "nbx.tuning" opt switch) */ \
static void allToAllConsensus \
( \
/*! [in] The \em non-zero value at [proci] is sent to proci */ \
const UList<Native>& sendData, \
/*! [out] The non-zero value received from each rank */ \
UList<Native>& recvData, \
/*! Message tag for the communication */ \
const int tag, \
const label communicator = worldComm \
); \
\
/*!\brief Exchange \c Native data between ranks [NBX] */ \
/*! \p recvData map is always cleared initially so a simple check */ \
/*! of its keys is sufficient to determine connectivity. */ \
/*! \em non-parallel : copy own rank (if it exists) */ \
/*! See notes about message tags and "nbx.tuning" opt switch */ \
static void allToAllConsensus \
( \
/*! [in] The value at [proci] is sent to proci. */ \
const Map<Native>& sendData, \
/*! [out] The values received from given ranks. */ \
Map<Native>& recvData, \
/*! Message tag for the communication */ \
const int tag, \
const label communicator = worldComm \
); \
\
/*!\brief Exchange \c Native data between ranks [NBX] */ \
/*! \returns any received data as a Map */ \
static Map<Native> allToAllConsensus \
( \
/*! [in] The value at [proci] is sent to proci. */ \
const Map<Native>& sendData, \
/*! Message tag for the communication */ \
const int tag, \
const label communicator = worldComm \
) \
{ \
Map<Native> recvData; \
allToAllConsensus(sendData, recvData, tag, communicator); \
return recvData; \
}
//- Exchange \b non-zero integer data with all ranks in the communicator Pstream_CommonRoutines(int32_t);
//- using non-blocking consensus exchange. Pstream_CommonRoutines(int64_t);
// The \c sendData[proci] is the (non-zero) value to send to proci.
// After return recvData contains the non-zero values sent from the
// other processors. The recvData list is always assigned zero before
// receipt and values of zero are never transmitted.
// After return recvData contains the data from the other processors.
// \n
// For \b non-parallel : does a simple copy of sendData to recvData
//
// \note The message tag should be chosen to be a unique value
// since the implementation uses probing with ANY_SOURCE !!
// An initial barrier may help to avoid synchronisation problems
// caused elsewhere (See "nbx.tuning" opt switch)
static void allToAllConsensus
(
const UList<int32_t>& sendData,
UList<int32_t>& recvData,
const int tag,
const label communicator = worldComm
);
//- Exchange \b non-zero integer data with all ranks in the communicator #undef Pstream_CommonRoutines
//- using non-blocking consensus exchange.
// The \c sendData[proci] is the (non-zero) value to send to proci.
// After return recvData contains the non-zero values sent from the
// other processors. The recvData list is always assigned zero before
// receipt and values of zero are never transmitted.
// After return recvData contains the data from the other processors.
// \n
// For \b non-parallel : does a simple copy of sendData to recvData
//
// \note The message tag should be chosen to be a unique value
// since the implementation uses probing with ANY_SOURCE !!
// An initial barrier may help to avoid synchronisation problems
// caused elsewhere (See "nbx.tuning" opt switch)
static void allToAllConsensus
(
const UList<int64_t>& sendData,
UList<int64_t>& recvData,
const int tag,
const label communicator = worldComm
);
//- Exchange \b non-zero integer data with all ranks in the communicator
//- using non-blocking consensus exchange.
// The \c sendData[proci] is the (non-zero) value to send to proci.
// After return recvData contains the non-zero values sent from the
// other processors. Since the recvData map always cleared before
// receipt and values of zero are never transmitted, a simple check
// of its keys is sufficient to determine connectivity.
// \n
// For \b non-parallel : copy own rank (if it exists and non-zero)
// from sendData to recvData.
//
// \note The message tag should be chosen to be a unique value
// since the implementation uses probing with ANY_SOURCE !!
// An initial barrier may help to avoid synchronisation problems
// caused elsewhere (See "nbx.tuning" opt switch)
static void allToAllConsensus
(
const Map<int32_t>& sendData,
Map<int32_t>& recvData,
const int tag,
const label communicator = worldComm
);
//- Exchange \b non-zero integer data with all ranks in the communicator
//- using non-blocking consensus exchange.
// The \c sendData[proci] is the (non-zero) value to send to proci.
// After return recvData contains the non-zero values sent from the
// other processors. Since the recvData map always cleared before
// receipt and values of zero are never transmitted, a simple check
// of its keys is sufficient to determine connectivity.
// \n
// For \b non-parallel : copy own rank (if it exists and non-zero)
// from sendData to recvData.
//
// \note The message tag should be chosen to be a unique value
// since the implementation uses probing with ANY_SOURCE !!
// An initial barrier may help to avoid synchronisation problems
// caused elsewhere (See "nbx.tuning" opt switch)
static void allToAllConsensus
(
const Map<int64_t>& sendData,
Map<int64_t>& recvData,
const int tag,
const label communicator = worldComm
);
// Low-level gather/scatter routines // Low-level gather/scatter routines
@ -1121,13 +1076,7 @@ public:
/*! Number of send/recv data per rank. Globally consistent! */ \ /*! Number of send/recv data per rank. Globally consistent! */ \
int count, \ int count, \
const label communicator = worldComm \ const label communicator = worldComm \
); ); \
Pstream_CommonRoutines(char);
#undef Pstream_CommonRoutines
#define Pstream_CommonRoutines(Native) \
\ \
/*! \brief Receive variable length \c Native data from all ranks */ \ /*! \brief Receive variable length \c Native data from all ranks */ \
static void gather \ static void gather \

View File

@ -63,17 +63,9 @@ void Foam::UPstream::mpiAllGather \
int count, \ int count, \
const label comm \ const label comm \
) \ ) \
{} {} \
\
Pstream_CommonRoutines(char); \
#undef Pstream_CommonRoutines
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
#undef Pstream_CommonRoutines
#define Pstream_CommonRoutines(Native) \
void Foam::UPstream::gather \ void Foam::UPstream::gather \
( \ ( \
const Native* sendData, \ const Native* sendData, \

View File

@ -79,17 +79,8 @@ void Foam::UPstream::mpiAllGather \
allData, count, \ allData, count, \
TaggedType, comm \ TaggedType, comm \
); \ ); \
} } \
\
Pstream_CommonRoutines(char, MPI_BYTE);
#undef Pstream_CommonRoutines
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
#undef Pstream_CommonRoutines
#define Pstream_CommonRoutines(Native, TaggedType) \
void Foam::UPstream::gather \ void Foam::UPstream::gather \
( \ ( \
const Native* sendData, \ const Native* sendData, \

View File

@ -507,7 +507,7 @@ void Foam::PstreamDetail::allToAllConsensus
if (!UPstream::is_rank(comm)) if (!UPstream::is_rank(comm))
{ {
return; return; // Process not in communicator
} }
const label myProci = UPstream::myProcNo(comm); const label myProci = UPstream::myProcNo(comm);
@ -539,11 +539,15 @@ void Foam::PstreamDetail::allToAllConsensus
if (!UPstream::is_parallel(comm)) if (!UPstream::is_parallel(comm))
{ {
// deep copy // Non-parallel : deep copy
recvData.deepCopy(sendData); recvData.deepCopy(sendData);
return; return;
} }
// Fake send/recv for myself
{
recvData[myProci] = sendData[myProci];
}
// Implementation description // Implementation description
// -------------------------- // --------------------------
@ -562,7 +566,10 @@ void Foam::PstreamDetail::allToAllConsensus
// This is because we are dealing with a flat list of entries to // This is because we are dealing with a flat list of entries to
// send and not a sparse Map etc. // send and not a sparse Map etc.
DynamicList<MPI_Request> sendRequests(sendData.size());
// ------------------------------------------------------------------------
// Setup sends
// ------------------------------------------------------------------------
profilingPstream::beginTiming(); profilingPstream::beginTiming();
@ -573,20 +580,16 @@ void Foam::PstreamDetail::allToAllConsensus
MPI_Barrier(PstreamGlobals::MPICommunicators_[comm]); MPI_Barrier(PstreamGlobals::MPICommunicators_[comm]);
} }
DynamicList<MPI_Request> sendRequests(sendData.size());
// Start nonblocking synchronous send to process dest // Start nonblocking synchronous send to destination rank
for (label proci = 0; proci < numProc; ++proci) for (label proci = 0; proci < numProc; ++proci)
{ {
if (sendData[proci] == zeroValue) if (sendData[proci] == zeroValue)
{ {
// Do not send/recv empty data // Do not send/recv empty data
} }
else if (proci == myProci) else if (proci != myProci)
{
// Do myself
recvData[proci] = sendData[proci];
}
else
{ {
// Has data to send // Has data to send
@ -604,7 +607,9 @@ void Foam::PstreamDetail::allToAllConsensus
} }
// ------------------------------------------------------------------------
// Probe and receive // Probe and receive
// ------------------------------------------------------------------------
MPI_Request barrierRequest; MPI_Request barrierRequest;
@ -721,22 +726,29 @@ void Foam::PstreamDetail::allToAllConsensus
} }
// Initial: clear out everything // Initial: clear out everything
const Type zeroValue = pTraits<Type>::zero;
recvBufs.clear(); recvBufs.clear();
if (!UPstream::is_parallel(comm)) // Fake send/recv for myself - parallel or non-parallel
{ {
// Do myself
const auto iter = sendBufs.find(myProci); const auto iter = sendBufs.find(myProci);
if (iter.good() && (iter.val() != zeroValue)) if (iter.good())
{ {
// Do myself: insert_or_assign // Do myself: insert_or_assign
recvBufs(iter.key()) = iter.val(); recvBufs(iter.key()) = iter.val();
} }
}
if (!UPstream::is_parallel(comm))
{
// Nothing left to do
return; return;
} }
// ------------------------------------------------------------------------
// Setup sends
// ------------------------------------------------------------------------
// Algorithm NBX: Nonblocking consensus // Algorithm NBX: Nonblocking consensus
// Implementation like above, but sending map data. // Implementation like above, but sending map data.
@ -752,7 +764,7 @@ void Foam::PstreamDetail::allToAllConsensus
} }
// Start nonblocking synchronous send to process dest // Start nonblocking synchronous send to destination ranks
// Same as forAllConstIters() // Same as forAllConstIters()
const auto endIter = sendBufs.cend(); const auto endIter = sendBufs.cend();
@ -761,19 +773,8 @@ void Foam::PstreamDetail::allToAllConsensus
const label proci = iter.key(); const label proci = iter.key();
const auto& sendData = iter.val(); const auto& sendData = iter.val();
if (sendData == zeroValue) if (proci != myProci && proci >= 0 && proci < numProc)
{ {
// Do not send/recv empty/zero data
}
else if (proci == myProci)
{
// Do myself: insert_or_assign
recvBufs(proci) = sendData;
}
else
{
// Has data to send
MPI_Issend MPI_Issend
( (
&sendData, &sendData,
@ -788,7 +789,9 @@ void Foam::PstreamDetail::allToAllConsensus
} }
// ------------------------------------------------------------------------
// Probe and receive // Probe and receive
// ------------------------------------------------------------------------
MPI_Request barrierRequest; MPI_Request barrierRequest;