diff --git a/etc/controlDict b/etc/controlDict index 53059f9f43..fc3d73ae54 100644 --- a/etc/controlDict +++ b/etc/controlDict @@ -1,7 +1,7 @@ /*--------------------------------*- C++ -*----------------------------------*\ | ========= | | | \\ / F ield | OpenFOAM: The Open Source CFD Toolbox | -| \\ / O peration | Version: v2306 | +| \\ / O peration | Version: v2312 | | \\ / A nd | Website: www.openfoam.com | | \\/ M anipulation | | \*---------------------------------------------------------------------------*/ @@ -174,12 +174,13 @@ OptimisationSwitches nbx.tuning 0; // Additional PstreamBuffers tuning parameters (experimental) - // -1 : PEX with all-to-all for buffer sizes and point-to-point - // for contents (legacy approach) - // 0 : hybrid PEX with NBX for buffer sizes and point-to-point - // for contents (proposed new approach) - // 1 : full NBX for buffer sizes and contents (very experimental) - pbufs.tuning -1; + // 0 : (legacy PEX) + // * all-to-all for buffer sizes [legacy approach] + // * point-to-point for contents + // 1 : (hybrid PEX) + // * NBX for buffer sizes [new approach] + // * point-to-point for contents + pbufs.tuning 0; // ===== diff --git a/src/OpenFOAM/db/IOstreams/Pstreams/Pstream.H b/src/OpenFOAM/db/IOstreams/Pstreams/Pstream.H index 32afe0be87..dbcfe69c56 100644 --- a/src/OpenFOAM/db/IOstreams/Pstreams/Pstream.H +++ b/src/OpenFOAM/db/IOstreams/Pstreams/Pstream.H @@ -599,7 +599,7 @@ public: // 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. // // Each entry of the recvBufs list is cleared before receipt. @@ -614,10 +614,10 @@ public: List& recvBufs, const int tag, 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. // // Each \em entry of the recvBufs map is cleared before receipt, @@ -636,7 +636,23 @@ public: Map& recvBufs, const int tag, 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 + static Map exchangeConsensus + ( + const Map& sendBufs, + const int tag, + const label comm, + const bool wait = true //!< (ignored) ); }; diff --git a/src/OpenFOAM/db/IOstreams/Pstreams/PstreamBuffers.C b/src/OpenFOAM/db/IOstreams/Pstreams/PstreamBuffers.C index 213969bd13..1949e7904c 100644 --- a/src/OpenFOAM/db/IOstreams/Pstreams/PstreamBuffers.C +++ b/src/OpenFOAM/db/IOstreams/Pstreams/PstreamBuffers.C @@ -36,7 +36,7 @@ License int Foam::PstreamBuffers::algorithm ( // Name may change in the future (JUN-2023) - Foam::debug::optimisationSwitch("pbufs.tuning", -1) + Foam::debug::optimisationSwitch("pbufs.tuning", 0) ); 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 * * * * * * * * * * * // +inline void Foam::PstreamBuffers::setFinished(bool on) noexcept +{ + finishedSendsCalled_ = on; +} + + inline void Foam::PstreamBuffers::initFinalExchange() { // Could also check that it is not called twice // but that is used for overlapping send/recv (eg, overset) - finishedSendsCalled_ = true; + setFinished(true); clearUnregistered(); } @@ -67,65 +66,149 @@ inline void Foam::PstreamBuffers::initFinalExchange() void Foam::PstreamBuffers::finalExchange ( + enum modeOption mode, const bool wait, labelList& recvSizes ) { initFinalExchange(); + // Pre-flight checks + switch (mode) + { + case modeOption::DEFAULT : + { + // Choose (ALL_TO_ALL | NBX_PEX) from static settings + mode = + ( + (algorithm <= 0) + ? modeOption::ALL_TO_ALL + : modeOption::NBX_PEX + ); + break; + } + + 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; + } + + if (commsType_ == UPstream::commsTypes::nonBlocking) { - if - ( - wait - && (algorithm >= algorithm_full_NBX) - && (UPstream::maxCommsSize <= 0) - ) + // PEX algorithm with different flavours of exchanging sizes + // PEX stage 1: exchange sizes + + labelList sendSizes; // Not used by gather/scatter + + switch (mode) { - // NBX algorithm (nonblocking exchange) - // - when requested and waiting, no data chunking etc + case modeOption::GATHER : + { + // gather mode (all-to-one): master [0] <- everyone + // - presumed that MPI_Gather will be the most efficient - PstreamDetail::exchangeConsensus, char> - ( - sendBuffers_, - recvBuffers_, - recvSizes, - (tag_ + 271828), // some unique tag? - comm_, - wait - ); + recvSizes = + UPstream::listGatherValues(sendBuffers_[0].size(), comm_); - return; + 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) + sendSizes.resize_nocopy(nProcs_); + forAll(sendBuffers_, proci) + { + sendSizes[proci] = sendBuffers_[proci].size(); + } + recvSizes.resize_nocopy(nProcs_); + + // Exchange sizes (non-blocking consensus) + UPstream::allToAllConsensus + ( + sendSizes, + recvSizes, + (tag_ + 314159), // some unique tag? + 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 algorithm with two different flavours of exchanging sizes - - // Assemble the send sizes (cf. Pstream::exchangeSizes) - labelList sendSizes(nProcs_); - forAll(sendBuffers_, proci) - { - sendSizes[proci] = sendBuffers_[proci].size(); - } - recvSizes.resize_nocopy(nProcs_); - - if (algorithm == algorithm_PEX_allToAll) - { - // PEX stage 1: exchange sizes (all-to-all) - UPstream::allToAll(sendSizes, recvSizes, comm_); - } - else - { - // PEX stage 1: exchange sizes (non-blocking consensus) - UPstream::allToAllConsensus - ( - sendSizes, - recvSizes, - (tag_ + 314159), // some unique tag? - comm_ - ); - } - // PEX stage 2: point-to-point data exchange Pstream::exchange, char> ( @@ -166,7 +249,7 @@ void Foam::PstreamBuffers::finalExchange recvSizes[proci] = 1; // Connected } - for (label proci=0; proci < nProcs_; ++proci) + for (label proci = 0; proci < nProcs_; ++proci) { if (!recvSizes[proci]) // Not connected { @@ -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, char> - ( - sendBuffers_, - recvSizes, - recvBuffers_, - tag_, - comm_, - wait - ); - } -} - - // * * * * * * * * * * * * * * * * Constructor * * * * * * * * * * * * * * * // Foam::PstreamBuffers::PstreamBuffers @@ -382,7 +378,7 @@ void Foam::PstreamBuffers::clear() { clearSends(); clearRecvs(); - finishedSendsCalled_ = false; + setFinished(false); } @@ -431,13 +427,13 @@ void Foam::PstreamBuffers::clearStorage() } recvPositions_ = Zero; - finishedSendsCalled_ = false; + setFinished(false); } void Foam::PstreamBuffers::initRegisterSend() { - if (!finishedSendsCalled_) + if (!finished()) { for (label proci = 0; proci < nProcs_; ++proci) { @@ -474,7 +470,7 @@ bool Foam::PstreamBuffers::hasSendData() const bool Foam::PstreamBuffers::hasRecvData() const { - if (finishedSendsCalled_) + if (finished()) { 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 { - if (finishedSendsCalled_) + if (finished()) { const label len(recvBuffers_[proci].size() - recvPositions_[proci]); @@ -529,7 +525,7 @@ Foam::labelList Foam::PstreamBuffers::recvDataCounts() const { labelList counts(nProcs_, Zero); - if (finishedSendsCalled_) + if (finished()) { forAll(recvBuffers_, proci) { @@ -560,7 +556,7 @@ Foam::label Foam::PstreamBuffers::maxNonLocalRecvCount { label maxLen = 0; - if (finishedSendsCalled_) + if (finished()) { forAll(recvBuffers_, proci) { @@ -599,7 +595,7 @@ Foam::label Foam::PstreamBuffers::maxNonLocalRecvCount() const const Foam::UList Foam::PstreamBuffers::peekRecvData(const label proci) const { - if (finishedSendsCalled_) + if (finished()) { const label pos = recvPositions_[proci]; 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) { 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 recvSizes.resize_nocopy(sendBuffers_.size()); - finalExchange(wait, recvSizes); + finalExchange(modeOption::DEFAULT, wait, recvSizes); if (commsType_ != UPstream::commsTypes::nonBlocking) { @@ -717,8 +712,9 @@ bool Foam::PstreamBuffers::finishedSends if (changed) { // Update send/recv topology + labelList recvSizes; - finishedSends(recvSizes, wait); // eg, using all-to-all + finishedSends(recvSizes, wait); // modeOption::DEFAULT (eg all-to-all) // The send ranks sendProcs.clear(); @@ -754,14 +750,14 @@ bool Foam::PstreamBuffers::finishedSends void Foam::PstreamBuffers::finishedGathers(const bool wait) { labelList recvSizes; - finalGatherScatter(true, wait, recvSizes); + finalExchange(modeOption::GATHER, wait, recvSizes); } void Foam::PstreamBuffers::finishedScatters(const bool wait) { labelList recvSizes; - finalGatherScatter(false, wait, recvSizes); + finalExchange(modeOption::SCATTER, wait, recvSizes); } @@ -771,7 +767,7 @@ void Foam::PstreamBuffers::finishedGathers const bool wait ) { - finalGatherScatter(true, wait, recvSizes); + finalExchange(modeOption::GATHER, wait, recvSizes); if (commsType_ != UPstream::commsTypes::nonBlocking) { @@ -793,7 +789,7 @@ void Foam::PstreamBuffers::finishedScatters const bool wait ) { - finalGatherScatter(false, wait, recvSizes); + finalExchange(modeOption::SCATTER, wait, recvSizes); 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; +} + + // ************************************************************************* // diff --git a/src/OpenFOAM/db/IOstreams/Pstreams/PstreamBuffers.H b/src/OpenFOAM/db/IOstreams/Pstreams/PstreamBuffers.H index fc7f6f3389..e61bf65e90 100644 --- a/src/OpenFOAM/db/IOstreams/Pstreams/PstreamBuffers.H +++ b/src/OpenFOAM/db/IOstreams/Pstreams/PstreamBuffers.H @@ -32,7 +32,7 @@ Description Use UOPstream to stream data into buffers, call finishedSends() to 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 explicit buffers. @@ -151,6 +151,19 @@ class bitSet; 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 //- Track if sends are complete @@ -190,20 +203,24 @@ class PstreamBuffers // Private Member Functions + //- Change status of finished sends called + inline void setFinished(bool on) noexcept; + //- Clear 'unregistered' send buffers, tag as being send-ready inline void initFinalExchange(); //- Mark all sends as having been done. - // This will start receives (nonBlocking comms). + // This will start receives (non-blocking comms). void finalExchange ( + enum modeOption mode, const bool wait, labelList& recvSizes ); //- Mark sends as done. // Only exchange sizes using the neighbour ranks - // (nonBlocking comms). + // (non-blocking comms). void finalExchange ( const labelUList& sendProcs, @@ -212,14 +229,6 @@ class PstreamBuffers labelList& recvSizes ); - //- For all-to-one or one-to-all - void finalGatherScatter - ( - const bool isGather, - const bool wait, - labelList& recvSizes - ); - // Friendship Access @@ -343,17 +352,11 @@ public: // Queries //- True if finishedSends() or finishedNeighbourSends() has been called - bool finished() const noexcept - { - return finishedSendsCalled_; - } + bool finished() const noexcept; //- Is clearStorage of individual receive buffer by external hooks //- allowed? (default: true) - bool allowClearRecv() const noexcept - { - return allowClearRecv_; - } + bool allowClearRecv() const noexcept; //- True if any (local) send buffers have data bool hasSendData() const; @@ -436,74 +439,96 @@ public: // Regular Functions - //- Mark sends as done + //- Mark the send phase as being finished. // - // Non-blocking mode: populates receive buffers (all-to-all). - // \param wait wait for requests to complete (in nonBlocking mode) + // Non-blocking mode: populates receive buffers using all-to-all + // or NBX (depending on tuning parameters). + // \param wait wait for requests to complete (in non-blocking mode) 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. // - // Non-blocking mode: populates receive buffers (all-to-all). - // \param[out] recvSizes the sizes (bytes) received - // \param wait wait for requests to complete (in nonBlocking mode) + // Non-blocking mode: populates receive buffers using all-to-all + // or NBX (depending on tuning parameters). + // \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. - void finishedSends(labelList& recvSizes, const bool wait = true); + // Non-blocking mode: populates receive buffers using NBX. + // \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 - //- Mark sends as done using subset of send/recv ranks - //- and recover the sizes (bytes) received. + //- Mark the send phase as being finished, with communication + //- being limited to a known subset of send/recv ranks. // // Non-blocking mode: populates receive buffers. // - // \param neighProcs ranks used for sends/recvs - // \param wait wait for requests to complete (in nonBlocking mode) - // - // \warning currently only valid for nonBlocking comms. + // \warning currently only valid for non-blocking comms. // \note Same as finishedSends with identical sendProcs/recvProcs void finishedNeighbourSends ( + //! ranks used for sends/recvs const labelUList& neighProcs, + //! wait for requests to complete (in non-blocking mode) const bool wait = true ); - //- Mark sends as done using subset of send/recv ranks - //- and recover the sizes (bytes) received. + //- Mark the send phase as being finished, with communication + //- being limited to a known subset of send/recv ranks. + //- Recovers the sizes (bytes) received. // // Non-blocking mode: it will populate receive buffers. // - // \param neighProcs ranks used for sends/recvs - // \param[out] recvSizes the sizes (bytes) received - // \param wait wait for requests to complete (in nonBlocking mode) - // - // \warning currently only valid for nonBlocking mode. + // \warning currently only valid for non-blocking mode. void finishedNeighbourSends ( + //! ranks used for sends/recvs const labelUList& neighProcs, + //! [out] the sizes (bytes) received labelList& recvSizes, + //! wait for requests to complete (in non-blocking mode) const bool wait = true ); //- A caching version that uses a limited send/recv connectivity. // // 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 // - // \warning currently only valid for nonBlocking comms. + // \warning currently only valid for non-blocking comms. bool finishedSends ( + //! inter-rank connections (on/off) for sending ranks bitSet& sendConnections, + //! ranks used for sends DynamicList