profilingSummary.C
Go to the documentation of this file.
1 /*---------------------------------------------------------------------------*\
2  ========= |
3  \\ / F ield | OpenFOAM: The Open Source CFD Toolbox
4  \\ / O peration |
5  \\ / A nd | www.openfoam.com
6  \\/ M anipulation |
7 -------------------------------------------------------------------------------
8  Copyright (C) 2017-2020 OpenCFD Ltd.
9 -------------------------------------------------------------------------------
10 License
11  This file is part of OpenFOAM.
12 
13  OpenFOAM is free software: you can redistribute it and/or modify it
14  under the terms of the GNU General Public License as published by
15  the Free Software Foundation, either version 3 of the License, or
16  (at your option) any later version.
17 
18  OpenFOAM is distributed in the hope that it will be useful, but WITHOUT
19  ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
20  FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
21  for more details.
22 
23  You should have received a copy of the GNU General Public License
24  along with OpenFOAM. If not, see <http://www.gnu.org/licenses/>.
25 
26 Application
27  profilingSummary
28 
29 Group
30  grpMiscUtilities
31 
32 Description
33  Collects information from profiling files in the processor
34  sub-directories and summarizes the number of calls and time spent as
35  max/avg/min values. If the values are identical for all processes,
36  only a single value is written.
37 
38 \*---------------------------------------------------------------------------*/
39 
40 #include "Time.H"
41 #include "polyMesh.H"
42 #include "OSspecific.H"
43 #include "IFstream.H"
44 #include "OFstream.H"
45 #include "argList.H"
46 #include "stringOps.H"
47 #include "timeSelector.H"
48 #include "IOobjectList.H"
49 #include "functionObject.H"
50 
51 using namespace Foam;
52 
53 // The name of the sub-dictionary entry for profiling fileName:
54 static const word profilingFileName("profiling");
55 
56 // The name of the sub-dictionary entry for profiling:
57 static const word blockNameProfiling("profiling");
58 
59 // The name of the sub-dictionary entry for profiling and tags of entries
60 // that will be processed to determine (max,avg,min) values
61 const HashTable<wordList> processing
62 {
63  { "profiling", { "calls", "totalTime", "childTime", "maxMem" } },
64  { "memInfo", { "size", "free" } },
65 };
66 
67 
68 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
69 
70 int main(int argc, char *argv[])
71 {
73  (
74  "Collect profiling information from processor directories and"
75  " summarize time spent and number of calls as (max avg min) values."
76  );
77 
78  timeSelector::addOptions(true, true); // constant(true), zero(true)
80  argList::noFunctionObjects(); // Never use function objects
81 
82  // Note that this should work without problems when profiling is active,
83  // since we don't trigger it anywhere
84 
85  #include "setRootCase.H"
86  #include "createTime.H"
87 
88  // Determine the processor count
89  const label nProcs = fileHandler().nProcs(args.path());
90 
91  // Create the processor databases
92  PtrList<Time> databases(nProcs);
93 
94  forAll(databases, proci)
95  {
96  databases.set
97  (
98  proci,
99  new Time
100  (
102  args.rootPath(),
103  args.caseName()/("processor" + Foam::name(proci))
104  )
105  );
106  }
107 
108  if (!nProcs)
109  {
111  << "No processor* directories found"
112  << exit(FatalError);
113  }
114 
115 
116  // Use the times list from the master processor
117  // and select a subset based on the command-line options
119  (
120  databases[0].times(),
121  args
122  );
123 
124  if (timeDirs.empty())
125  {
127  << "No times selected" << nl << endl;
128  return 1;
129  }
130 
131  // ----------------------------------------------------------------------
132 
133  // Processor local profiling information
134  List<dictionary> profiles(nProcs);
135 
136  // Loop over all times
137  forAll(timeDirs, timei)
138  {
139  // Set time for global database
140  runTime.setTime(timeDirs[timei], timei);
141 
142  Info<< "Time = " << runTime.timeName() << endl;
143 
144  // Name/location for the output summary
145  const fileName outputName
146  {
148  "profiling",
149  runTime.timeName(),
150  profilingFileName
151  };
152 
153 
154  label nDict = 0;
155 
156  // Set time for all databases
157  forAll(databases, proci)
158  {
159  profiles[proci].clear();
160  databases[proci].setTime(timeDirs[timei], timei);
161 
162  // Look for "uniform/profiling" in each processor directory
163  IOobjectList objects
164  (
165  databases[proci].time(),
166  databases[proci].timeName(),
167  "uniform"
168  );
169 
170  const IOobject* ioptr = objects.findObject(profilingFileName);
171  if (ioptr)
172  {
173  IOdictionary dict(*ioptr);
174 
175  // Full copy
176  profiles[proci] = dict;
177 
178  // Assumed to be good if it has 'profiling' sub-dict
179 
180  const dictionary* ptr = dict.findDict(blockNameProfiling);
181  if (ptr)
182  {
183  ++nDict;
184  }
185  }
186 
187  if (nDict < proci)
188  {
189  break;
190  }
191  }
192 
193  if (nDict != nProcs)
194  {
195  Info<< "found " << nDict << "/" << nProcs
196  << " profiling files" << nl << endl;
197  continue;
198  }
199 
200 
201  // Information seems to be there for all processors
202  // can do a summary
203 
204  IOdictionary summary
205  (
206  IOobject
207  (
209  runTime,
212  false, // no register
213  true // global-like
214  )
215  );
216 
217  summary.note() =
218  (
219  "summarized (max avg min) values from "
220  + Foam::name(nProcs) + " processors"
221  );
222 
223 
224  // Accumulator for each tag
226 
227  // Use first as 'master' to decide what others have
228  forAllConstIters(profiles.first(), mainIter)
229  {
230  const entry& mainEntry = mainIter();
231 
232  // level1: eg, profiling {} or memInfo {}
233  const word& level1Name = mainEntry.keyword();
234 
235  if
236  (
237  !processing.found(level1Name)
238  || !mainEntry.isDict()
239  || mainEntry.dict().empty()
240  )
241  {
242  continue; // Only process known types
243  }
244 
245  const wordList& tags = processing[level1Name];
246 
247  const dictionary& level1Dict = mainEntry.dict();
248 
249  // We need to handle sub-dicts with other dicts
250  // Eg, trigger0 { .. } trigger1 { .. }
251  //
252  // and ones with primitives
253  // Eg, size xx; free yy;
254 
255  // Decide based on the first entry:
256 
257  // level2: eg, profiling { trigger0 { } }
258  // or simply itself it contains primitives only
259 
260  wordList level2Names;
261 
262  const bool hasDictEntries
263  = mainEntry.dict().first()->isDict();
264 
265  if (hasDictEntries)
266  {
267  level2Names =
268  mainEntry.dict().sortedToc(stringOps::natural_sort());
269  }
270  else
271  {
272  level2Names = {level1Name};
273  }
274 
275  summary.set(level1Name, dictionary());
276 
277  dictionary& outputDict = summary.subDict(level1Name);
278 
279  for (const word& level2Name : level2Names)
280  {
281  // Presize everything
282  stats.clear();
283  for (const word& tag : tags)
284  {
285  stats(tag).reserve(nProcs);
286  }
287 
288  label nEntry = 0;
289 
290  for (const dictionary& procDict : profiles)
291  {
292  const dictionary* inDictPtr = procDict.findDict(level1Name);
293 
294  if (inDictPtr && hasDictEntries)
295  {
296  // Descend to the next level as required
297  inDictPtr = inDictPtr->findDict(level2Name);
298  }
299 
300  if (!inDictPtr)
301  {
302  break;
303  }
304 
305  ++nEntry;
306 
307  for (const word& tag : tags)
308  {
309  scalar val;
310 
311  if
312  (
313  inDictPtr->readIfPresent(tag, val, keyType::LITERAL)
314  )
315  {
316  stats(tag).append(val);
317  }
318  }
319  }
320 
321  if (nEntry != nProcs)
322  {
323  continue;
324  }
325 
326  dictionary* outDictPtr = nullptr;
327 
328  // Make a full copy of this entry prior to editing it
329  if (hasDictEntries)
330  {
331  outputDict.add(level2Name, level1Dict.subDict(level2Name));
332  outDictPtr = outputDict.findDict(level2Name);
333  }
334  else
335  {
336  // merge into existing (empty) dictionary
337  summary.add(level1Name, level1Dict, true);
338  outDictPtr = &outputDict;
339  }
340 
341  dictionary& outSubDict = *outDictPtr;
342 
343  // Remove trailing 'processor0' from any descriptions
344  // (looks nicer)
345  {
346  const word key("description");
347  string val;
348 
349  if (outSubDict.readIfPresent(key, val))
350  {
351  if (val.removeEnd("processor0"))
352  {
353  outSubDict.set(key, val);
354  }
355  }
356  }
357 
358  // Process each tag (calls, time etc)
359  for (const word& tag : tags)
360  {
361  DynamicList<scalar>& lst = stats(tag);
362 
363  if (lst.size() == nProcs)
364  {
365  sort(lst);
366  const scalar avg = sum(lst) / nProcs;
367 
368  if (lst.first() != lst.last())
369  {
370  outSubDict.set
371  (
372  tag,
374  {
375  lst.last(), avg, lst.first()
376  }
377  );
378  }
379  }
380  }
381  }
382  }
383 
384 
385  // Now write the summary
386  {
387  mkDir(summary.path());
388 
389  OFstream os(summary.objectPath());
390 
391  summary.writeHeader(os);
392  summary.writeData(os);
393  summary.writeEndDivider(os);
394 
395  Info<< "Wrote to " << outputName << nl << endl;
396  }
397  }
398 
399  Info<< "End\n" << endl;
400 
401  return 0;
402 }
403 
404 
405 // ************************************************************************* //
Foam::entry
A keyword and a list of tokens is an 'entry'.
Definition: entry.H:67
Foam::dictionary::findDict
dictionary * findDict(const word &keyword, enum keyType::option matchOpt=keyType::REGEX)
Find and return a sub-dictionary pointer if present.
Definition: dictionaryI.H:127
Foam::IOobject::NO_WRITE
Definition: IOobject.H:195
Foam::IOdictionary
IOdictionary is derived from dictionary and IOobject to give the dictionary automatic IO functionalit...
Definition: IOdictionary.H:54
runTime
engineTime & runTime
Definition: createEngineTime.H:13
Foam::IOobject
Defines the attributes of an object for which implicit objectRegistry management is supported,...
Definition: IOobject.H:169
OSspecific.H
Functions used by OpenFOAM that are specific to POSIX compliant operating systems and need to be repl...
Foam::Time
Class to control time during OpenFOAM simulations that is also the top-level objectRegistry.
Definition: Time.H:73
Foam::word
A class for handling words, derived from Foam::string.
Definition: word.H:65
Foam::fileName
A class for handling file names.
Definition: fileName.H:73
Foam::DynamicList< scalar >
Foam::argList::caseName
const fileName & caseName() const noexcept
Return case name (parallel run) or global case (serial run)
Definition: argListI.H:69
Foam::entry::isDict
virtual bool isDict() const noexcept
Return true if this entry is a dictionary.
Definition: entry.H:233
Foam::Time::timeName
static word timeName(const scalar t, const int precision=precision_)
Definition: Time.C:780
Foam::glTF::key
auto key(const Type &t) -> typename std::enable_if< std::is_enum< Type >::value, typename std::underlying_type< Type >::type >::type
Definition: foamGltfBase.H:108
Foam::argList::addNote
static void addNote(const string &note)
Add extra notes for the usage information.
Definition: argList.C:412
Foam::timeSelector::select
instantList select(const instantList &times) const
Select a list of Time values that are within the ranges.
Definition: timeSelector.C:89
Foam::entry::keyword
const keyType & keyword() const noexcept
Return keyword.
Definition: entry.H:195
Foam::fileHandler
const fileOperation & fileHandler()
Get current file handler.
Definition: fileOperation.C:1485
IOobjectList.H
Foam::endl
Ostream & endl(Ostream &os)
Add newline and flush stream.
Definition: Ostream.H:369
Foam::dictionary::set
entry * set(entry *entryPtr)
Assign a new entry, overwriting any existing entry.
Definition: dictionary.C:780
polyMesh.H
Foam::Time::controlDictName
static word controlDictName
The default control dictionary name (normally "controlDict")
Definition: Time.H:226
forAll
#define forAll(list, i)
Loop across all elements in list.
Definition: stdFoam.H:296
OFstream.H
outputName
word outputName("finiteArea-edges.obj")
Foam::argList::noFunctionObjects
static void noFunctionObjects(bool addWithOption=false)
Remove '-noFunctionObjects' option and ignore any occurrences.
Definition: argList.C:473
Foam::Info
messageStream Info
Information stream (stdout output on master, null elsewhere)
Foam::fileOperation::nProcs
virtual label nProcs(const fileName &dir, const fileName &local="") const
Get number of processor directories/results. Used for e.g.
Definition: fileOperation.C:1193
argList.H
Foam::dictionary::subDict
const dictionary & subDict(const word &keyword, enum keyType::option matchOpt=keyType::REGEX) const
Find and return a sub-dictionary.
Definition: dictionary.C:460
Foam::sort
void sort(UList< T > &a)
Definition: UList.C:261
Foam::PtrList
A list of pointers to objects of type <T>, with allocation/deallocation management of the pointers....
Definition: List.H:59
Foam::functionObject::outputPrefix
static word outputPrefix
Directory prefix.
Definition: functionObject.H:376
IFstream.H
timeName
word timeName
Definition: getTimeIndex.H:3
dict
dictionary dict
Definition: searchingEngine.H:14
Foam::argList::path
fileName path() const
Return the full path to the (processor local) case.
Definition: argListI.H:81
Foam::FatalError
error FatalError
Foam::dictionary
A list of keyword definitions, which are a keyword followed by a number of values (eg,...
Definition: dictionary.H:123
os
OBJstream os(runTime.globalPath()/outputName)
Foam
Namespace for OpenFOAM.
Definition: atmBoundaryLayer.C:33
Foam::entry::dict
virtual const dictionary & dict() const =0
Return dictionary, if entry is a dictionary.
Foam::IOobjectList
List of IOobjects with searching and retrieving facilities.
Definition: IOobjectList.H:55
Foam::exit
errorManipArg< error, int > exit(error &err, const int errNo=1)
Definition: errorManip.H:130
Foam::OFstream
Output to file stream, using an OSstream.
Definition: OFstream.H:53
Foam::HashTable< wordList >
Foam::dictionary::isDict
bool isDict(const word &keyword, enum keyType::option matchOpt=keyType::REGEX) const
Check if entry is found and is a sub-dictionary.
Definition: dictionaryI.H:147
Time.H
setRootCase.H
FatalErrorInFunction
#define FatalErrorInFunction
Report an error message using Foam::FatalError.
Definition: error.H:453
Foam::nl
constexpr char nl
Definition: Ostream.H:404
Foam::Time::path
fileName path() const
Return path.
Definition: Time.H:358
forAllConstIters
forAllConstIters(mixture.phases(), phase)
Definition: pEqn.H:28
Foam::HashTable::clear
void clear()
Clear all entries from table.
Definition: HashTable.C:630
Foam::timeSelector::addOptions
static void addOptions(const bool constant=true, const bool withZero=false)
Add timeSelector options to argList::validOptions.
Definition: timeSelector.C:102
Foam::List< instant >
Foam::Time::setTime
virtual void setTime(const Time &t)
Reset the time and time-index to those of the given time.
Definition: Time.C:1003
Foam::FixedList< scalar, 3 >
Foam::sum
dimensioned< Type > sum(const DimensionedField< Type, GeoMesh > &df)
Definition: DimensionedFieldFunctions.C:327
timeSelector.H
createTime.H
Foam::stringOps::natural_sort
Encapsulation of natural order sorting for algorithms.
Definition: stringOpsSort.H:62
Foam::name
word name(const expressions::valueTypeCode typeCode)
A word representation of a valueTypeCode. Empty for INVALID.
Definition: exprTraits.C:59
Foam::dictionary::add
entry * add(entry *entryPtr, bool mergeEntry=false)
Add a new entry.
Definition: dictionary.C:640
Foam::keyType::LITERAL
String literal.
Definition: keyType.H:81
Foam::argList::noParallel
static void noParallel()
Remove the parallel options.
Definition: argList.C:510
functionObject.H
Foam::IOobject::NO_READ
Definition: IOobject.H:188
args
Foam::argList args(argc, argv)
Foam::mkDir
bool mkDir(const fileName &pathName, mode_t mode=0777)
Make a directory and return an error if it could not be created.
Definition: MSwindows.C:507
stringOps.H
Foam::dictionary::sortedToc
wordList sortedToc() const
Return the sorted table of contents.
Definition: dictionary.C:616
WarningInFunction
#define WarningInFunction
Report a warning using Foam::Warning.
Definition: messageStream.H:328
Foam::string::removeEnd
bool removeEnd(const std::string &text)
Remove the given text from the end of the string.
Definition: string.C:229
Foam::argList::rootPath
const fileName & rootPath() const noexcept
Return root path.
Definition: argListI.H:63
Foam::dictionary::readIfPresent
bool readIfPresent(const word &keyword, T &val, enum keyType::option matchOpt=keyType::REGEX) const
Definition: dictionaryTemplates.C:405