mirror of
https://develop.openfoam.com/Development/openfoam.git
synced 2025-11-28 03:28:01 +00:00
Merge branch 'feature-profiling-summary' into 'develop'
minor improvements in profiling See merge request Development/OpenFOAM-plus!178
This commit is contained in:
@ -40,7 +40,7 @@ using namespace Foam;
|
|||||||
int main(int argc, char *argv[])
|
int main(int argc, char *argv[])
|
||||||
{
|
{
|
||||||
const int n = 10000000;
|
const int n = 10000000;
|
||||||
const char* const memTags = "peak/size/rss mem: ";
|
const char* const memTags = "peak/size/rss/free mem: ";
|
||||||
|
|
||||||
memInfo mem;
|
memInfo mem;
|
||||||
|
|
||||||
|
|||||||
@ -30,6 +30,7 @@ Description
|
|||||||
#include "profilingSysInfo.H"
|
#include "profilingSysInfo.H"
|
||||||
#include "IOstreams.H"
|
#include "IOstreams.H"
|
||||||
#include "endian.H"
|
#include "endian.H"
|
||||||
|
#include "cpuInfo.H"
|
||||||
|
|
||||||
using namespace Foam;
|
using namespace Foam;
|
||||||
|
|
||||||
@ -38,7 +39,9 @@ using namespace Foam;
|
|||||||
|
|
||||||
int main(int argc, char *argv[])
|
int main(int argc, char *argv[])
|
||||||
{
|
{
|
||||||
profiling::sysInfo().write(Info);
|
profilingSysInfo().write(Info);
|
||||||
|
|
||||||
|
cpuInfo().write(Info);
|
||||||
|
|
||||||
#ifdef WM_BIG_ENDIAN
|
#ifdef WM_BIG_ENDIAN
|
||||||
Info
|
Info
|
||||||
|
|||||||
@ -0,0 +1,3 @@
|
|||||||
|
profilingSummary.C
|
||||||
|
|
||||||
|
EXE = $(FOAM_APPBIN)/profilingSummary
|
||||||
@ -0,0 +1,3 @@
|
|||||||
|
EXE_INC =
|
||||||
|
|
||||||
|
EXE_LIBS =
|
||||||
@ -0,0 +1,414 @@
|
|||||||
|
/*---------------------------------------------------------------------------*\
|
||||||
|
========= |
|
||||||
|
\\ / F ield | OpenFOAM: The Open Source CFD Toolbox
|
||||||
|
\\ / O peration |
|
||||||
|
\\ / A nd | Copyright (C) 2017 OpenCFD Ltd.
|
||||||
|
\\/ M anipulation |
|
||||||
|
-------------------------------------------------------------------------------
|
||||||
|
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
|
||||||
|
profilingSummary
|
||||||
|
|
||||||
|
Group
|
||||||
|
grpMiscUtilities
|
||||||
|
|
||||||
|
Description
|
||||||
|
Collects information from profiling files in the processor
|
||||||
|
sub-directories and summarizes the number of calls and time spent as
|
||||||
|
max/avg/min values. If the values are identical for all processes,
|
||||||
|
only a single value is written.
|
||||||
|
|
||||||
|
\*---------------------------------------------------------------------------*/
|
||||||
|
|
||||||
|
#include "Time.H"
|
||||||
|
#include "polyMesh.H"
|
||||||
|
#include "OSspecific.H"
|
||||||
|
#include "IFstream.H"
|
||||||
|
#include "OFstream.H"
|
||||||
|
#include "argList.H"
|
||||||
|
#include "stringOps.H"
|
||||||
|
#include "timeSelector.H"
|
||||||
|
#include "IOobjectList.H"
|
||||||
|
|
||||||
|
using namespace Foam;
|
||||||
|
|
||||||
|
// The name of the sub-dictionary entry for profiling fileName:
|
||||||
|
static const word profilingFileName("profiling");
|
||||||
|
|
||||||
|
// The name of the sub-dictionary entry for profiling:
|
||||||
|
static const word blockNameProfiling("profiling");
|
||||||
|
|
||||||
|
// The name of the sub-dictionary entry for profiling and tags of entries
|
||||||
|
// that will be processed to determine (max,avg,min) values
|
||||||
|
const HashTable<wordList> processing
|
||||||
|
{
|
||||||
|
{ "profiling", { "calls", "totalTime", "childTime", "maxMem" } },
|
||||||
|
{ "memInfo", { "size", "free" } },
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
|
||||||
|
|
||||||
|
int main(int argc, char *argv[])
|
||||||
|
{
|
||||||
|
argList::addNote
|
||||||
|
(
|
||||||
|
"Collect profiling information from processor directories and\n"
|
||||||
|
"summarize the time spent and number of calls as (max avg min) values."
|
||||||
|
);
|
||||||
|
|
||||||
|
timeSelector::addOptions(true, true);
|
||||||
|
argList::noParallel();
|
||||||
|
argList::noFunctionObjects();
|
||||||
|
|
||||||
|
// Note that this should work without problems when profiling is active,
|
||||||
|
// since we don't trigger it anywhere
|
||||||
|
|
||||||
|
#include "setRootCase.H"
|
||||||
|
#include "createTime.H"
|
||||||
|
|
||||||
|
// Determine the processor count
|
||||||
|
#ifdef fileOperation_H
|
||||||
|
const label nProcs = fileHandler().nProcs(args.path());
|
||||||
|
#else
|
||||||
|
label nProcs = 0;
|
||||||
|
while (isDir(args.path()/(word("processor") + name(nProcs))))
|
||||||
|
{
|
||||||
|
++nProcs;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// Create the processor databases
|
||||||
|
PtrList<Time> databases(nProcs);
|
||||||
|
|
||||||
|
forAll(databases, proci)
|
||||||
|
{
|
||||||
|
databases.set
|
||||||
|
(
|
||||||
|
proci,
|
||||||
|
new Time
|
||||||
|
(
|
||||||
|
Time::controlDictName,
|
||||||
|
args.rootPath(),
|
||||||
|
args.caseName()/fileName(word("processor") + name(proci))
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!nProcs)
|
||||||
|
{
|
||||||
|
FatalErrorInFunction
|
||||||
|
<< "No processor* directories found"
|
||||||
|
<< exit(FatalError);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Use the times list from the master processor
|
||||||
|
// and select a subset based on the command-line options
|
||||||
|
instantList timeDirs = timeSelector::select
|
||||||
|
(
|
||||||
|
databases[0].times(),
|
||||||
|
args
|
||||||
|
);
|
||||||
|
|
||||||
|
if (timeDirs.empty())
|
||||||
|
{
|
||||||
|
WarningInFunction
|
||||||
|
<< "No times selected" << nl << endl;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------
|
||||||
|
|
||||||
|
// Processor local profiling information
|
||||||
|
List<dictionary> profiles(nProcs);
|
||||||
|
|
||||||
|
// Loop over all times
|
||||||
|
forAll(timeDirs, timei)
|
||||||
|
{
|
||||||
|
// Set time for global database
|
||||||
|
runTime.setTime(timeDirs[timei], timei);
|
||||||
|
|
||||||
|
Info<< "Time = " << runTime.timeName() << endl;
|
||||||
|
|
||||||
|
// Name/location for the output summary
|
||||||
|
const fileName outputName
|
||||||
|
{
|
||||||
|
"postProcessing",
|
||||||
|
"profiling",
|
||||||
|
runTime.timeName(),
|
||||||
|
profilingFileName
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
label nDict = 0;
|
||||||
|
|
||||||
|
// Set time for all databases
|
||||||
|
forAll(databases, proci)
|
||||||
|
{
|
||||||
|
profiles[proci].clear();
|
||||||
|
databases[proci].setTime(timeDirs[timei], timei);
|
||||||
|
|
||||||
|
// Look for "uniform/profiling" in each processor directory
|
||||||
|
IOobjectList objects
|
||||||
|
(
|
||||||
|
databases[proci].time(),
|
||||||
|
databases[proci].timeName(),
|
||||||
|
"uniform"
|
||||||
|
);
|
||||||
|
|
||||||
|
IOobject* ioptr = objects.lookup(profilingFileName);
|
||||||
|
if (ioptr)
|
||||||
|
{
|
||||||
|
IOdictionary dict(*ioptr);
|
||||||
|
|
||||||
|
// Full copy
|
||||||
|
profiles[proci] = dict;
|
||||||
|
|
||||||
|
// Assumed to be good if it has 'profiling' sub-dict
|
||||||
|
|
||||||
|
const dictionary* ptr = dict.subDictPtr(blockNameProfiling);
|
||||||
|
if (ptr)
|
||||||
|
{
|
||||||
|
++nDict;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (nDict < proci)
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (nDict != nProcs)
|
||||||
|
{
|
||||||
|
Info<< "found " << nDict << "/" << nProcs
|
||||||
|
<< " profiling files" << nl << endl;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Information seems to be there for all processors
|
||||||
|
// can do a summary
|
||||||
|
|
||||||
|
IOdictionary summary
|
||||||
|
(
|
||||||
|
IOobject
|
||||||
|
(
|
||||||
|
runTime.path()/outputName,
|
||||||
|
runTime,
|
||||||
|
IOobject::NO_READ,
|
||||||
|
IOobject::NO_WRITE,
|
||||||
|
false, // no register
|
||||||
|
true // global-like
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
summary.note() =
|
||||||
|
(
|
||||||
|
"summarized (max avg min) values from "
|
||||||
|
+ Foam::name(nProcs) + " processors"
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
// Accumulator for each tag
|
||||||
|
HashTable<DynamicList<scalar>> stats;
|
||||||
|
|
||||||
|
// Use first as 'master' to decide what others have
|
||||||
|
forAllConstIters(profiles.first(), mainIter)
|
||||||
|
{
|
||||||
|
const entry& mainEntry = mainIter();
|
||||||
|
|
||||||
|
// level1: eg, profiling {} or memInfo {}
|
||||||
|
const word& level1Name = mainEntry.keyword();
|
||||||
|
|
||||||
|
if
|
||||||
|
(
|
||||||
|
!processing.found(level1Name)
|
||||||
|
|| !mainEntry.isDict()
|
||||||
|
|| mainEntry.dict().empty()
|
||||||
|
)
|
||||||
|
{
|
||||||
|
continue; // Only process known types
|
||||||
|
}
|
||||||
|
|
||||||
|
const wordList& tags = processing[level1Name];
|
||||||
|
|
||||||
|
const dictionary& level1Dict = mainEntry.dict();
|
||||||
|
|
||||||
|
// We need to handle sub-dicts with other dicts
|
||||||
|
// Eg, trigger0 { .. } trigger1 { .. }
|
||||||
|
//
|
||||||
|
// and ones with primitives
|
||||||
|
// Eg, size xx; free yy;
|
||||||
|
|
||||||
|
// Decide based on the first entry:
|
||||||
|
|
||||||
|
// level2: eg, profiling { trigger0 { } }
|
||||||
|
// or simply itself it contains primitives only
|
||||||
|
|
||||||
|
wordList level2Names;
|
||||||
|
|
||||||
|
const bool hasDictEntries
|
||||||
|
= mainEntry.dict().first()->isDict();
|
||||||
|
|
||||||
|
if (hasDictEntries)
|
||||||
|
{
|
||||||
|
level2Names =
|
||||||
|
mainEntry.dict().sortedToc(stringOps::natural_sort());
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
level2Names = {level1Name};
|
||||||
|
}
|
||||||
|
|
||||||
|
summary.set(level1Name, dictionary());
|
||||||
|
|
||||||
|
dictionary& outputDict = summary.subDict(level1Name);
|
||||||
|
|
||||||
|
for (const word& level2Name : level2Names)
|
||||||
|
{
|
||||||
|
// Presize everything
|
||||||
|
stats.clear();
|
||||||
|
for (const word& tag : tags)
|
||||||
|
{
|
||||||
|
stats(tag).reserve(nProcs);
|
||||||
|
}
|
||||||
|
|
||||||
|
label nEntry = 0;
|
||||||
|
|
||||||
|
for (const dictionary& procDict : profiles)
|
||||||
|
{
|
||||||
|
const dictionary* inDictPtr =
|
||||||
|
procDict.subDictPtr(level1Name);
|
||||||
|
|
||||||
|
if (inDictPtr && hasDictEntries)
|
||||||
|
{
|
||||||
|
// descend to the next level as required
|
||||||
|
inDictPtr = inDictPtr->subDictPtr(level2Name);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!inDictPtr)
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
++nEntry;
|
||||||
|
|
||||||
|
for (const word& tag : tags)
|
||||||
|
{
|
||||||
|
const entry* eptr = inDictPtr->lookupEntryPtr
|
||||||
|
(
|
||||||
|
tag,
|
||||||
|
false,
|
||||||
|
false
|
||||||
|
);
|
||||||
|
|
||||||
|
if (eptr)
|
||||||
|
{
|
||||||
|
const scalar val = readScalar(eptr->stream());
|
||||||
|
stats(tag).append(val);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (nEntry != nProcs)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
dictionary* outDictPtr = nullptr;
|
||||||
|
|
||||||
|
// Make a full copy of this entry prior to editing it
|
||||||
|
if (hasDictEntries)
|
||||||
|
{
|
||||||
|
outputDict.add(level2Name, level1Dict.subDict(level2Name));
|
||||||
|
outDictPtr = outputDict.subDictPtr(level2Name);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// merge into existing (empty) dictionary
|
||||||
|
summary.add(level1Name, level1Dict, true);
|
||||||
|
outDictPtr = &outputDict;
|
||||||
|
}
|
||||||
|
|
||||||
|
dictionary& outSubDict = *outDictPtr;
|
||||||
|
|
||||||
|
// Remove trailing 'processor0' from any descriptions
|
||||||
|
// (looks nicer)
|
||||||
|
{
|
||||||
|
const word key("description");
|
||||||
|
string val;
|
||||||
|
|
||||||
|
if (outSubDict.readIfPresent(key, val))
|
||||||
|
{
|
||||||
|
if (val.removeEnd("processor0"))
|
||||||
|
{
|
||||||
|
outSubDict.set(key, val);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Process each tag (calls, time etc)
|
||||||
|
for (const word& tag : tags)
|
||||||
|
{
|
||||||
|
DynamicList<scalar>& lst = stats(tag);
|
||||||
|
|
||||||
|
if (lst.size() == nProcs)
|
||||||
|
{
|
||||||
|
sort(lst);
|
||||||
|
const scalar avg = sum(lst) / nProcs;
|
||||||
|
|
||||||
|
if (lst.first() != lst.last())
|
||||||
|
{
|
||||||
|
outSubDict.set
|
||||||
|
(
|
||||||
|
tag,
|
||||||
|
FixedList<scalar, 3>
|
||||||
|
{
|
||||||
|
lst.last(), avg, lst.first()
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Now write the summary
|
||||||
|
{
|
||||||
|
mkDir(summary.path());
|
||||||
|
|
||||||
|
OFstream os(summary.objectPath());
|
||||||
|
|
||||||
|
summary.writeHeader(os);
|
||||||
|
summary.writeData(os);
|
||||||
|
summary.writeEndDivider(os);
|
||||||
|
|
||||||
|
Info<< "Wrote to " << outputName << nl << endl;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Info<< "End\n" << endl;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// ************************************************************************* //
|
||||||
@ -113,10 +113,8 @@ void Foam::cpuInfo::parse()
|
|||||||
std::string line, key, val;
|
std::string line, key, val;
|
||||||
|
|
||||||
std::ifstream is("/proc/cpuinfo");
|
std::ifstream is("/proc/cpuinfo");
|
||||||
while (is.good())
|
while (is.good() && std::getline(is, line))
|
||||||
{
|
{
|
||||||
std::getline(is, line);
|
|
||||||
|
|
||||||
if (!split(line, key, val))
|
if (!split(line, key, val))
|
||||||
{
|
{
|
||||||
continue;
|
continue;
|
||||||
@ -156,12 +154,6 @@ Foam::cpuInfo::cpuInfo()
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// * * * * * * * * * * * * * * * * Destructor * * * * * * * * * * * * * * * //
|
|
||||||
|
|
||||||
Foam::cpuInfo::~cpuInfo()
|
|
||||||
{}
|
|
||||||
|
|
||||||
|
|
||||||
// * * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * //
|
// * * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * //
|
||||||
|
|
||||||
void Foam::cpuInfo::write(Ostream& os) const
|
void Foam::cpuInfo::write(Ostream& os) const
|
||||||
|
|||||||
@ -88,9 +88,8 @@ public:
|
|||||||
//- Construct and populate with information
|
//- Construct and populate with information
|
||||||
cpuInfo();
|
cpuInfo();
|
||||||
|
|
||||||
|
|
||||||
//- Destructor
|
//- Destructor
|
||||||
~cpuInfo();
|
~cpuInfo() = default;
|
||||||
|
|
||||||
|
|
||||||
// Member Functions
|
// Member Functions
|
||||||
|
|||||||
@ -36,83 +36,14 @@ Foam::memInfo::memInfo()
|
|||||||
:
|
:
|
||||||
peak_(0),
|
peak_(0),
|
||||||
size_(0),
|
size_(0),
|
||||||
rss_(0)
|
rss_(0),
|
||||||
|
free_(0)
|
||||||
{
|
{
|
||||||
update();
|
update();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// * * * * * * * * * * * * * * * * Destructor * * * * * * * * * * * * * * * //
|
|
||||||
|
|
||||||
Foam::memInfo::~memInfo()
|
|
||||||
{}
|
|
||||||
|
|
||||||
|
|
||||||
// * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * * //
|
// * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * * //
|
||||||
//
|
|
||||||
// Parse the following type of content.
|
|
||||||
//
|
|
||||||
// ===========================
|
|
||||||
// VmPeak: 15920 kB
|
|
||||||
// VmSize: 15916 kB
|
|
||||||
// VmLck: 0 kB
|
|
||||||
// VmPin: 0 kB
|
|
||||||
// VmHWM: 6972 kB
|
|
||||||
// VmRSS: 6972 kB
|
|
||||||
// VmLib: 2208 kB
|
|
||||||
// VmPTE: 52 kB
|
|
||||||
// VmPMD: 12 kB
|
|
||||||
// VmSwap: 0 kB
|
|
||||||
|
|
||||||
const Foam::memInfo& Foam::memInfo::update()
|
|
||||||
{
|
|
||||||
// Clear (invalidate) values first
|
|
||||||
peak_ = size_ = rss_ = 0;
|
|
||||||
std::string line;
|
|
||||||
|
|
||||||
unsigned nKeys = 0;
|
|
||||||
|
|
||||||
std::ifstream is("/proc/" + std::to_string(Foam::pid()) + "/status");
|
|
||||||
while (is.good() && nKeys < 3) // Stop after getting the known keys
|
|
||||||
{
|
|
||||||
std::getline(is, line);
|
|
||||||
|
|
||||||
const auto keyLen = line.find(':');
|
|
||||||
if (keyLen == std::string::npos)
|
|
||||||
{
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Value is after the ':', but skip any leading whitespace since
|
|
||||||
// strtoi will do it anyhow
|
|
||||||
const auto begVal = line.find_first_not_of("\t :", keyLen);
|
|
||||||
if (begVal == std::string::npos)
|
|
||||||
{
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
const std::string key = line.substr(0, keyLen);
|
|
||||||
|
|
||||||
if (key == "VmPeak")
|
|
||||||
{
|
|
||||||
peak_ = std::stoi(line.substr(begVal));
|
|
||||||
++nKeys;
|
|
||||||
}
|
|
||||||
else if (key == "VmSize")
|
|
||||||
{
|
|
||||||
size_ = std::stoi(line.substr(begVal));
|
|
||||||
++nKeys;
|
|
||||||
}
|
|
||||||
else if (key == "VmRSS")
|
|
||||||
{
|
|
||||||
rss_ = std::stoi(line.substr(begVal));
|
|
||||||
++nKeys;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
bool Foam::memInfo::valid() const
|
bool Foam::memInfo::valid() const
|
||||||
{
|
{
|
||||||
@ -120,11 +51,116 @@ bool Foam::memInfo::valid() const
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void Foam::memInfo::clear()
|
||||||
|
{
|
||||||
|
peak_ = size_ = rss_ = 0;
|
||||||
|
free_ = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
const Foam::memInfo& Foam::memInfo::update()
|
||||||
|
{
|
||||||
|
clear();
|
||||||
|
std::string line;
|
||||||
|
|
||||||
|
// "/proc/PID/status"
|
||||||
|
// ===========================
|
||||||
|
// VmPeak: 15920 kB
|
||||||
|
// VmSize: 15916 kB
|
||||||
|
// VmLck: 0 kB
|
||||||
|
// VmPin: 0 kB
|
||||||
|
// VmHWM: 6972 kB
|
||||||
|
// VmRSS: 6972 kB
|
||||||
|
// ...
|
||||||
|
// Stop parsing when known keys have been extracted
|
||||||
|
{
|
||||||
|
std::ifstream is("/proc/" + std::to_string(Foam::pid()) + "/status");
|
||||||
|
|
||||||
|
for
|
||||||
|
(
|
||||||
|
unsigned nkeys = 3;
|
||||||
|
nkeys && is.good() && std::getline(is, line);
|
||||||
|
/*nil*/
|
||||||
|
)
|
||||||
|
{
|
||||||
|
const auto delim = line.find(':');
|
||||||
|
if (delim == std::string::npos)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
const std::string key(line.substr(0, delim));
|
||||||
|
|
||||||
|
// std::stoi() skips whitespace before using as many digits as
|
||||||
|
// possible. So just need to skip over the ':' and let stoi do
|
||||||
|
// the rest
|
||||||
|
|
||||||
|
if (key == "VmPeak")
|
||||||
|
{
|
||||||
|
peak_ = std::stoi(line.substr(delim+1));
|
||||||
|
--nkeys;
|
||||||
|
}
|
||||||
|
else if (key == "VmSize")
|
||||||
|
{
|
||||||
|
size_ = std::stoi(line.substr(delim+1));
|
||||||
|
--nkeys;
|
||||||
|
}
|
||||||
|
else if (key == "VmRSS")
|
||||||
|
{
|
||||||
|
rss_ = std::stoi(line.substr(delim+1));
|
||||||
|
--nkeys;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// "/proc/meminfo"
|
||||||
|
// ===========================
|
||||||
|
// MemTotal: 65879268 kB
|
||||||
|
// MemFree: 51544256 kB
|
||||||
|
// MemAvailable: 58999636 kB
|
||||||
|
// Buffers: 2116 kB
|
||||||
|
// ...
|
||||||
|
// Stop parsing when known keys have been extracted
|
||||||
|
{
|
||||||
|
std::ifstream is("/proc/meminfo");
|
||||||
|
|
||||||
|
for
|
||||||
|
(
|
||||||
|
unsigned nkeys = 1;
|
||||||
|
nkeys && is.good() && std::getline(is, line);
|
||||||
|
/*nil*/
|
||||||
|
)
|
||||||
|
{
|
||||||
|
const auto delim = line.find(':');
|
||||||
|
if (delim == std::string::npos)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
const std::string key = line.substr(0, delim);
|
||||||
|
|
||||||
|
// std::stoi() skips whitespace before using as many digits as
|
||||||
|
// possible. So just need to skip over the ':' and let stoi do
|
||||||
|
// the rest
|
||||||
|
|
||||||
|
if (key == "MemFree")
|
||||||
|
{
|
||||||
|
free_ = std::stoi(line.substr(delim+1));
|
||||||
|
--nkeys;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
void Foam::memInfo::write(Ostream& os) const
|
void Foam::memInfo::write(Ostream& os) const
|
||||||
{
|
{
|
||||||
os.writeEntry("size", size_);
|
os.writeEntry("size", size_);
|
||||||
os.writeEntry("peak", peak_);
|
os.writeEntry("peak", peak_);
|
||||||
os.writeEntry("rss", rss_);
|
os.writeEntry("rss", rss_);
|
||||||
|
os.writeEntry("free", free_);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -133,7 +169,7 @@ void Foam::memInfo::write(Ostream& os) const
|
|||||||
Foam::Istream& Foam::operator>>(Istream& is, memInfo& m)
|
Foam::Istream& Foam::operator>>(Istream& is, memInfo& m)
|
||||||
{
|
{
|
||||||
is.readBegin("memInfo");
|
is.readBegin("memInfo");
|
||||||
is >> m.peak_ >> m.size_ >> m.rss_;
|
is >> m.peak_ >> m.size_ >> m.rss_ >> m.free_;
|
||||||
is.readEnd("memInfo");
|
is.readEnd("memInfo");
|
||||||
|
|
||||||
is.check(FUNCTION_NAME);
|
is.check(FUNCTION_NAME);
|
||||||
@ -146,7 +182,8 @@ Foam::Ostream& Foam::operator<<(Ostream& os, const memInfo& m)
|
|||||||
os << token::BEGIN_LIST
|
os << token::BEGIN_LIST
|
||||||
<< m.peak_ << token::SPACE
|
<< m.peak_ << token::SPACE
|
||||||
<< m.size_ << token::SPACE
|
<< m.size_ << token::SPACE
|
||||||
<< m.rss_
|
<< m.rss_ << token::SPACE
|
||||||
|
<< m.free_
|
||||||
<< token::END_LIST;
|
<< token::END_LIST;
|
||||||
|
|
||||||
os.check(FUNCTION_NAME);
|
os.check(FUNCTION_NAME);
|
||||||
|
|||||||
@ -3,7 +3,7 @@
|
|||||||
\\ / F ield | OpenFOAM: The Open Source CFD Toolbox
|
\\ / F ield | OpenFOAM: The Open Source CFD Toolbox
|
||||||
\\ / O peration |
|
\\ / O peration |
|
||||||
\\ / A nd | Copyright (C) 2011-2016 OpenFOAM Foundation
|
\\ / A nd | Copyright (C) 2011-2016 OpenFOAM Foundation
|
||||||
\\/ M anipulation | Copyright (C) 2016 OpenCFD Ltd.
|
\\/ M anipulation | Copyright (C) 2016-2017 OpenCFD Ltd.
|
||||||
-------------------------------------------------------------------------------
|
-------------------------------------------------------------------------------
|
||||||
License
|
License
|
||||||
This file is part of OpenFOAM.
|
This file is part of OpenFOAM.
|
||||||
@ -25,10 +25,11 @@ Class
|
|||||||
Foam::memInfo
|
Foam::memInfo
|
||||||
|
|
||||||
Description
|
Description
|
||||||
Memory usage information for the process running this object.
|
Memory usage information for the current process, and the system memory
|
||||||
|
that is free.
|
||||||
|
|
||||||
Note
|
Note
|
||||||
Uses the information from /proc/PID/status
|
Uses the information from /proc/PID/status and from /proc/meminfo
|
||||||
|
|
||||||
SourceFiles
|
SourceFiles
|
||||||
memInfo.C
|
memInfo.C
|
||||||
@ -69,26 +70,33 @@ class memInfo
|
|||||||
//- Resident set size of the process (VmRSS in /proc/PID/status)
|
//- Resident set size of the process (VmRSS in /proc/PID/status)
|
||||||
int rss_;
|
int rss_;
|
||||||
|
|
||||||
|
//- System memory free (MemFree in /proc/meminfo)
|
||||||
|
int free_;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
|
||||||
// Constructors
|
// Constructors
|
||||||
|
|
||||||
//- Construct null
|
//- Construct and populate with values
|
||||||
memInfo();
|
memInfo();
|
||||||
|
|
||||||
|
|
||||||
//- Destructor
|
//- Destructor
|
||||||
~memInfo();
|
~memInfo() = default;
|
||||||
|
|
||||||
|
|
||||||
// Member Functions
|
// Member Functions
|
||||||
|
|
||||||
//- Update according to /proc/PID/status contents
|
//- True if the memory information appears valid
|
||||||
|
bool valid() const;
|
||||||
|
|
||||||
|
//- Reset to zero
|
||||||
|
void clear();
|
||||||
|
|
||||||
|
//- Update according to /proc/PID/status and /proc/memory contents
|
||||||
const memInfo& update();
|
const memInfo& update();
|
||||||
|
|
||||||
|
|
||||||
// Access
|
|
||||||
|
|
||||||
//- Peak memory (VmPeak in /proc/PID/status) at last update()
|
//- Peak memory (VmPeak in /proc/PID/status) at last update()
|
||||||
inline int peak() const
|
inline int peak() const
|
||||||
{
|
{
|
||||||
@ -107,8 +115,11 @@ public:
|
|||||||
return rss_;
|
return rss_;
|
||||||
}
|
}
|
||||||
|
|
||||||
//- True if the memory information appears valid
|
//- System memory free (MemFree in /proc/meminfo)
|
||||||
bool valid() const;
|
inline int free() const
|
||||||
|
{
|
||||||
|
return free_;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
// Write
|
// Write
|
||||||
|
|||||||
@ -40,6 +40,7 @@ int Foam::profiling::allowed
|
|||||||
|
|
||||||
Foam::profiling* Foam::profiling::pool_(nullptr);
|
Foam::profiling* Foam::profiling::pool_(nullptr);
|
||||||
|
|
||||||
|
|
||||||
// * * * * * * * * * * * * Protected Member Functions * * * * * * * * * * * //
|
// * * * * * * * * * * * * Protected Member Functions * * * * * * * * * * * //
|
||||||
|
|
||||||
Foam::profilingInformation* Foam::profiling::find
|
Foam::profilingInformation* Foam::profiling::find
|
||||||
@ -98,10 +99,8 @@ bool Foam::profiling::print(Ostream& os)
|
|||||||
{
|
{
|
||||||
return pool_->writeData(os);
|
return pool_->writeData(os);
|
||||||
}
|
}
|
||||||
else
|
|
||||||
{
|
return false;
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -109,12 +108,10 @@ bool Foam::profiling::writeNow()
|
|||||||
{
|
{
|
||||||
if (active())
|
if (active())
|
||||||
{
|
{
|
||||||
return pool_->write();
|
return pool_->regIOobject::write();
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -189,7 +186,7 @@ Foam::profilingInformation* Foam::profiling::New
|
|||||||
clockTime& timer
|
clockTime& timer
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
profilingInformation *info = 0;
|
profilingInformation *info = nullptr;
|
||||||
|
|
||||||
if (active())
|
if (active())
|
||||||
{
|
{
|
||||||
@ -246,7 +243,7 @@ Foam::profiling::profiling
|
|||||||
const Time& owner
|
const Time& owner
|
||||||
)
|
)
|
||||||
:
|
:
|
||||||
regIOobject(io),
|
IOdictionary(io),
|
||||||
owner_(owner),
|
owner_(owner),
|
||||||
clockTime_(),
|
clockTime_(),
|
||||||
hash_(),
|
hash_(),
|
||||||
@ -265,7 +262,7 @@ Foam::profiling::profiling
|
|||||||
const Time& owner
|
const Time& owner
|
||||||
)
|
)
|
||||||
:
|
:
|
||||||
regIOobject(io),
|
IOdictionary(io),
|
||||||
owner_(owner),
|
owner_(owner),
|
||||||
clockTime_(),
|
clockTime_(),
|
||||||
hash_(),
|
hash_(),
|
||||||
|
|||||||
@ -53,6 +53,7 @@ SourceFiles
|
|||||||
#define profiling_H
|
#define profiling_H
|
||||||
|
|
||||||
#include "profilingTrigger.H"
|
#include "profilingTrigger.H"
|
||||||
|
#include "IOdictionary.H"
|
||||||
#include "HashPtrTable.H"
|
#include "HashPtrTable.H"
|
||||||
#include "Tuple2.H"
|
#include "Tuple2.H"
|
||||||
#include "LIFOStack.H"
|
#include "LIFOStack.H"
|
||||||
@ -77,7 +78,7 @@ class profilingSysInfo;
|
|||||||
|
|
||||||
class profiling
|
class profiling
|
||||||
:
|
:
|
||||||
public regIOobject
|
public IOdictionary
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
|
||||||
|
|||||||
@ -81,12 +81,6 @@ Foam::profilingInformation::profilingInformation
|
|||||||
{}
|
{}
|
||||||
|
|
||||||
|
|
||||||
// * * * * * * * * * * * * * * * * Destructor * * * * * * * * * * * * * * * //
|
|
||||||
|
|
||||||
Foam::profilingInformation::~profilingInformation()
|
|
||||||
{}
|
|
||||||
|
|
||||||
|
|
||||||
// * * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * //
|
// * * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * //
|
||||||
|
|
||||||
void Foam::profilingInformation::update(const scalar elapsed)
|
void Foam::profilingInformation::update(const scalar elapsed)
|
||||||
|
|||||||
@ -153,7 +153,7 @@ public:
|
|||||||
|
|
||||||
|
|
||||||
//- Destructor
|
//- Destructor
|
||||||
~profilingInformation();
|
~profilingInformation() = default;
|
||||||
|
|
||||||
|
|
||||||
// Member Functions
|
// Member Functions
|
||||||
|
|||||||
@ -48,18 +48,6 @@ inline static void printEnv
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// * * * * * * * * * * * * * * * * Constructors * * * * * * * * * * * * * * //
|
|
||||||
|
|
||||||
Foam::profilingSysInfo::profilingSysInfo()
|
|
||||||
{}
|
|
||||||
|
|
||||||
|
|
||||||
// * * * * * * * * * * * * * * * * Destructor * * * * * * * * * * * * * * * //
|
|
||||||
|
|
||||||
Foam::profilingSysInfo::~profilingSysInfo()
|
|
||||||
{}
|
|
||||||
|
|
||||||
|
|
||||||
// * * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * //
|
// * * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * //
|
||||||
|
|
||||||
Foam::Ostream& Foam::profilingSysInfo::write
|
Foam::Ostream& Foam::profilingSysInfo::write
|
||||||
|
|||||||
@ -50,26 +50,16 @@ class profilingSysInfo;
|
|||||||
|
|
||||||
class profilingSysInfo
|
class profilingSysInfo
|
||||||
{
|
{
|
||||||
// Private Member Functions
|
|
||||||
|
|
||||||
//- Disallow default bitwise copy construct
|
|
||||||
profilingSysInfo(const profilingSysInfo&) = delete;
|
|
||||||
|
|
||||||
//- Disallow default bitwise assignment
|
|
||||||
void operator=(const profilingSysInfo&) = delete;
|
|
||||||
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
|
||||||
|
|
||||||
// Constructors
|
// Constructors
|
||||||
|
|
||||||
//- Construct from components
|
//- Construct null
|
||||||
profilingSysInfo();
|
profilingSysInfo() = default;
|
||||||
|
|
||||||
|
|
||||||
//- Destructor
|
//- Destructor
|
||||||
~profilingSysInfo();
|
~profilingSysInfo() = default;
|
||||||
|
|
||||||
|
|
||||||
// Member Functions
|
// Member Functions
|
||||||
|
|||||||
Reference in New Issue
Block a user