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-2022 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)),
105  args.allowLibs()
106  )
107  );
108  }
109 
110  if (!nProcs)
111  {
113  << "No processor* directories found"
114  << exit(FatalError);
115  }
116 
117 
118  // Use the times list from the master processor
119  // and select a subset based on the command-line options
121  (
122  databases[0].times(),
123  args
124  );
125 
126  if (timeDirs.empty())
127  {
129  << "No times selected" << nl << endl;
130  return 1;
131  }
132 
133  // ----------------------------------------------------------------------
134 
135  // Processor local profiling information
136  List<dictionary> profiles(nProcs);
137 
138  // Loop over all times
139  forAll(timeDirs, timei)
140  {
141  // Set time for global database
142  runTime.setTime(timeDirs[timei], timei);
143 
144  Info<< "Time = " << runTime.timeName() << endl;
145 
146  // Name/location for the output summary
147  const fileName outputName
148  {
150  "profiling",
151  runTime.timeName(),
152  profilingFileName
153  };
154 
155 
156  label nDict = 0;
157 
158  // Set time for all databases
159  forAll(databases, proci)
160  {
161  profiles[proci].clear();
162  databases[proci].setTime(timeDirs[timei], timei);
163 
164  // Look for "uniform/profiling" in each processor directory
165  IOobjectList objects
166  (
167  databases[proci].time(),
168  databases[proci].timeName(),
169  "uniform"
170  );
171 
172  const IOobject* ioptr = objects.findObject(profilingFileName);
173  if (ioptr)
174  {
175  IOdictionary dict(*ioptr);
176 
177  // Full copy
178  profiles[proci] = dict;
179 
180  // Assumed to be good if it has 'profiling' sub-dict
181 
182  const dictionary* ptr = dict.findDict(blockNameProfiling);
183  if (ptr)
184  {
185  ++nDict;
186  }
187  }
188 
189  if (nDict < proci)
190  {
191  break;
192  }
193  }
194 
195  if (nDict != nProcs)
196  {
197  Info<< "found " << nDict << "/" << nProcs
198  << " profiling files" << nl << endl;
199  continue;
200  }
201 
202 
203  // Information seems to be there for all processors
204  // can do a summary
205 
206  IOdictionary summary
207  (
208  IOobject
209  (
211  runTime,
215  true // global-like
216  )
217  );
218 
219  summary.note() =
220  (
221  "summarized (max avg min) values from "
222  + Foam::name(nProcs) + " processors"
223  );
224 
225 
226  // Accumulator for each tag
228 
229  // Use first as 'master' to decide what others have
230  forAllConstIters(profiles.first(), mainIter)
231  {
232  const entry& mainEntry = mainIter();
233 
234  // level1: eg, profiling {} or memInfo {}
235  const word& level1Name = mainEntry.keyword();
236 
237  if
238  (
239  !processing.found(level1Name)
240  || !mainEntry.isDict()
241  || mainEntry.dict().empty()
242  )
243  {
244  continue; // Only process known types
245  }
246 
247  const wordList& tags = processing[level1Name];
248 
249  const dictionary& level1Dict = mainEntry.dict();
250 
251  // We need to handle sub-dicts with other dicts
252  // Eg, trigger0 { .. } trigger1 { .. }
253  //
254  // and ones with primitives
255  // Eg, size xx; free yy;
256 
257  // Decide based on the first entry:
258 
259  // level2: eg, profiling { trigger0 { } }
260  // or simply itself it contains primitives only
261 
262  wordList level2Names;
263 
264  const bool hasDictEntries
265  = mainEntry.dict().first()->isDict();
266 
267  if (hasDictEntries)
268  {
269  level2Names =
270  mainEntry.dict().sortedToc(stringOps::natural_sort());
271  }
272  else
273  {
274  level2Names = {level1Name};
275  }
276 
277  summary.set(level1Name, dictionary());
278 
279  dictionary& outputDict = summary.subDict(level1Name);
280 
281  for (const word& level2Name : level2Names)
282  {
283  // Presize everything
284  stats.clear();
285  for (const word& tag : tags)
286  {
287  stats(tag).reserve(nProcs);
288  }
289 
290  label nEntry = 0;
291 
292  for (const dictionary& procDict : profiles)
293  {
294  const dictionary* inDictPtr = procDict.findDict(level1Name);
295 
296  if (inDictPtr && hasDictEntries)
297  {
298  // Descend to the next level as required
299  inDictPtr = inDictPtr->findDict(level2Name);
300  }
301 
302  if (!inDictPtr)
303  {
304  break;
305  }
306 
307  ++nEntry;
308 
309  for (const word& tag : tags)
310  {
311  scalar val;
312 
313  if
314  (
315  inDictPtr->readIfPresent(tag, val, keyType::LITERAL)
316  )
317  {
318  stats(tag).append(val);
319  }
320  }
321  }
322 
323  if (nEntry != nProcs)
324  {
325  continue;
326  }
327 
328  dictionary* outDictPtr = nullptr;
329 
330  // Make a full copy of this entry prior to editing it
331  if (hasDictEntries)
332  {
333  outputDict.add(level2Name, level1Dict.subDict(level2Name));
334  outDictPtr = outputDict.findDict(level2Name);
335  }
336  else
337  {
338  // merge into existing (empty) dictionary
339  summary.add(level1Name, level1Dict, true);
340  outDictPtr = &outputDict;
341  }
342 
343  dictionary& outSubDict = *outDictPtr;
344 
345  // Remove trailing 'processor0' from any descriptions
346  // (looks nicer)
347  {
348  const word key("description");
349  string val;
350 
351  if (outSubDict.readIfPresent(key, val))
352  {
353  if (val.removeEnd("processor0"))
354  {
355  outSubDict.set(key, val);
356  }
357  }
358  }
359 
360  // Process each tag (calls, time etc)
361  for (const word& tag : tags)
362  {
363  DynamicList<scalar>& lst = stats(tag);
364 
365  if (lst.size() == nProcs)
366  {
367  sort(lst);
368  const scalar avg = sum(lst) / nProcs;
369 
370  if (lst.first() != lst.last())
371  {
372  outSubDict.set
373  (
374  tag,
376  {
377  lst.last(), avg, lst.first()
378  }
379  );
380  }
381  }
382  }
383  }
384  }
385 
386 
387  // Now write the summary
388  {
389  mkDir(summary.path());
390 
391  OFstream os(summary.objectPath());
392 
393  summary.writeHeader(os);
394  summary.writeData(os);
396 
397  Info<< "Wrote to " << outputName << nl << endl;
398  }
399  }
400 
401  Info<< "End\n" << endl;
402 
403  return 0;
404 }
405 
406 
407 // ************************************************************************* //
dictionary dict
static void noFunctionObjects(bool addWithOption=false)
Remove &#39;-noFunctionObjects&#39; option and ignore any occurrences.
Definition: argList.C:547
static void addNote(const string &note)
Add extra notes for the usage information.
Definition: argList.C:462
void size(const label n)
Older name for setAddressableSize.
Definition: UList.H:116
dimensioned< Type > sum(const DimensionedField< Type, GeoMesh > &f1)
fileName path() const
Return path = rootPath/caseName. Same as TimePaths::path()
Definition: Time.H:503
A class for handling file names.
Definition: fileName.H:72
List of IOobjects with searching and retrieving facilities. Implemented as a HashTable, so the various sorted methods should be used if traversing in parallel.
Definition: IOobjectList.H:55
errorManipArg< error, int > exit(error &err, const int errNo=1)
Definition: errorManip.H:125
error FatalError
Error stream (stdout output on all processes), with additional &#39;FOAM FATAL ERROR&#39; header text and sta...
A list of keyword definitions, which are a keyword followed by a number of values (eg...
Definition: dictionary.H:129
#define FatalErrorInFunction
Report an error message using Foam::FatalError.
Definition: error.H:598
Output to file stream, using an OSstream.
Definition: OFstream.H:49
constexpr char nl
The newline &#39;\n&#39; character (0x0a)
Definition: Ostream.H:50
bool empty() const noexcept
True if List is empty (ie, size() is zero)
Definition: UList.H:666
T & first()
Access first element of the list, position [0].
Definition: UList.H:853
engineTime & runTime
Ostream & endl(Ostream &os)
Add newline and flush stream.
Definition: Ostream.H:531
virtual const dictionary & dict() const =0
Return dictionary, if entry is a dictionary.
static void noParallel()
Remove the parallel options.
Definition: argList.C:584
refPtr< fileOperation > fileHandler(std::nullptr_t)
Delete current file handler - forwards to fileOperation::handler()
entry * add(entry *entryPtr, bool mergeEntry=false)
Add a new entry.
Definition: dictionary.C:625
Ignore writing from objectRegistry::writeObject()
const dictionary & subDict(const word &keyword, enum keyType::option matchOpt=keyType::REGEX) const
Find and return a sub-dictionary.
Definition: dictionary.C:441
Class to control time during OpenFOAM simulations that is also the top-level objectRegistry.
Definition: Time.H:69
instantList select(const instantList &times) const
Select a list of Time values that are within the ranges.
Definition: timeSelector.C:88
bool allowFunctionObjects() const
The controlDict &#39;functions&#39; entry is allowed to be used.
Definition: argList.C:2088
IOdictionary is derived from dictionary and IOobject to give the dictionary automatic IO functionalit...
Definition: IOdictionary.H:50
#define forAll(list, i)
Loop across all elements in list.
Definition: stdFoam.H:421
Functions used by OpenFOAM that are specific to POSIX compliant operating systems and need to be repl...
word timeName
Definition: getTimeIndex.H:3
bool allowLibs() const
The controlDict &#39;libs&#39; entry is allowed to be used. (eg, has not been disabled by the -no-libs option...
Definition: argList.C:2106
word outputName("finiteArea-edges.obj")
const keyType & keyword() const noexcept
Return keyword.
Definition: entry.H:231
bool mkDir(const fileName &pathName, mode_t mode=0777)
Make a directory and return an error if it could not be created.
Definition: POSIX.C:614
word name(const expressions::valueTypeCode typeCode)
A word representation of a valueTypeCode. Empty for expressions::valueTypeCode::INVALID.
Definition: exprTraits.C:127
A class for handling words, derived from Foam::string.
Definition: word.H:63
void sort(UList< T > &list)
Sort the list.
Definition: UList.C:296
void clear()
Remove all entries from table.
Definition: HashTable.C:725
virtual void setTime(const Time &t)
Reset the time and time-index to those of the given time.
Definition: Time.C:935
String literal.
Definition: keyType.H:82
static word controlDictName
The default control dictionary name (normally "controlDict")
Definition: Time.H:268
const fileName & caseName() const noexcept
Return case name (parallel run) or global case (serial run)
Definition: argListI.H:62
static Ostream & writeEndDivider(Ostream &os)
Write the standard end file divider.
static word timeName(const scalar t, const int precision=precision_)
Return a time name for the given scalar time value formatted with the given precision.
Definition: Time.C:714
bool readIfPresent(const word &keyword, T &val, enum keyType::option matchOpt=keyType::REGEX) const
Find an entry if present, and assign to T val. FatalIOError if it is found and the number of tokens i...
const fileName & rootPath() const noexcept
Return root path.
Definition: argListI.H:56
OBJstream os(runTime.globalPath()/outputName)
T & last()
Access last element of the list, position [size()-1].
Definition: UList.H:867
Encapsulation of natural order sorting for algorithms.
Definition: stringOpsSort.H:63
fileName path() const
Return the full path to the (processor local) case.
Definition: argListI.H:74
#define WarningInFunction
Report a warning using Foam::Warning.
auto key(const Type &t) -> typename std::enable_if< std::is_enum< Type >::value, typename std::underlying_type< Type >::type >::type
Definition: foamGltfBase.H:103
A list of pointers to objects of type <T>, with allocation/deallocation management of the pointers...
Definition: List.H:55
void reserve(label numEntries)
Reserve space for at least the specified number of elements (not the number of buckets) and regenerat...
Definition: HashTable.C:712
Nothing to be read.
static word outputPrefix
Directory prefix.
messageStream Info
Information stream (stdout output on master, null elsewhere)
virtual bool isDict() const noexcept
Return true if this entry is a dictionary.
Definition: entry.H:287
entry * set(entry *entryPtr)
Assign a new entry, overwriting any existing entry.
Definition: dictionary.C:765
T * first()
The first entry in the list.
Definition: UILList.H:542
Foam::argList args(argc, argv)
Defines the attributes of an object for which implicit objectRegistry management is supported...
Definition: IOobject.H:172
Do not request registration (bool: false)
bool removeEnd(const std::string &text)
Remove the given text from the end of the string.
Definition: string.C:222
wordList sortedToc() const
Return the sorted table of contents.
Definition: dictionary.C:601
static void addOptions(const bool constant=true, const bool withZero=false)
Add timeSelector options to argList::validOptions.
Definition: timeSelector.C:101
Namespace for OpenFOAM.
forAllConstIters(mixture.phases(), phase)
Definition: pEqn.H:28
A keyword and a list of tokens is an &#39;entry&#39;.
Definition: entry.H:63
const dictionary * findDict(const word &keyword, enum keyType::option matchOpt=keyType::REGEX) const
Find and return a sub-dictionary pointer if present (and a sub-dictionary) otherwise return nullptr...
Definition: dictionaryI.H:124