2222
2323namespace
2424{
25+ struct ManagedBuffer
26+ {
27+ std::vector<char > m_Data;
28+ adios2::helper::Comm::Req m_Request;
29+ bool m_Active = false ;
30+ };
2531
2632class BufferPool
2733{
@@ -30,24 +36,54 @@ class BufferPool
3036
3137 ~BufferPool () = default ;
3238
33- std::vector< char > &GetNextBuffer ()
39+ ManagedBuffer &GetNextBuffer ()
3440 {
35- size_t bufferIdx = m_CurrentBufferIdx;
41+ size_t startIdx = m_CurrentBufferIdx;
3642
37- if (m_CurrentBufferIdx < m_Pool. size () - 1 )
43+ while ( true )
3844 {
39- m_CurrentBufferIdx += 1 ;
45+ ManagedBuffer &mb = m_Pool[m_CurrentBufferIdx];
46+
47+ // Advance index for next call
48+ m_CurrentBufferIdx = (m_CurrentBufferIdx + 1 ) % m_Pool.size ();
49+
50+ // If buffer was never used, it's safe
51+ if (!mb.m_Active )
52+ {
53+ return mb;
54+ }
55+
56+ // Check if the previous MPI_Isend using this buffer is complete
57+ if (mb.m_Request .Test ())
58+ {
59+ mb.m_Active = false ;
60+ return mb;
61+ }
62+
63+ // If we've circled the whole pool and none have completed, we MUST wait
64+ if (m_CurrentBufferIdx == startIdx)
65+ {
66+ mb.m_Request .Wait ();
67+ mb.m_Active = false ;
68+ return mb;
69+ }
4070 }
41- else
71+ }
72+
73+ void Flush ()
74+ {
75+ for (auto &mb : m_Pool)
4276 {
43- m_CurrentBufferIdx = 0 ;
77+ if (mb.m_Active )
78+ {
79+ mb.m_Request .Wait ();
80+ mb.m_Active = false ;
81+ }
4482 }
45-
46- return m_Pool[bufferIdx];
4783 }
4884
4985 size_t m_CurrentBufferIdx = 0 ;
50- std::vector<std::vector< char > > m_Pool;
86+ std::vector<ManagedBuffer > m_Pool;
5187};
5288
5389struct WriterGroupState
@@ -186,30 +222,7 @@ void BP5Writer::ReroutingCommunicationLoop()
186222 m_Profiler.AddTimerWatch (" Rerouting_Send_NB" );
187223 m_Profiler.AddTimerWatch (" Rerouting_Send_B" );
188224 m_Profiler.AddTimerWatch (" Rerouting_Recv_NB" );
189-
190- auto lf_SendNonBlocking = [](RerouteMessage &msg, adios2::helper::Comm &comm, int toRank,
191- std::vector<char > &buffer,
192- adios2::profiling::JSONProfiler &profiler) {
193- profiler.Start (" Rerouting_Send_NB" );
194- msg.NonBlockingSendTo (comm, toRank, buffer);
195- profiler.Stop (" Rerouting_Send_NB" );
196- };
197-
198- auto lf_SendBlocking = [](RerouteMessage &msg, adios2::helper::Comm &comm, int toRank,
199- std::vector<char > &buffer,
200- adios2::profiling::JSONProfiler &profiler) {
201- profiler.Start (" Rerouting_Send_B" );
202- msg.BlockingSendTo (comm, toRank, buffer);
203- profiler.Stop (" Rerouting_Send_B" );
204- };
205-
206- auto lf_RecvBlocking = [](RerouteMessage &msg, adios2::helper::Comm &comm, int fromRank,
207- std::vector<char > &buffer,
208- adios2::profiling::JSONProfiler &profiler) {
209- profiler.Start (" Rerouting_Recv_NB" );
210- msg.BlockingRecvFrom (comm, fromRank, buffer);
211- profiler.Stop (" Rerouting_Recv_NB" );
212- };
225+ m_Profiler.AddTimerWatch (" Rerouting_GetNextBuffer" );
213226
214227 int subCoord = m_Aggregator->m_AggregatorRank ;
215228 bool iAmSubCoord = m_RankMPI == subCoord;
@@ -234,8 +247,21 @@ void BP5Writer::ReroutingCommunicationLoop()
234247
235248 // Most sends are currently non-blocking. We use the pool to avoid a
236249 // situation where the buffer is destructed before the send is complete.
237- BufferPool sendBuffers (100 );
238- std::vector<char > recvBuffer;
250+ size_t poolSize = 1 ;
251+ if (iAmGlobalCoord)
252+ {
253+ // twice the number of subcoordinators
254+ poolSize = 2 * m_Partitioning.m_Partitions .size ();
255+ }
256+ else if (iAmSubCoord)
257+ {
258+ // twice the number of ranks in my writer chain/group
259+ poolSize = 2 * m_Partitioning.m_Partitions [m_Aggregator->m_SubStreamIndex ].size ();
260+ }
261+ BufferPool sendBuffers (poolSize);
262+
263+ std::vector<char > recvBuffer; // For blocking recvs
264+ std::vector<char > sendBuffer; // For blocking sends
239265 int writingRank = -1 ;
240266 uint64_t currentFilePos = 0 ;
241267 uint64_t writeMoreCount = 0 ;
@@ -247,6 +273,32 @@ void BP5Writer::ReroutingCommunicationLoop()
247273 bool expectingWriteCompletion = false ;
248274 bool sentIdle = false ;
249275
276+ auto lf_SendNonBlocking = [&sendBuffers](RerouteMessage &msg, adios2::helper::Comm &comm,
277+ int toRank,
278+ adios2::profiling::JSONProfiler &profiler) {
279+ profiler.Start (" Rerouting_Send_NB" );
280+ profiler.Start (" Rerouting_GetNextBuffer" );
281+ ManagedBuffer &buffer = sendBuffers.GetNextBuffer ();
282+ profiler.Stop (" Rerouting_GetNextBuffer" );
283+ buffer.m_Request = msg.NonBlockingSendTo (comm, toRank, buffer.m_Data );
284+ buffer.m_Active = true ;
285+ profiler.Stop (" Rerouting_Send_NB" );
286+ };
287+
288+ auto lf_SendBlocking = [&sendBuffer](RerouteMessage &msg, adios2::helper::Comm &comm,
289+ int toRank, adios2::profiling::JSONProfiler &profiler) {
290+ profiler.Start (" Rerouting_Send_B" );
291+ msg.BlockingSendTo (comm, toRank, sendBuffer);
292+ profiler.Stop (" Rerouting_Send_B" );
293+ };
294+
295+ auto lf_RecvBlocking = [&recvBuffer](RerouteMessage &msg, adios2::helper::Comm &comm,
296+ int fromRank, adios2::profiling::JSONProfiler &profiler) {
297+ profiler.Start (" Rerouting_Recv_NB" );
298+ msg.BlockingRecvFrom (comm, fromRank, recvBuffer);
299+ profiler.Stop (" Rerouting_Recv_NB" );
300+ };
301+
250302 if (iAmGlobalCoord)
251303 {
252304 // Global coordinator initializes the state it tracks for each subcoord
@@ -299,6 +351,7 @@ void BP5Writer::ReroutingCommunicationLoop()
299351
300352 while (lf_keepGoing ())
301353 {
354+ bool workDone = false ; // Flag to decide whether to yield on this iteration
302355 int msgReady = 0 ;
303356 helper::Comm::Status status =
304357 m_Comm.Iprobe (static_cast <int >(helper::Comm::Constants::CommRecvAny), 0 , &msgReady);
@@ -307,7 +360,8 @@ void BP5Writer::ReroutingCommunicationLoop()
307360 if (msgReady)
308361 {
309362 RerouteMessage message;
310- lf_RecvBlocking (message, m_Comm, status.Source , recvBuffer, m_Profiler);
363+ lf_RecvBlocking (message, m_Comm, status.Source , m_Profiler);
364+ workDone = true ;
311365
312366 switch ((RerouteMessage::MessageType)message.m_MsgType )
313367 {
@@ -383,8 +437,7 @@ void BP5Writer::ReroutingCommunicationLoop()
383437 closeAckMsg.m_SrcRank = m_RankMPI;
384438 closeAckMsg.m_DestRank = globalCoord;
385439 closeAckMsg.m_WildCard = static_cast <int >(m_Aggregator->m_SubStreamIndex );
386- lf_SendBlocking (closeAckMsg, m_Comm, globalCoord, sendBuffers.GetNextBuffer (),
387- m_Profiler);
440+ lf_SendBlocking (closeAckMsg, m_Comm, globalCoord, m_Profiler);
388441 break ;
389442 case RerouteMessage::MessageType::GROUP_CLOSE_ACK :
390443 // msg for global coordinator
@@ -433,8 +486,7 @@ void BP5Writer::ReroutingCommunicationLoop()
433486 inquiryMsg.m_MsgType = RerouteMessage::MessageType::STATUS_INQUIRY ;
434487 inquiryMsg.m_SrcRank = m_RankMPI;
435488 inquiryMsg.m_DestRank = scRank;
436- lf_SendNonBlocking (inquiryMsg, m_Comm, scRank,
437- sendBuffers.GetNextBuffer (), m_Profiler);
489+ lf_SendNonBlocking (inquiryMsg, m_Comm, scRank, m_Profiler);
438490 }
439491 }
440492
@@ -471,8 +523,7 @@ void BP5Writer::ReroutingCommunicationLoop()
471523 // The response to the status query is my subfile and queue size
472524 replyMsg.m_WildCard = static_cast <int >(m_Aggregator->m_SubStreamIndex );
473525 replyMsg.m_Size = static_cast <uint64_t >(writerQueue.size ());
474- lf_SendNonBlocking (replyMsg, m_Comm, globalCoord, sendBuffers.GetNextBuffer (),
475- m_Profiler);
526+ lf_SendNonBlocking (replyMsg, m_Comm, globalCoord, m_Profiler);
476527 break ;
477528 case RerouteMessage::MessageType::STATUS_REPLY :
478529 if (m_Parameters.verbose > 3 )
@@ -507,8 +558,7 @@ void BP5Writer::ReroutingCommunicationLoop()
507558 rejectMsg.m_MsgType = RerouteMessage::MessageType::REROUTE_REJECT ;
508559 rejectMsg.m_SrcRank = message.m_SrcRank ;
509560 rejectMsg.m_DestRank = message.m_DestRank ;
510- lf_SendNonBlocking (rejectMsg, m_Comm, globalCoord, sendBuffers.GetNextBuffer (),
511- m_Profiler);
561+ lf_SendNonBlocking (rejectMsg, m_Comm, globalCoord, m_Profiler);
512562 }
513563 else
514564 {
@@ -527,8 +577,7 @@ void BP5Writer::ReroutingCommunicationLoop()
527577 ackMsg.m_SrcRank = message.m_SrcRank ;
528578 ackMsg.m_DestRank = message.m_DestRank ;
529579 ackMsg.m_WildCard = reroutedRank;
530- lf_SendNonBlocking (ackMsg, m_Comm, globalCoord, sendBuffers.GetNextBuffer (),
531- m_Profiler);
580+ lf_SendNonBlocking (ackMsg, m_Comm, globalCoord, m_Profiler);
532581 }
533582 break ;
534583 case RerouteMessage::MessageType::REROUTE_REJECT :
@@ -579,8 +628,7 @@ void BP5Writer::ReroutingCommunicationLoop()
579628 adios2::helper::RerouteMessage writeMoreMsg;
580629 writeMoreMsg.m_MsgType = RerouteMessage::MessageType::WRITE_MORE ;
581630 writeMoreMsg.m_WildCard = message.m_WildCard ; // i.e. the rerouted writer rank
582- lf_SendNonBlocking (writeMoreMsg, m_Comm, message.m_DestRank ,
583- sendBuffers.GetNextBuffer (), m_Profiler);
631+ lf_SendNonBlocking (writeMoreMsg, m_Comm, message.m_DestRank , m_Profiler);
584632
585633 groupIdlesNeeded.insert (message.m_DestRank );
586634
@@ -643,8 +691,7 @@ void BP5Writer::ReroutingCommunicationLoop()
643691 // done at this point. However, I need to do a blocking send because I
644692 // am about to return from this function, at which point my buffer pool
645693 // goes away.
646- lf_SendBlocking (writeCompleteMsg, m_Comm, m_TargetCoordinator,
647- sendBuffers.GetNextBuffer (), m_Profiler);
694+ lf_SendBlocking (writeCompleteMsg, m_Comm, m_TargetCoordinator, m_Profiler);
648695
649696 receivedGroupClose = true ;
650697 continue ;
@@ -657,10 +704,10 @@ void BP5Writer::ReroutingCommunicationLoop()
657704 << m_TargetCoordinator << " ) of write completion -- NONBLOCKING"
658705 << std::endl;
659706 }
660- lf_SendNonBlocking (writeCompleteMsg, m_Comm, m_TargetCoordinator,
661- sendBuffers.GetNextBuffer (), m_Profiler);
707+ lf_SendNonBlocking (writeCompleteMsg, m_Comm, m_TargetCoordinator, m_Profiler);
662708 }
663709
710+ workDone = true ;
664711 sentFinished = true ;
665712 }
666713 }
@@ -679,16 +726,34 @@ void BP5Writer::ReroutingCommunicationLoop()
679726 << std::endl;
680727 }
681728 writerQueue.pop ();
682- adios2::helper::RerouteMessage writeMsg;
683- writeMsg.m_MsgType = RerouteMessage::MessageType::DO_WRITE ;
684- writeMsg.m_SrcRank = m_RankMPI;
685- writeMsg.m_DestRank = nextWriter;
686- writeMsg.m_WildCard = static_cast <int >(m_Aggregator->m_SubStreamIndex );
687- writeMsg.m_Offset = currentFilePos;
688- writingRank = nextWriter;
689- expectingWriteCompletion = true ;
690- lf_SendNonBlocking (writeMsg, m_Comm, nextWriter, sendBuffers.GetNextBuffer (),
691- m_Profiler);
729+
730+ if (nextWriter == m_RankMPI)
731+ {
732+ // Don't use MPI to tell ourselves to write
733+ std::unique_lock<std::mutex> lck (m_WriteMutex);
734+ m_TargetIndex = static_cast <int >(m_Aggregator->m_SubStreamIndex );
735+ m_DataPos = currentFilePos;
736+ m_TargetCoordinator = m_RankMPI;
737+ m_ReadyToWrite = true ;
738+ m_WriteCV.notify_one ();
739+
740+ writingRank = m_RankMPI;
741+ expectingWriteCompletion = true ;
742+ }
743+ else
744+ {
745+ adios2::helper::RerouteMessage writeMsg;
746+ writeMsg.m_MsgType = RerouteMessage::MessageType::DO_WRITE ;
747+ writeMsg.m_SrcRank = m_RankMPI;
748+ writeMsg.m_DestRank = nextWriter;
749+ writeMsg.m_WildCard = static_cast <int >(m_Aggregator->m_SubStreamIndex );
750+ writeMsg.m_Offset = currentFilePos;
751+ writingRank = nextWriter;
752+ expectingWriteCompletion = true ;
753+ lf_SendNonBlocking (writeMsg, m_Comm, nextWriter, m_Profiler);
754+ }
755+
756+ workDone = true ;
692757 }
693758 else if (!sentIdle)
694759 {
@@ -713,9 +778,9 @@ void BP5Writer::ReroutingCommunicationLoop()
713778 idleMsg.m_SrcRank = m_RankMPI;
714779 idleMsg.m_DestRank = globalCoord;
715780 idleMsg.m_WildCard = static_cast <int >(m_Aggregator->m_SubStreamIndex );
716- lf_SendNonBlocking (idleMsg, m_Comm, globalCoord, sendBuffers.GetNextBuffer (),
717- m_Profiler);
781+ lf_SendNonBlocking (idleMsg, m_Comm, globalCoord, m_Profiler);
718782 sentIdle = true ;
783+ workDone = true ;
719784 }
720785 }
721786
@@ -751,11 +816,12 @@ void BP5Writer::ReroutingCommunicationLoop()
751816 rerouteReqMsg.m_MsgType = RerouteMessage::MessageType::REROUTE_REQUEST ;
752817 rerouteReqMsg.m_SrcRank = writerSubcoordRank;
753818 rerouteReqMsg.m_DestRank = idleSubcoordRank;
754- lf_SendNonBlocking (rerouteReqMsg, m_Comm, writerSubcoordRank,
755- sendBuffers.GetNextBuffer (), m_Profiler);
819+ lf_SendNonBlocking (rerouteReqMsg, m_Comm, writerSubcoordRank, m_Profiler);
756820
757821 groupState[idleIdx].m_currentStatus = WriterGroupState::Status::PENDING ;
758822 groupState[writerIdx].m_currentStatus = WriterGroupState::Status::PENDING ;
823+
824+ workDone = true ;
759825 }
760826 else if (result == StateTraversal::SearchResult::FINISHED && groupIdlesNeeded.empty ())
761827 {
@@ -785,8 +851,7 @@ void BP5Writer::ReroutingCommunicationLoop()
785851 }
786852 adios2::helper::RerouteMessage closeMsg;
787853 closeMsg.m_MsgType = RerouteMessage::MessageType::GROUP_CLOSE ;
788- lf_SendNonBlocking (closeMsg, m_Comm, subCoordRanks[scIdx],
789- sendBuffers.GetNextBuffer (), m_Profiler);
854+ lf_SendNonBlocking (closeMsg, m_Comm, subCoordRanks[scIdx], m_Profiler);
790855 }
791856 }
792857
@@ -858,6 +923,12 @@ void BP5Writer::ReroutingCommunicationLoop()
858923 }
859924 }
860925 }
926+
927+ if (!workDone)
928+ {
929+ // Yield to avoid busy-cycling the CPU when nothing productive was done
930+ std::this_thread::yield ();
931+ }
861932 }
862933
863934 // Before leaving this method, subcoordinators need to update the variable tracking
@@ -867,6 +938,15 @@ void BP5Writer::ReroutingCommunicationLoop()
867938 m_DataPos = currentFilePos;
868939 }
869940
941+ if (m_Parameters.verbose > 3 )
942+ {
943+ std::cout << " Rank " << m_RankMPI << " flushing pending sends..." << std::endl;
944+ }
945+
946+ // Ensure all ISends associated with buffers in the pool are complete
947+ // before we let the pool go out of scope
948+ sendBuffers.Flush ();
949+
870950 if (m_Parameters.verbose > 3 )
871951 {
872952 std::cout << " Rank " << m_RankMPI << " Exit ReroutingCommunicationLoop" << std::endl;
0 commit comments