mirror of
https://develop.openfoam.com/Development/openfoam.git
synced 2025-11-28 03:28:01 +00:00
ENH: reduce overhead of masterOFstream
- use OCharStream instead of OStringStream to avoid copying char data. - replace PstreamBuffers mechanism with a direct non-blocking PEX algorithm, which avoids unnecessary serialization/de-serialization of char data. Also reduces memory footprint and allocations somewhat. - polling dispatch to write file contents as they become available
This commit is contained in:
@ -6,7 +6,7 @@
|
|||||||
\\/ M anipulation |
|
\\/ M anipulation |
|
||||||
-------------------------------------------------------------------------------
|
-------------------------------------------------------------------------------
|
||||||
Copyright (C) 2017 OpenFOAM Foundation
|
Copyright (C) 2017 OpenFOAM Foundation
|
||||||
Copyright (C) 2020-2023 OpenCFD Ltd.
|
Copyright (C) 2020-2025 OpenCFD Ltd.
|
||||||
-------------------------------------------------------------------------------
|
-------------------------------------------------------------------------------
|
||||||
License
|
License
|
||||||
This file is part of OpenFOAM.
|
This file is part of OpenFOAM.
|
||||||
@ -29,7 +29,7 @@ License
|
|||||||
#include "masterOFstream.H"
|
#include "masterOFstream.H"
|
||||||
#include "OFstream.H"
|
#include "OFstream.H"
|
||||||
#include "OSspecific.H"
|
#include "OSspecific.H"
|
||||||
#include "PstreamBuffers.H"
|
#include "Pstream.H"
|
||||||
#include "masterUncollatedFileOperation.H"
|
#include "masterUncollatedFileOperation.H"
|
||||||
|
|
||||||
// * * * * * * * * * * * * * Private Member Functions * * * * * * * * * * * //
|
// * * * * * * * * * * * * * Private Member Functions * * * * * * * * * * * //
|
||||||
@ -41,9 +41,9 @@ void Foam::masterOFstream::checkWrite
|
|||||||
std::streamsize len
|
std::streamsize len
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
if (!len)
|
if (!str || !(len > 0))
|
||||||
{
|
{
|
||||||
// Can probably skip all of this if there is nothing to write
|
// Can skip everything if there is nothing to write
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -63,9 +63,7 @@ void Foam::masterOFstream::checkWrite
|
|||||||
<< exit(FatalIOError);
|
<< exit(FatalIOError);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Use writeRaw() instead of writeQuoted(string,false) to output
|
// Write characters directly to std::ostream
|
||||||
// characters directly.
|
|
||||||
|
|
||||||
os.writeRaw(str, len);
|
os.writeRaw(str, len);
|
||||||
|
|
||||||
if (!os.good())
|
if (!os.good())
|
||||||
@ -77,24 +75,29 @@ void Foam::masterOFstream::checkWrite
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void Foam::masterOFstream::checkWrite
|
|
||||||
(
|
|
||||||
const fileName& fName,
|
|
||||||
const std::string& s
|
|
||||||
)
|
|
||||||
{
|
|
||||||
checkWrite(fName, s.data(), s.length());
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void Foam::masterOFstream::commit()
|
void Foam::masterOFstream::commit()
|
||||||
{
|
{
|
||||||
if (UPstream::parRun())
|
// Take ownership of serialized content
|
||||||
|
DynamicList<char> charData(OCharStream::release());
|
||||||
|
|
||||||
|
if (!UPstream::parRun())
|
||||||
{
|
{
|
||||||
|
// Write (non-empty) data
|
||||||
|
checkWrite(pathName_, charData);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Ignore content if not writing
|
||||||
|
if (!writeOnProc_)
|
||||||
|
{
|
||||||
|
charData.clear();
|
||||||
|
}
|
||||||
|
|
||||||
List<fileName> filePaths(UPstream::nProcs(comm_));
|
List<fileName> filePaths(UPstream::nProcs(comm_));
|
||||||
filePaths[UPstream::myProcNo(comm_)] = pathName_;
|
filePaths[UPstream::myProcNo(comm_)] = pathName_;
|
||||||
Pstream::gatherList(filePaths, UPstream::msgType(), comm_);
|
Pstream::gatherList(filePaths, UPstream::msgType(), comm_);
|
||||||
|
|
||||||
|
// Test for identical output paths
|
||||||
bool uniform =
|
bool uniform =
|
||||||
(
|
(
|
||||||
UPstream::master(comm_)
|
UPstream::master(comm_)
|
||||||
@ -105,69 +108,136 @@ void Foam::masterOFstream::commit()
|
|||||||
|
|
||||||
if (uniform)
|
if (uniform)
|
||||||
{
|
{
|
||||||
|
// Identical file paths - write on master
|
||||||
if (UPstream::master(comm_) && writeOnProc_)
|
if (UPstream::master(comm_) && writeOnProc_)
|
||||||
{
|
{
|
||||||
checkWrite(pathName_, this->str());
|
checkWrite(pathName_, charData);
|
||||||
}
|
}
|
||||||
|
|
||||||
this->reset();
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Different files
|
// Different files
|
||||||
PstreamBuffers pBufs(comm_);
|
// ---------------
|
||||||
|
//
|
||||||
|
// Non-sparse (most ranks have writeOnProc_ == true),
|
||||||
|
// so gather sizes first and use PEX-like handling,
|
||||||
|
// with polling for when data becomes available.
|
||||||
|
//
|
||||||
|
// Could also consider double buffering + write to reduce
|
||||||
|
// memory overhead.
|
||||||
|
|
||||||
if (!UPstream::master(comm_))
|
// Or int64_t
|
||||||
{
|
const label dataSize =
|
||||||
if (writeOnProc_)
|
(
|
||||||
{
|
(UPstream::is_subrank(comm_) && writeOnProc_)
|
||||||
// Send buffer to master
|
? charData.size()
|
||||||
string s(this->str());
|
: 0
|
||||||
|
);
|
||||||
|
|
||||||
UOPstream os(UPstream::masterNo(), pBufs);
|
const labelList recvSizes
|
||||||
os.write(s.data(), s.length());
|
(
|
||||||
}
|
UPstream::listGatherValues<label>(dataSize, comm_)
|
||||||
this->reset(); // Done with contents
|
);
|
||||||
}
|
|
||||||
|
|
||||||
pBufs.finishedGathers();
|
|
||||||
|
|
||||||
|
// Receive from these procs
|
||||||
|
DynamicList<int> recvProcs;
|
||||||
|
|
||||||
if (UPstream::master(comm_))
|
if (UPstream::master(comm_))
|
||||||
{
|
{
|
||||||
if (writeOnProc_)
|
// Sorted by message size
|
||||||
|
labelList order(Foam::sortedOrder(recvSizes));
|
||||||
|
recvProcs.reserve_exact(order.size());
|
||||||
|
|
||||||
|
// Want to receive large messages first. Ignore empty slots
|
||||||
|
forAllReverse(order, i)
|
||||||
{
|
{
|
||||||
// Write master data
|
const label proci = order[i];
|
||||||
checkWrite(filePaths[UPstream::masterNo()], this->str());
|
|
||||||
}
|
|
||||||
this->reset(); // Done with contents
|
|
||||||
|
|
||||||
|
// Ignore empty slots
|
||||||
// Allocate large enough to read without resizing
|
if (recvSizes[proci] > 0)
|
||||||
List<char> buf(pBufs.maxRecvCount());
|
|
||||||
|
|
||||||
for (const int proci : UPstream::subProcs(comm_))
|
|
||||||
{
|
|
||||||
const std::streamsize count(pBufs.recvDataCount(proci));
|
|
||||||
|
|
||||||
if (count)
|
|
||||||
{
|
{
|
||||||
UIPstream is(proci, pBufs);
|
recvProcs.push_back(proci);
|
||||||
|
|
||||||
is.read(buf.data(), count);
|
|
||||||
checkWrite(filePaths[proci], buf.cdata(), count);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
checkWrite(pathName_, this->str());
|
|
||||||
this->reset();
|
|
||||||
}
|
|
||||||
|
|
||||||
// This method is only called once (internally)
|
// Non-blocking communication
|
||||||
// so no need to clear/flush old buffered data
|
const label startOfRequests = UPstream::nRequests();
|
||||||
|
|
||||||
|
// Some unique tag for this read/write grouping (extra precaution)
|
||||||
|
const int messageTag = (UPstream::msgType() + 256);
|
||||||
|
|
||||||
|
if (UPstream::is_subrank(comm_) && dataSize > 0)
|
||||||
|
{
|
||||||
|
// Send to content to master
|
||||||
|
UOPstream::write
|
||||||
|
(
|
||||||
|
UPstream::commsTypes::nonBlocking,
|
||||||
|
UPstream::masterNo(),
|
||||||
|
charData.cdata_bytes(),
|
||||||
|
charData.size_bytes(),
|
||||||
|
messageTag,
|
||||||
|
comm_
|
||||||
|
);
|
||||||
|
}
|
||||||
|
else if (UPstream::master(comm_))
|
||||||
|
{
|
||||||
|
// The receive slots
|
||||||
|
List<List<char>> recvBuffers(UPstream::nProcs(comm_));
|
||||||
|
|
||||||
|
// Receive from these procs (non-empty slots)
|
||||||
|
for (const int proci : recvProcs)
|
||||||
|
{
|
||||||
|
auto& slot = recvBuffers[proci];
|
||||||
|
slot.resize_nocopy(recvSizes[proci]);
|
||||||
|
|
||||||
|
// Receive content
|
||||||
|
UIPstream::read
|
||||||
|
(
|
||||||
|
UPstream::commsTypes::nonBlocking,
|
||||||
|
proci,
|
||||||
|
slot.data_bytes(),
|
||||||
|
slot.size_bytes(),
|
||||||
|
messageTag,
|
||||||
|
comm_
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (writeOnProc_)
|
||||||
|
{
|
||||||
|
// Write non-empty master data
|
||||||
|
checkWrite(pathName_, charData);
|
||||||
|
charData.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Poll for completed receive requests and dispatch
|
||||||
|
DynamicList<int> indices(recvProcs.size());
|
||||||
|
while
|
||||||
|
(
|
||||||
|
UPstream::waitSomeRequests
|
||||||
|
(
|
||||||
|
startOfRequests,
|
||||||
|
recvProcs.size(),
|
||||||
|
&indices
|
||||||
|
)
|
||||||
|
)
|
||||||
|
{
|
||||||
|
for (const int i : indices)
|
||||||
|
{
|
||||||
|
const int proci = recvProcs[i];
|
||||||
|
auto& slot = recvBuffers[proci];
|
||||||
|
|
||||||
|
// Write non-empty sub-proc data
|
||||||
|
checkWrite(filePaths[proci], slot);
|
||||||
|
|
||||||
|
// Eager cleanup
|
||||||
|
slot.clear();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
UPstream::waitRequests(startOfRequests);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -176,21 +246,24 @@ void Foam::masterOFstream::commit()
|
|||||||
Foam::masterOFstream::masterOFstream
|
Foam::masterOFstream::masterOFstream
|
||||||
(
|
(
|
||||||
IOstreamOption::atomicType atomic,
|
IOstreamOption::atomicType atomic,
|
||||||
const label comm,
|
const int communicator,
|
||||||
const fileName& pathName,
|
const fileName& pathName,
|
||||||
IOstreamOption streamOpt,
|
IOstreamOption streamOpt,
|
||||||
IOstreamOption::appendType append,
|
IOstreamOption::appendType append,
|
||||||
const bool writeOnProc
|
const bool writeOnProc
|
||||||
)
|
)
|
||||||
:
|
:
|
||||||
OStringStream(streamOpt),
|
OCharStream(streamOpt),
|
||||||
pathName_(pathName),
|
pathName_(pathName),
|
||||||
atomic_(atomic),
|
atomic_(atomic),
|
||||||
compression_(streamOpt.compression()),
|
compression_(streamOpt.compression()),
|
||||||
append_(append),
|
append_(append),
|
||||||
writeOnProc_(writeOnProc),
|
writeOnProc_(writeOnProc),
|
||||||
comm_(comm)
|
comm_(communicator < 0 ? UPstream::worldComm : communicator)
|
||||||
{}
|
{
|
||||||
|
// Start with a slightly larger buffer
|
||||||
|
OCharStream::reserve(4*1024);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
// * * * * * * * * * * * * * * * * Destructor * * * * * * * * * * * * * * * //
|
// * * * * * * * * * * * * * * * * Destructor * * * * * * * * * * * * * * * //
|
||||||
|
|||||||
@ -6,7 +6,7 @@
|
|||||||
\\/ M anipulation |
|
\\/ M anipulation |
|
||||||
-------------------------------------------------------------------------------
|
-------------------------------------------------------------------------------
|
||||||
Copyright (C) 2017 OpenFOAM Foundation
|
Copyright (C) 2017 OpenFOAM Foundation
|
||||||
Copyright (C) 2020-2023 OpenCFD Ltd.
|
Copyright (C) 2020-2025 OpenCFD Ltd.
|
||||||
-------------------------------------------------------------------------------
|
-------------------------------------------------------------------------------
|
||||||
License
|
License
|
||||||
This file is part of OpenFOAM.
|
This file is part of OpenFOAM.
|
||||||
@ -41,7 +41,7 @@ SourceFiles
|
|||||||
#ifndef Foam_masterOFstream_H
|
#ifndef Foam_masterOFstream_H
|
||||||
#define Foam_masterOFstream_H
|
#define Foam_masterOFstream_H
|
||||||
|
|
||||||
#include "StringStream.H"
|
#include "SpanStream.H"
|
||||||
#include "UPstream.H"
|
#include "UPstream.H"
|
||||||
|
|
||||||
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
|
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
|
||||||
@ -55,7 +55,7 @@ namespace Foam
|
|||||||
|
|
||||||
class masterOFstream
|
class masterOFstream
|
||||||
:
|
:
|
||||||
public OStringStream
|
public OCharStream
|
||||||
{
|
{
|
||||||
// Private Data
|
// Private Data
|
||||||
|
|
||||||
@ -80,7 +80,8 @@ class masterOFstream
|
|||||||
|
|
||||||
// Private Member Functions
|
// Private Member Functions
|
||||||
|
|
||||||
//- Open file with checking and write append contents
|
//- Open file with checking and write append contents.
|
||||||
|
// A no-op if str is null or len is zero
|
||||||
void checkWrite
|
void checkWrite
|
||||||
(
|
(
|
||||||
const fileName& fName,
|
const fileName& fName,
|
||||||
@ -89,9 +90,16 @@ class masterOFstream
|
|||||||
);
|
);
|
||||||
|
|
||||||
//- Open file with checking and write append contents
|
//- Open file with checking and write append contents
|
||||||
void checkWrite(const fileName& fName, const std::string& s);
|
void checkWrite
|
||||||
|
(
|
||||||
|
const fileName& fName,
|
||||||
|
const UList<char>& charData
|
||||||
|
)
|
||||||
|
{
|
||||||
|
checkWrite(fName, charData.cdata(), charData.size_bytes());
|
||||||
|
}
|
||||||
|
|
||||||
//- Commit buffered information, including parallel gather as required
|
//- Commit buffered information, including communication as required
|
||||||
void commit();
|
void commit();
|
||||||
|
|
||||||
|
|
||||||
@ -104,7 +112,8 @@ public:
|
|||||||
masterOFstream
|
masterOFstream
|
||||||
(
|
(
|
||||||
IOstreamOption::atomicType atomic,
|
IOstreamOption::atomicType atomic,
|
||||||
const label comm,
|
//! The communicator number (-1 == worldComm)
|
||||||
|
const int communicator,
|
||||||
const fileName& pathname,
|
const fileName& pathname,
|
||||||
IOstreamOption streamOpt = IOstreamOption(),
|
IOstreamOption streamOpt = IOstreamOption(),
|
||||||
IOstreamOption::appendType append = IOstreamOption::NO_APPEND,
|
IOstreamOption::appendType append = IOstreamOption::NO_APPEND,
|
||||||
@ -115,7 +124,8 @@ public:
|
|||||||
//- from pathname, stream option, optional append
|
//- from pathname, stream option, optional append
|
||||||
masterOFstream
|
masterOFstream
|
||||||
(
|
(
|
||||||
const label comm,
|
//! The communicator number (-1 == worldComm)
|
||||||
|
const int communicator,
|
||||||
const fileName& pathname,
|
const fileName& pathname,
|
||||||
IOstreamOption streamOpt = IOstreamOption(),
|
IOstreamOption streamOpt = IOstreamOption(),
|
||||||
IOstreamOption::appendType append = IOstreamOption::NO_APPEND,
|
IOstreamOption::appendType append = IOstreamOption::NO_APPEND,
|
||||||
@ -125,7 +135,7 @@ public:
|
|||||||
masterOFstream
|
masterOFstream
|
||||||
(
|
(
|
||||||
IOstreamOption::NON_ATOMIC,
|
IOstreamOption::NON_ATOMIC,
|
||||||
comm,
|
communicator,
|
||||||
pathname,
|
pathname,
|
||||||
streamOpt,
|
streamOpt,
|
||||||
append,
|
append,
|
||||||
|
|||||||
Reference in New Issue
Block a user