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 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  #ifdef fileOperation_H
90  const label nProcs = fileHandler().nProcs(args.path());
91  #else
92  label nProcs = 0;
93  while (isDir(args.path()/("processor" + Foam::name(nProcs))))
94  {
95  ++nProcs;
96  }
97  #endif
98 
99  // Create the processor databases
100  PtrList<Time> databases(nProcs);
101 
102  forAll(databases, proci)
103  {
104  databases.set
105  (
106  proci,
107  new Time
108  (
110  args.rootPath(),
111  args.caseName()/("processor" + Foam::name(proci))
112  )
113  );
114  }
115 
116  if (!nProcs)
117  {
119  << "No processor* directories found"
120  << exit(FatalError);
121  }
122 
123 
124  // Use the times list from the master processor
125  // and select a subset based on the command-line options
127  (
128  databases[0].times(),
129  args
130  );
131 
132  if (timeDirs.empty())
133  {
135  << "No times selected" << nl << endl;
136  return 1;
137  }
138 
139  // ----------------------------------------------------------------------
140 
141  // Processor local profiling information
142  List<dictionary> profiles(nProcs);
143 
144  // Loop over all times
145  forAll(timeDirs, timei)
146  {
147  // Set time for global database
148  runTime.setTime(timeDirs[timei], timei);
149 
150  Info<< "Time = " << runTime.timeName() << endl;
151 
152  // Name/location for the output summary
153  const fileName outputName
154  {
156  "profiling",
157  runTime.timeName(),
158  profilingFileName
159  };
160 
161 
162  label nDict = 0;
163 
164  // Set time for all databases
165  forAll(databases, proci)
166  {
167  profiles[proci].clear();
168  databases[proci].setTime(timeDirs[timei], timei);
169 
170  // Look for "uniform/profiling" in each processor directory
171  IOobjectList objects
172  (
173  databases[proci].time(),
174  databases[proci].timeName(),
175  "uniform"
176  );
177 
178  const IOobject* ioptr = objects.findObject(profilingFileName);
179  if (ioptr)
180  {
181  IOdictionary dict(*ioptr);
182 
183  // Full copy
184  profiles[proci] = dict;
185 
186  // Assumed to be good if it has 'profiling' sub-dict
187 
188  const dictionary* ptr = dict.findDict(blockNameProfiling);
189  if (ptr)
190  {
191  ++nDict;
192  }
193  }
194 
195  if (nDict < proci)
196  {
197  break;
198  }
199  }
200 
201  if (nDict != nProcs)
202  {
203  Info<< "found " << nDict << "/" << nProcs
204  << " profiling files" << nl << endl;
205  continue;
206  }
207 
208 
209  // Information seems to be there for all processors
210  // can do a summary
211 
212  IOdictionary summary
213  (
214  IOobject
215  (
216  runTime.path()/outputName,
217  runTime,
220  false, // no register
221  true // global-like
222  )
223  );
224 
225  summary.note() =
226  (
227  "summarized (max avg min) values from "
228  + Foam::name(nProcs) + " processors"
229  );
230 
231 
232  // Accumulator for each tag
234 
235  // Use first as 'master' to decide what others have
236  forAllConstIters(profiles.first(), mainIter)
237  {
238  const entry& mainEntry = mainIter();
239 
240  // level1: eg, profiling {} or memInfo {}
241  const word& level1Name = mainEntry.keyword();
242 
243  if
244  (
245  !processing.found(level1Name)
246  || !mainEntry.isDict()
247  || mainEntry.dict().empty()
248  )
249  {
250  continue; // Only process known types
251  }
252 
253  const wordList& tags = processing[level1Name];
254 
255  const dictionary& level1Dict = mainEntry.dict();
256 
257  // We need to handle sub-dicts with other dicts
258  // Eg, trigger0 { .. } trigger1 { .. }
259  //
260  // and ones with primitives
261  // Eg, size xx; free yy;
262 
263  // Decide based on the first entry:
264 
265  // level2: eg, profiling { trigger0 { } }
266  // or simply itself it contains primitives only
267 
268  wordList level2Names;
269 
270  const bool hasDictEntries
271  = mainEntry.dict().first()->isDict();
272 
273  if (hasDictEntries)
274  {
275  level2Names =
276  mainEntry.dict().sortedToc(stringOps::natural_sort());
277  }
278  else
279  {
280  level2Names = {level1Name};
281  }
282 
283  summary.set(level1Name, dictionary());
284 
285  dictionary& outputDict = summary.subDict(level1Name);
286 
287  for (const word& level2Name : level2Names)
288  {
289  // Presize everything
290  stats.clear();
291  for (const word& tag : tags)
292  {
293  stats(tag).reserve(nProcs);
294  }
295 
296  label nEntry = 0;
297 
298  for (const dictionary& procDict : profiles)
299  {
300  const dictionary* inDictPtr = procDict.findDict(level1Name);
301 
302  if (inDictPtr && hasDictEntries)
303  {
304  // Descend to the next level as required
305  inDictPtr = inDictPtr->findDict(level2Name);
306  }
307 
308  if (!inDictPtr)
309  {
310  break;
311  }
312 
313  ++nEntry;
314 
315  for (const word& tag : tags)
316  {
317  scalar val;
318 
319  if
320  (
321  inDictPtr->readIfPresent(tag, val, keyType::LITERAL)
322  )
323  {
324  stats(tag).append(val);
325  }
326  }
327  }
328 
329  if (nEntry != nProcs)
330  {
331  continue;
332  }
333 
334  dictionary* outDictPtr = nullptr;
335 
336  // Make a full copy of this entry prior to editing it
337  if (hasDictEntries)
338  {
339  outputDict.add(level2Name, level1Dict.subDict(level2Name));
340  outDictPtr = outputDict.findDict(level2Name);
341  }
342  else
343  {
344  // merge into existing (empty) dictionary
345  summary.add(level1Name, level1Dict, true);
346  outDictPtr = &outputDict;
347  }
348 
349  dictionary& outSubDict = *outDictPtr;
350 
351  // Remove trailing 'processor0' from any descriptions
352  // (looks nicer)
353  {
354  const word key("description");
355  string val;
356 
357  if (outSubDict.readIfPresent(key, val))
358  {
359  if (val.removeEnd("processor0"))
360  {
361  outSubDict.set(key, val);
362  }
363  }
364  }
365 
366  // Process each tag (calls, time etc)
367  for (const word& tag : tags)
368  {
369  DynamicList<scalar>& lst = stats(tag);
370 
371  if (lst.size() == nProcs)
372  {
373  sort(lst);
374  const scalar avg = sum(lst) / nProcs;
375 
376  if (lst.first() != lst.last())
377  {
378  outSubDict.set
379  (
380  tag,
382  {
383  lst.last(), avg, lst.first()
384  }
385  );
386  }
387  }
388  }
389  }
390  }
391 
392 
393  // Now write the summary
394  {
395  mkDir(summary.path());
396 
397  OFstream os(summary.objectPath());
398 
399  summary.writeHeader(os);
400  summary.writeData(os);
401  summary.writeEndDivider(os);
402 
403  Info<< "Wrote to " << outputName << nl << endl;
404  }
405  }
406 
407  Info<< "End\n" << endl;
408 
409  return 0;
410 }
411 
412 
413 // ************************************************************************* //
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: dictionary.C:508
Foam::IOobject::NO_WRITE
Definition: IOobject.H:130
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:104
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:62
Foam::fileName
A class for handling file names.
Definition: fileName.H:69
Foam::entry::keyword
const keyType & keyword() const
Return keyword.
Definition: entry.H:187
Foam::DynamicList< scalar >
Foam::Time::timeName
static word timeName(const scalar t, const int precision=precision_)
Definition: Time.C:785
Foam::argList::addNote
static void addNote(const string &note)
Add extra notes for the usage information.
Definition: argList.C:413
Foam::timeSelector::select
instantList select(const instantList &times) const
Select a list of Time values that are within the ranges.
Definition: timeSelector.C:95
Foam::fileHandler
const fileOperation & fileHandler()
Get current file handler.
Definition: fileOperation.C:1170
IOobjectList.H
Foam::endl
Ostream & endl(Ostream &os)
Add newline and flush stream.
Definition: Ostream.H:350
Foam::dictionary::set
entry * set(entry *entryPtr)
Assign a new entry, overwriting any existing entry.
Definition: dictionary.C:847
polyMesh.H
Foam::Time::controlDictName
static word controlDictName
The default control dictionary name (normally "controlDict")
Definition: Time.H:226
Foam::argList::rootPath
const fileName & rootPath() const
Return root path.
Definition: argListI.H:63
forAll
#define forAll(list, i)
Loop across all elements in list.
Definition: stdFoam.H:296
OFstream.H
Foam::argList::noFunctionObjects
static void noFunctionObjects(bool addWithOption=false)
Remove '-noFunctionObjects' option and ignore any occurrences.
Definition: argList.C:454
Foam::entry::isDict
virtual bool isDict() const
Return true if this entry is a dictionary.
Definition: entry.H:222
Foam::Info
messageStream Info
Information stream (uses stdout - output is on the master only)
Foam::name
word name(const complex &c)
Return string representation of complex.
Definition: complex.C:76
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:915
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:528
Foam::sort
void sort(UList< T > &a)
Definition: UList.C:254
Foam::PtrList
A list of pointers to objects of type <T>, with allocation/deallocation management of the pointers....
Definition: List.H:62
Foam::functionObject::outputPrefix
static word outputPrefix
Directory prefix.
Definition: functionObject.H:340
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:121
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:87
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: dictionary.C:498
Time.H
setRootCase.H
FatalErrorInFunction
#define FatalErrorInFunction
Report an error message using Foam::FatalError.
Definition: error.H:372
Foam::nl
constexpr char nl
Definition: Ostream.H:385
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:108
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:1006
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::dictionary::add
entry * add(entry *entryPtr, bool mergeEntry=false)
Add a new entry.
Definition: dictionary.C:708
Foam::argList::caseName
const fileName & caseName() const
Return case name (parallel run) or global case (serial run)
Definition: argListI.H:69
Foam::keyType::LITERAL
String literal.
Definition: keyType.H:73
Foam::argList::noParallel
static void noParallel()
Remove the parallel options.
Definition: argList.C:491
functionObject.H
Foam::IOobject::NO_READ
Definition: IOobject.H:123
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:684
WarningInFunction
#define WarningInFunction
Report a warning using Foam::Warning.
Definition: messageStream.H:298
Foam::string::removeEnd
bool removeEnd(const std::string &text)
Remove the given text from the end of the string.
Definition: string.C:242
Foam::dictionary::readIfPresent
bool readIfPresent(const word &keyword, T &val, enum keyType::option matchOpt=keyType::REGEX) const
Definition: dictionaryTemplates.C:417
Foam::isDir
bool isDir(const fileName &name, const bool followLink=true)
Does the name exist as a DIRECTORY in the file system?
Definition: MSwindows.C:643