ENH: reduces buffer use / blocking in Ensight output

- reuse single component buffer within an Ensight output method.
  Use direct UPstream read/write to avoid Pstream char buffers

- replace blocking transfer with scheduled for Ensight cloud output
This commit is contained in:
Mark Olesen
2021-10-29 14:17:56 +02:00
parent 33ff3201ea
commit 048166c3d8
5 changed files with 302 additions and 216 deletions

View File

@ -50,6 +50,7 @@ SourceFiles
#include "ListOps.H" #include "ListOps.H"
#include "ListListOps.H" #include "ListListOps.H"
#include "IndirectList.H" #include "IndirectList.H"
#include "DynamicList.H"
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
@ -260,17 +261,27 @@ labelList getPolysNFaces(const polyMesh& mesh, const labelUList& addr);
labelList getPolysNPointsPerFace(const polyMesh& mesh, const labelUList& addr); labelList getPolysNPointsPerFace(const polyMesh& mesh, const labelUList& addr);
//- Copy specified field component into a scalarField //- Copy specified field component into a scalar buffer
// works for various lists types //- works for various lists types. Must be adequately sized before calling
template<template<typename> class FieldContainer, class Type> template<template<typename> class FieldContainer, class Type>
void copyComponent void copyComponent
( (
scalarField& res, List<scalar>& cmptBuffer,
const FieldContainer<Type>& input, const FieldContainer<Type>& input,
const direction cmpt const direction cmpt
); );
//- Write field content (component-wise)
template<template<typename> class FieldContainer, class Type>
void writeFieldContent
(
ensightFile& os,
const FieldContainer<Type>& fld,
bool parallel //!< Collective write?
);
//- Write coordinates (component-wise) for the given part //- Write coordinates (component-wise) for the given part
template<template<typename> class FieldContainer> template<template<typename> class FieldContainer>
bool writeCoordinates bool writeCoordinates

View File

