openfoam/applications/test/parallel-comm3b/Test-parallel-comm3b.C
Mark Olesen d9c73ae489 ENH: improve handling of multi-pass send/recv (#3152)
- the maxCommsSize variable is used to 'chunk' large data transfers
  (eg, with PstreamBuffers) into a multi-pass send/recv sequence.

  The send/recv windows for chunk-wise transfers:

      iter    data window
      ----    -----------
      0       [0, 1*chunk]
      1       [1*chunk, 2*chunk]
      2       [2*chunk, 3*chunk]
      ...

  Since we mostly send/recv in bytes, the current internal limit
  for MPI counts (INT_MAX) can be hit rather quickly.

  The chunking limit should thus also be INT_MAX, but since it is
  rather tedious to specify such large numbers, can instead use

      maxCommsSize = -1

  to specify (INT_MAX-1) as the limit.
  The default value of maxCommsSize = 0 (ie, no chunking).

Note
~~~~
  In previous versions, the number of chunks was determined by the
  sender sizes. This required an additional MPI_Allreduce to establish
  an overall consistent number of chunks to walk. This additional
  overhead each time meant that maxCommsSize was rarely actually
  enabled.

  We can, however, instead rely on the send/recv buffers having been
  consistently sized and simply walk through the local send/recvs until
  no further chunks need to be exchanged. As an additional enhancement,
  the message tags are connected to chunking iteration, which allows
  the setup of all send/recvs without an intermediate Allwait.

ENH: extend UPstream::probeMessage to use int64 instead of int for sizes
2024-05-07 15:33:02 +02:00

229 lines
5.7 KiB
C

/*---------------------------------------------------------------------------*\
========= |
\\ / F ield | OpenFOAM: The Open Source CFD Toolbox
\\ / O peration |
\\ / A nd | www.openfoam.com
\\/ M anipulation |
-------------------------------------------------------------------------------
Copyright (C) 2023 OpenCFD Ltd.
-------------------------------------------------------------------------------
License
This file is part of OpenFOAM.
OpenFOAM is free software: you can redistribute it and/or modify it
under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
OpenFOAM is distributed in the hope that it will be useful, but WITHOUT
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
for more details.
You should have received a copy of the GNU General Public License
along with OpenFOAM. If not, see <http://www.gnu.org/licenses/>.
Application
Test-parallel-comm3b
Description
Basic communicator tests
\*---------------------------------------------------------------------------*/
#include "argList.H"
#include "Time.H"
#include "IPstream.H"
#include "OPstream.H"
#include "Pair.H"
#include "Tuple2.H"
#include "IOstreams.H"
#include "StringStream.H"
#include "Random.H"
using namespace Foam;
void printRequests(const UList<UPstream::Request>& requests)
{
OStringStream buf;
buf << "request: " << requests.size() << '(';
for (const auto& req : requests)
{
if (req.good())
{
buf << " " << Foam::name(req.pointer());
}
else
{
buf << " null";
}
}
buf << " )";
Pout << buf.str().c_str() << endl;
}
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
int main(int argc, char *argv[])
{
argList::noBanner();
argList::noCheckProcessorDirectories();
#include "setRootCase.H"
if (!Pstream::parRun())
{
Info<< "\nWarning: not parallel - skipping further tests\n" << endl;
return 0;
}
const int tag = (UPstream::msgType() + 314159);
const label comm = UPstream::worldComm;
Random rnd(20*UPstream::myProcNo());
Map<DynamicList<char>> sendBufs;
Map<DynamicList<char>> recvBufs;
DynamicList<UPstream::Request> sendRequests(10);
DynamicList<UPstream::Request> recvRequests(10);
// Map request indices to procs
Map<label> recvFromProc(20);
if (!Pstream::master())
{
// Send some random length to master
const int toProci = UPstream::masterNo();
label len = rnd.position<label>(10, 20);
if (UPstream::myProcNo() && (UPstream::myProcNo() % 3) == 0) len = 0;
// Has data to send
if (len)
{
auto& buf = sendBufs(toProci);
buf.resize(len, 'x');
UOPstream::write
(
sendRequests.emplace_back(),
UPstream::masterNo(),
sendBufs[toProci],
tag,
comm,
UPstream::sendModes::sync
);
}
}
// Probe and receive
UPstream::Request barrierReq;
for (bool barrier_active = false, done = false; !done; /*nil*/)
{
std::pair<int, int64_t> probed =
UPstream::probeMessage
(
UPstream::commsTypes::nonBlocking,
-1, // ANY_SOURCE
tag,
comm
);
if (probed.second > 0)
{
// Message found and had size: receive it
const label proci(probed.first);
const label count(probed.second);
recvBufs(proci).resize_nocopy(count);
recvFromProc(recvRequests.size()) = proci;
// Non-blocking read
UIPstream::read
(
recvRequests.emplace_back(),
proci,
recvBufs[proci],
tag,
comm
);
}
if (barrier_active)
{
// Test barrier for completion
if (UPstream::finishedRequest(barrierReq))
{
done = true;
}
}
else
{
// Check if all sends have arrived
if (UPstream::finishedRequests(sendRequests))
{
UPstream::barrier(comm, &barrierReq);
barrier_active = true;
}
}
}
if (recvRequests.empty())
{
Pout << "No receive requests" << endl;
}
else
{
printRequests(recvRequests);
}
// Either MPI_Waitall, or MPI_Waitany...
label loop = 0;
for (bool dispatched = recvRequests.empty(); !dispatched; /*nil*/)
{
label index = UPstream::waitAnyRequest(recvRequests);
if (index < 0)
{
Pout<< "Waitany (loop:" << loop << ") : done" << endl;
dispatched = true;
}
else
{
Pout<< "Waitany (loop:"
<< loop << ") "
<< index << " of " << recvRequests.size()
<< " from proc:" << recvFromProc.lookup(index, -1)
<< endl;
printRequests(recvRequests);
}
++loop;
}
// Not needed: all tested...
// UPstream::waitRequest(recvRequests);
UPstream::barrier(UPstream::worldComm);
Info<< "\nEnd\n" << endl;
return 0;
}
// ************************************************************************* //