@ -27,20 +27,27 @@ License
#include "ensightOutput.H" #include "ensightOutput.H"
#include "ensightPTraits.H" #include "ensightPTraits.H"
#include "globalIndex.H"
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
template<template<typename> class FieldContainer, class Type> template<template<typename> class FieldContainer, class Type>
void Foam::ensightOutput::Detail::copyComponent void Foam::ensightOutput::Detail::copyComponent
( (
scalarField& res, List<scalar>& cmptBuffer,
const FieldContainer<Type>& input, const FieldContainer<Type>& input,
const direction cmpt const direction cmpt
) )
{ {
res.resize(input.size()); if (cmptBuffer.size() < input.size())
{
FatalErrorInFunction
<< "Component buffer too small: "
<< cmptBuffer.size() << " < " << input.size() << nl
<< exit(FatalError);
}
auto iter = res.begin(); auto iter = cmptBuffer.begin();
for (const Type& val : input) for (const Type& val : input)
{ {
@ -50,6 +57,80 @@ void Foam::ensightOutput::Detail::copyComponent
} }
template<template<typename> class FieldContainer, class Type>
void Foam::ensightOutput::Detail::writeFieldContent
(
ensightFile& os,
const FieldContainer<Type>& fld,
bool parallel
)
{
// already checked prior to calling, but extra safety
parallel = parallel && Pstream::parRun();
// Size information (offsets are irrelevant)
globalIndex procAddr;
if (parallel)
{
procAddr.reset(UPstream::listGatherValues<label>(fld.size()));
}
else
{
// Master size
procAddr.reset(labelList(Foam::one{}, fld.size()));
}
if (Pstream::master())
{
DynamicList<scalar> cmptBuffer(procAddr.maxSize());
for (direction d=0; d < pTraits<Type>::nComponents; ++d)
{
const direction cmpt = ensightPTraits<Type>::componentOrder[d];
// Write master data
cmptBuffer.resize_nocopy(procAddr.localSize(0));
copyComponent(cmptBuffer, fld, cmpt);
os.writeList(cmptBuffer);
// Receive and write
for (const label proci : procAddr.subProcs())
{
cmptBuffer.resize_nocopy(procAddr.localSize(proci));
UIPstream::read
(
UPstream::commsTypes::scheduled,
proci,
cmptBuffer.data_bytes(),
cmptBuffer.size_bytes()
);
os.writeList(cmptBuffer);
}
}
}
else if (parallel)
{
// Send
List<scalar> cmptBuffer(fld.size());
for (direction d=0; d < pTraits<Type>::nComponents; ++d)
{
const direction cmpt = ensightPTraits<Type>::componentOrder[d];
copyComponent(cmptBuffer, fld, cmpt);
UOPstream::write
(
UPstream::commsTypes::scheduled,
Pstream::masterNo(),
cmptBuffer.cdata_bytes(),
cmptBuffer.size_bytes()
);
}
}
}
template<template<typename> class FieldContainer> template<template<typename> class FieldContainer>
bool Foam::ensightOutput::Detail::writeCoordinates bool Foam::ensightOutput::Detail::writeCoordinates
( (
@ -63,57 +144,13 @@ bool Foam::ensightOutput::Detail::writeCoordinates
{ {
parallel = parallel && Pstream::parRun(); parallel = parallel && Pstream::parRun();
const IntRange<int> senders =
(
parallel
? Pstream::subProcs()
: IntRange<int>()
);
// Using manual copyComponent(...) instead of fld.component() to support
// indirect lists etc.
scalarField send(fld.size());
if (Pstream::master()) if (Pstream::master())
{ {
os.beginPart(partId, partName); os.beginPart(partId, partName);
os.beginCoordinates(nPoints); os.beginCoordinates(nPoints);
for (direction cmpt=0; cmpt < point::nComponents; ++cmpt)
{
// Main
copyComponent(send, fld, cmpt);
os.writeList(send);
// Others
for (const int proci : senders)
{
IPstream fromOther(Pstream::commsTypes::scheduled, proci);
scalarField recv(fromOther);
os.writeList(recv);
}
}
} }
else if (senders)
{
// Send from other (parallel)
for (direction cmpt=0; cmpt < point::nComponents; ++cmpt) ensightOutput::Detail::writeFieldContent(os, fld, parallel);
{
copyComponent(send, fld, cmpt);
OPstream toMaster
(
Pstream::commsTypes::scheduled,
Pstream::masterNo()
);
toMaster << send;
}
}
return true; return true;
} }
@ -130,14 +167,6 @@ bool Foam::ensightOutput::Detail::writeFieldComponents
{ {
parallel = parallel && Pstream::parRun(); parallel = parallel && Pstream::parRun();
const IntRange<int> senders =
(
parallel
? Pstream::subProcs()
: IntRange<int>()
);
// Preliminary checks // Preliminary checks
{ {
bool hasField = !fld.empty(); bool hasField = !fld.empty();
@ -152,52 +181,12 @@ bool Foam::ensightOutput::Detail::writeFieldComponents
} }
// Using manual copyComponent(...) instead of fld.component() to support
// indirect lists etc.
scalarField send(fld.size());
if (Pstream::master()) if (Pstream::master())
{ {
os.writeKeyword(key); os.writeKeyword(key);
for (direction d=0; d < pTraits<Type>::nComponents; ++d)
{
const direction cmpt = ensightPTraits<Type>::componentOrder[d];
// Main
copyComponent(send, fld, cmpt);
os.writeList(send);
// Others
for (const int proci : senders)
{
IPstream fromOther(Pstream::commsTypes::scheduled, proci);
scalarField recv(fromOther);
os.writeList(recv);
}
}
} }
else if (senders)
{
// Send from other (parallel)
for (direction d=0; d < pTraits<Type>::nComponents; ++d) ensightOutput::Detail::writeFieldContent(os, fld, parallel);
{
const direction cmpt = ensightPTraits<Type>::componentOrder[d];
copyComponent(send, fld, cmpt);
OPstream toMaster
(
Pstream::commsTypes::scheduled,
Pstream::masterNo()
);
toMaster << send;
}
}
return true; return true;
} }
@ -294,6 +283,7 @@ bool Foam::ensightOutput::Detail::writeFaceLocalField
<< exit(FatalError); << exit(FatalError);
} }
if (Pstream::master()) if (Pstream::master())
{ {
os.beginPart(part.index()); os.beginPart(part.index());
@ -345,6 +335,7 @@ bool Foam::ensightOutput::writeField
if (!hasField) return false; if (!hasField) return false;
} }
if (Pstream::master()) if (Pstream::master())
{ {
os.beginPart(part.index()); os.beginPart(part.index());

View File

@ -5,7 +5,7 @@
\\ / A nd | www.openfoam.com \\ / A nd | www.openfoam.com
\\/ M anipulation | \\/ M anipulation |
------------------------------------------------------------------------------- -------------------------------------------------------------------------------
Copyright (C) 2016-2020 OpenCFD Ltd. Copyright (C) 2016-2021 OpenCFD Ltd.
------------------------------------------------------------------------------- -------------------------------------------------------------------------------
License License
This file is part of OpenFOAM. This file is part of OpenFOAM.
@ -29,17 +29,17 @@ License
#include "fvMesh.H" #include "fvMesh.H"
#include "Cloud.H" #include "Cloud.H"
#include "passiveParticle.H" #include "passiveParticle.H"
#include "pointField.H" #include "globalIndex.H"
// * * * * * * * * * * * * * * * Local Functions * * * * * * * * * * * * * * // // * * * * * * * * * * * * * * * Local Functions * * * * * * * * * * * * * * //
namespace Foam namespace Foam
{ {
//- Binary output //- Binary output
static inline void writeMeasured static inline void writeMeasured_binary
( (
ensightFile& os, ensightFile& os,
const pointField& points const UList<point>& points
) )
{ {
for (const point& p : points) for (const point& p : points)
@ -51,11 +51,11 @@ namespace Foam
} }
//- ASCII output. Id + position together //- ASCII output. Id + position together
static inline label writeMeasured static inline label writeMeasured_ascii
( (
ensightFile& os, ensightFile& os,
label pointId, label pointId,
const pointField& points const UList<point>& points
) )
{ {
for (const point& p : points) for (const point& p : points)
@ -80,17 +80,50 @@ bool Foam::ensightOutput::writeCloudPositions
const fvMesh& mesh, const fvMesh& mesh,
const word& cloudName, const word& cloudName,
bool exists, bool exists,
autoPtr<ensightFile>& output, autoPtr<ensightFile>& output
Pstream::commsTypes comm
) )
{ {
pointField positions; label nLocalParcels(0);
autoPtr<Cloud<passiveParticle>> parcelsPtr;
if (exists) if (exists)
{ {
Cloud<passiveParticle> parcels(mesh, cloudName, false); parcelsPtr.reset(new Cloud<passiveParticle>(mesh, cloudName, false));
nLocalParcels = parcelsPtr().size();
}
positions.resize(parcels.size()); // Total number of parcels on all processes
const label nTotParcels = returnReduce(nLocalParcels, sumOp<label>());
if (Pstream::master())
{
ensightFile& os = output();
os.beginParticleCoordinates(nTotParcels);
}
if (!nTotParcels)
{
return false; // DONE
}
// Size information (offsets are irrelevant)
const globalIndex procAddr
(
UPstream::listGatherValues<label>(nLocalParcels),
globalIndex::SIZES
);
DynamicList<point> positions;
positions.reserve(Pstream::master() ? procAddr.maxSize() : nLocalParcels);
// Extract positions
if (parcelsPtr)
{
const auto& parcels = *parcelsPtr;
positions.resize_nocopy(parcels.size()); // same as nLocalParcels
auto outIter = positions.begin(); auto outIter = positions.begin();
@ -99,75 +132,74 @@ bool Foam::ensightOutput::writeCloudPositions
*outIter = p.position(); *outIter = p.position();
++outIter; ++outIter;
} }
parcelsPtr.reset(nullptr);
} }
// Total number of parcels on all processes
const label nTotParcels = returnReduce(positions.size(), sumOp<label>());
// Update the exists/not exists information (for return value)
exists = nTotParcels;
if (Pstream::master()) if (Pstream::master())
{ {
ensightFile& os = output(); ensightFile& os = output();
const bool isBinaryOutput = (os.format() == IOstream::BINARY);
os.beginParticleCoordinates(nTotParcels); label parcelId = 0;
if (!exists)
{
return exists; // DONE
}
if (os.format() == IOstream::BINARY) if (isBinaryOutput)
{ {
// binary write is Ensight6 - first ids, then positions // NB: binary write is Ensight6 - first ids, then positions
// 1-index // 1-index
for (label parcelId = 1; parcelId <= nTotParcels; ++parcelId) for (label id = 1; id <= nTotParcels; ++id)
{ {
os.write(parcelId); os.write(id);
} }
// Master // Write master data
writeMeasured(os, positions); writeMeasured_binary(os, positions);
// Slaves
for (const int slave : Pstream::subProcs())
{
IPstream fromSlave(comm, slave);
pointField recv(fromSlave);
writeMeasured(os, recv);
}
} }
else else
{ {
// ASCII id + position together // NB: ascii write is (id + position) together
label parcelId = 0;
// Master // Write master data
parcelId = writeMeasured(os, parcelId, positions); parcelId = writeMeasured_ascii(os, parcelId, positions);
}
// Slaves
for (const int slave : Pstream::subProcs()) // Receive and write
for (const label proci : procAddr.subProcs())
{
positions.resize_nocopy(procAddr.localSize(proci));
UIPstream::read
(
UPstream::commsTypes::scheduled,
proci,
positions.data_bytes(),
positions.size_bytes()
);
if (isBinaryOutput)
{ {
IPstream fromSlave(comm, slave); writeMeasured_binary(os, positions);
pointField recv(fromSlave); }
else
parcelId = writeMeasured(os, parcelId, recv); {
parcelId = writeMeasured_ascii(os, parcelId, positions);
} }
} }
} }
else if (nTotParcels) else
{ {
// SLAVE, and data exist // Send
OPstream toMaster(comm, Pstream::masterNo()); UOPstream::write
(
toMaster UPstream::commsTypes::scheduled,
<< positions; Pstream::masterNo(),
positions.cdata_bytes(),
positions.size_bytes()
);
} }
return exists; return true;
} }

View File

@ -5,7 +5,7 @@
\\ / A nd | www.openfoam.com \\ / A nd | www.openfoam.com
\\/ M anipulation | \\/ M anipulation |
------------------------------------------------------------------------------- -------------------------------------------------------------------------------
Copyright (C) 2016-2019 OpenCFD Ltd. Copyright (C) 2016-2021 OpenCFD Ltd.
------------------------------------------------------------------------------- -------------------------------------------------------------------------------
License License
This file is part of OpenFOAM. This file is part of OpenFOAM.
@ -47,7 +47,7 @@ SourceFiles
namespace Foam namespace Foam
{ {
// Forward declarations // Forward Declarations
class fvMesh; class fvMesh;
namespace ensightOutput namespace ensightOutput
@ -62,8 +62,7 @@ bool writeCloudPositions
const fvMesh& mesh, const fvMesh& mesh,
const word& cloudName, const word& cloudName,
bool exists, bool exists,
autoPtr<ensightFile>& output, autoPtr<ensightFile>& output
Pstream::commsTypes comm = Pstream::commsTypes::blocking
); );
@ -72,8 +71,7 @@ template<class Type>
bool writeCloudField bool writeCloudField
( (
const IOField<Type>& field, const IOField<Type>& field,
ensightFile& os, ensightFile& os
Pstream::commsTypes comm = Pstream::commsTypes::blocking
); );
@ -84,11 +82,33 @@ bool writeCloudField
( (
const IOobject& io, const IOobject& io,
bool exists, bool exists,
autoPtr<ensightFile>& output, autoPtr<ensightFile>& output
Pstream::commsTypes comm = Pstream::commsTypes::blocking
); );
/*---------------------------------------------------------------------------*\
Namespace ensightOutput::Detail
\*---------------------------------------------------------------------------*/
namespace Detail
{
//- Write cloud field data (serial) with rounding and newlines.
// \return the current output count
template<class Type>
label writeCloudFieldContent
(
ensightFile& os,
const UList<Type>& fld,
label count = 0 //!< The current output count
);
} // End namespace Detail
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
} // End namespace ensightOutput } // End namespace ensightOutput
} // End namespace Foam } // End namespace Foam

View File

@ -27,6 +27,42 @@ License
#include "ensightOutputCloud.H" #include "ensightOutputCloud.H"
#include "ensightPTraits.H" #include "ensightPTraits.H"
#include "globalIndex.H"
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
template<class Type>
Foam::label Foam::ensightOutput::Detail::writeCloudFieldContent
(
ensightFile& os,
const UList<Type>& field,
label count
)
{
// Write master data
for (Type val : field) // <-- working on a copy!
{
if (mag(val) < 1e-90) // approximately root(ROOTVSMALL)
{
val = Zero;
}
for (direction d=0; d < pTraits<Type>::nComponents; ++d)
{
const direction cmpt = ensightPTraits<Type>::componentOrder[d];
os.write(component(val, cmpt));
if (++count % 6 == 0)
{
os.newline();
}
}
}
return count;
}
// * * * * * * * * * * * * * * * Global Functions * * * * * * * * * * * * * // // * * * * * * * * * * * * * * * Global Functions * * * * * * * * * * * * * //
@ -34,8 +70,7 @@ template<class Type>
bool Foam::ensightOutput::writeCloudField bool Foam::ensightOutput::writeCloudField
( (
const IOField<Type>& field, const IOField<Type>& field,
ensightFile& os, ensightFile& os
Pstream::commsTypes comm
) )
{ {
if (returnReduce(field.empty(), andOp<bool>())) if (returnReduce(field.empty(), andOp<bool>()))
@ -43,59 +78,51 @@ bool Foam::ensightOutput::writeCloudField
return false; return false;
} }
// Size information (offsets are irrelevant)
globalIndex procAddr;
if (Pstream::parRun())
{
procAddr.reset(UPstream::listGatherValues<label>(field.size()));
}
else
{
procAddr.reset(labelList(Foam::one{}, field.size()));
}
if (Pstream::master()) if (Pstream::master())
{ {
// 6 values per line // 6 values per line
label count = 0; label count = 0;
// Master // Write master data
for (Type val : field) // <-- working on a copy count = ensightOutput::Detail::writeCloudFieldContent
(
os,
field,
count
);
// Receive and write
DynamicList<Type> recvData(procAddr.maxNonLocalSize());
for (const label proci : procAddr.subProcs())
{ {
if (mag(val) < 1e-90) // approximately root(ROOTVSMALL) recvData.resize_nocopy(procAddr.localSize(proci));
{ UIPstream::read
val = Zero; (
} UPstream::commsTypes::scheduled,
proci,
recvData.data_bytes(),
recvData.size_bytes()
);
for (direction d=0; d < pTraits<Type>::nComponents; ++d) count = ensightOutput::Detail::writeCloudFieldContent
{ (
const direction cmpt = os,
ensightPTraits<Type>::componentOrder[d]; recvData,
count
os.write(component(val, cmpt)); );
if (++count % 6 == 0)
{
os.newline();
}
}
}
// Slaves
for (const int slave : Pstream::subProcs())
{
IPstream fromSlave(comm, slave);
Field<Type> recv(fromSlave);
for (Type val : recv) // <-- working on a copy
{
if (mag(val) < 1e-90) // approximately root(ROOTVSMALL)
{
val = Zero;
}
for (direction d=0; d < pTraits<Type>::nComponents; ++d)
{
const direction cmpt =
ensightPTraits<Type>::componentOrder[d];
os.write(component(val, cmpt));
if (++count % 6 == 0)
{
os.newline();
}
}
}
} }
// Add final newline if required // Add final newline if required
@ -106,8 +133,14 @@ bool Foam::ensightOutput::writeCloudField
} }
else else
{ {
OPstream toMaster(comm, Pstream::masterNo()); // Send
toMaster << field; UOPstream::write
(
UPstream::commsTypes::scheduled,
Pstream::masterNo(),
field.cdata_bytes(),
field.size_bytes()
);
} }
return true; return true;
@ -119,8 +152,7 @@ bool Foam::ensightOutput::writeCloudField
( (
const IOobject& io, const IOobject& io,
const bool exists, const bool exists,
autoPtr<ensightFile>& output, autoPtr<ensightFile>& output
Pstream::commsTypes comm
) )
{ {
if (exists) if (exists)
@ -134,7 +166,7 @@ bool Foam::ensightOutput::writeCloudField
IOField<Type> field(fieldObj); IOField<Type> field(fieldObj);
writeCloudField(field, output.ref(), comm); writeCloudField(field, output.ref());
} }
return true; return true;