Belle II Software  release-05-02-19
ProcessStatistics.cc
1 /**************************************************************************
2  * BASF2 (Belle Analysis Framework 2) *
3  * Copyright(C) 2010 - Belle II Collaboration *
4  * *
5  * Author: The Belle II Collaboration *
6  * Contributors: Martin Ritter *
7  * *
8  * This software is provided "as is" without any warranty. *
9  **************************************************************************/
10 
11 #include <framework/core/ProcessStatistics.h>
12 
13 #include <framework/logging/Logger.h>
14 #include <framework/pcore/ProcHandler.h>
15 #include <framework/gearbox/Unit.h>
16 #include <framework/utilities/Utils.h>
17 
18 #include <boost/algorithm/string/replace.hpp>
19 #include <regex>
20 #include <boost/format.hpp>
21 
22 #include <algorithm>
23 #include <sstream>
24 
25 using namespace std;
26 using namespace Belle2;
27 
28 int ProcessStatistics::getIndex(const Module* module)
29 {
30  auto indexIt = m_modulesToStatsIndex.find(module);
31  if (indexIt == m_modulesToStatsIndex.end()) {
32  int index = m_stats.size();
33  m_modulesToStatsIndex[module] = index;
34  m_stats.emplace_back();
35  initModule(module);
36  return index;
37  } else {
38  return indexIt->second;
39  }
40 }
41 void ProcessStatistics::initModule(const Module* module)
42 {
43  int index = getIndex(module);
44  ModuleStatistics& stats = m_stats.at(index);
45  if (module and stats.getName().empty()) {
46  const string& type = module->getType();
47  if (type == "Tx" or type == "Rx")
48  stats.setName(type);
49  else
50  stats.setName(module->getName());
51  }
52  stats.setIndex(index);
53 }
54 
55 string ProcessStatistics::getStatisticsString(ModuleStatistics::EStatisticCounters mode,
56  const std::vector<ModuleStatistics>* modules) const
57 {
58  const ModuleStatistics& global = getGlobal();
59  if (!modules) modules = &(getAll());
60  int moduleNameLength = 21; //minimum: 80 characters
61  const int lengthOfRest = 80 - moduleNameLength;
62  for (const ModuleStatistics& stats : *modules) {
63  int len = stats.getName().length();
64  if (len > moduleNameLength)
65  moduleNameLength = len;
66  }
67  const std::string numTabsModule = (boost::format("%d") % (moduleNameLength + 1)).str();
68  const std::string numWidth = (boost::format("%d") % (moduleNameLength + 1 + lengthOfRest)).str();
69  boost::format outputheader("%s %|" + numTabsModule + "t|| %10s | %10s | %10s | %17s\n");
70  boost::format output("%s %|" + numTabsModule + "t|| %10.0f | %10.0f | %10.2f | %7.2f +-%7.2f\n");
71 
72  stringstream out;
73  out << boost::format("%|" + numWidth + "T=|\n");
74  out << outputheader % "Name" % "Calls" % "Memory(MB)" % "Time(s)" % "Time(ms)/Call";
75  out << boost::format("%|" + numWidth + "T=|\n");
76 
77  std::vector<ModuleStatistics> modulesSortedByIndex(*modules);
78  sort(modulesSortedByIndex.begin(), modulesSortedByIndex.end(), [](const ModuleStatistics & a, const ModuleStatistics & b) { return a.getIndex() < b.getIndex(); });
79 
80  for (const ModuleStatistics& stats : modulesSortedByIndex) {
81  out << output
82  % stats.getName()
83  % stats.getCalls(mode)
84  % (stats.getMemorySum(mode) / 1024)
85  % (stats.getTimeSum(mode) / Unit::s)
86  % (stats.getTimeMean(mode) / Unit::ms)
87  % (stats.getTimeStddev(mode) / Unit::ms);
88  }
89 
90  out << boost::format("%|" + numWidth + "T=|\n");
91  out << output
92  % (ProcHandler::isOutputProcess() ? "Total (output proc.)" : "Total")
93  % global.getCalls(mode)
94  % (global.getMemorySum(mode) / 1024)
95  % (global.getTimeSum(mode) / Unit::s)
96  % (global.getTimeMean(mode) / Unit::ms)
97  % (global.getTimeStddev(mode) / Unit::ms);
98  out << boost::format("%|" + numWidth + "T=|\n");
99  return out.str();
100 }
101 
102 void ProcessStatistics::appendUnmergedModules(const ProcessStatistics* otherObject)
103 {
104  unsigned int minIndexUnmerged = 0;
105  if (otherObject->m_modulesToStatsIndex.empty()) {
106  B2WARNING("ProcessStatistics::appendUnmergedModules(): Module -> index list is empty? This might produce wrong results");
107  } else {
108  minIndexUnmerged = otherObject->m_modulesToStatsIndex.begin()->second;
109  for (auto pair : otherObject->m_modulesToStatsIndex) {
110  if (pair.second < (int)minIndexUnmerged)
111  minIndexUnmerged = pair.second;
112  }
113  }
114  if (minIndexUnmerged > m_stats.size())
115  B2FATAL("(minIndexUnmerged > m_stats.size()) :( ");
116  if (minIndexUnmerged > otherObject->m_stats.size())
117  B2FATAL("(minIndexUnmerged > otherObject->m_stats.size()) :( ");
118 
119 
120  //the first minIndexUnmerged entries in m_stats should just be merged...
121  for (unsigned int i = 0; i < minIndexUnmerged; i++) {
122  ModuleStatistics& myStats = m_stats[i];
123  const ModuleStatistics& otherStats = otherObject->m_stats[i];
124  if (myStats.getName() == otherStats.getName()) {
125  myStats.update(otherStats);
126  } else {
127  B2ERROR("mismatch in module names in statistics (" << myStats.getName() << " vs. " << otherStats.getName() <<
128  "). ProcessStatistics::merge() can only merge statistics that contain exactly the same modules.");
129  }
130  }
131 
132  //append the rest
133  for (unsigned int i = minIndexUnmerged; i < otherObject->m_stats.size(); i++) {
134  const ModuleStatistics& otherStats = otherObject->m_stats[i];
135  m_stats.emplace_back(otherStats);
136  m_stats.back().setIndex(m_stats.size() - 1);
137  }
138  //copy m_modulesToStatsIndex
139  //shift indices by #entries missing in otherObject
140  const int shift = m_stats.size() - otherObject->m_stats.size();
141  if (shift < 0) {
142  B2FATAL("shift negative: " << shift);
143  }
144  for (auto pair : otherObject->m_modulesToStatsIndex) {
145  m_modulesToStatsIndex[pair.first] = pair.second + shift;
146  }
147 }
148 
149 
151 {
152  const auto* otherObject = static_cast<const ProcessStatistics*>(other);
153 
154  if (m_stats == otherObject->m_stats) {
155  //fast version for merging between processes
156  for (unsigned int i = 0; i < otherObject->m_stats.size(); i++)
157  m_stats[i].update(otherObject->m_stats[i]);
158  } else {
159  //note: statistics in m_global are not merged for pp, we use the output process instead
160  //for objects read from file we need to add them though
161  m_global.update(otherObject->m_global);
162 
163  appendUnmergedModules(otherObject);
164  }
165 
166  //if the other object has transient data on modules, copy remaining counters
167  if (!otherObject->m_modulesToStatsIndex.empty())
168  setTransientCounters(otherObject);
169 }
170 
171 void ProcessStatistics::setTransientCounters(const ProcessStatistics* otherObject)
172 {
173  m_globalTime = otherObject->m_globalTime;
174  m_globalMemory = otherObject->m_globalMemory;
175  m_moduleTime = otherObject->m_moduleTime;
176  m_moduleMemory = otherObject->m_moduleMemory;
177  m_suspendedTime = otherObject->m_suspendedTime;
178  m_suspendedMemory = otherObject->m_suspendedMemory;
179 }
180 
181 void ProcessStatistics::clear()
182 {
183  m_global.clear();
184  for (auto& stats : m_stats) { stats.clear(); }
185 }
186 
187 void ProcessStatistics::setCounters(double& time, double& memory,
188  double startTime, double startMemory)
189 {
190  time = Utils::getClock() - startTime;
191  memory = Utils::getRssMemoryKB() - startMemory;
192 }
193 
194 TObject* ProcessStatistics::Clone(const char*) const
195 {
196  auto* p = new ProcessStatistics(*this);
197  return p;
198 }
199 
200 std::string ProcessStatistics::getInfoHTML() const
201 {
202  std::string s = getStatisticsString();
203  const static std::regex tagRegex("^==*$");
204  s = std::regex_replace(s, tagRegex, "");
205 
206  boost::algorithm::replace_all(s, "|", "</td><td>");
207  boost::algorithm::replace_all(s, "\n", "</td></tr><tr><td>");
208  return "Event Statistics:<br /><table border=0><tr><td>" + s + "</td></tr></table>";
209 }
210 
Belle2::ProcessStatistics::m_globalTime
double m_globalTime
store clock counter for global time consumption
Definition: ProcessStatistics.h:223
Belle2::ModuleStatistics::EStatisticCounters
EStatisticCounters
Enum to define all counter types.
Definition: ModuleStatistics.h:39
Belle2::ModuleStatistics::getMemorySum
value_type getMemorySum(EStatisticCounters type=c_Total) const
return the total used memory for a given counter
Definition: ModuleStatistics.h:112
Belle2::ModuleStatistics::update
void update(const ModuleStatistics &other)
Add statistics for each category.
Definition: ModuleStatistics.h:73
Belle2::ProcessStatistics::m_globalMemory
double m_globalMemory
(transient)
Definition: ProcessStatistics.h:225
Belle2::ProcessStatistics::m_moduleMemory
double m_moduleMemory
(transient)
Definition: ProcessStatistics.h:229
Belle2::ProcessStatistics::m_stats
std::vector< Belle2::ModuleStatistics > m_stats
module statistics
Definition: ProcessStatistics.h:215
Belle2::Module
Base class for Modules.
Definition: Module.h:74
Belle2::ModuleStatistics::getName
const std::string & getName() const
Return the previously set name.
Definition: ModuleStatistics.h:86
Belle2::merge
std::vector< std::vector< double > > merge(std::vector< std::vector< std::vector< double >>> toMerge)
merge { vector<double> a, vector<double> b} into {a, b}
Definition: tools.h:44
Belle2::ProcessStatistics::m_suspendedMemory
double m_suspendedMemory
(transient)
Definition: ProcessStatistics.h:237
Belle2
Abstract base class for different kinds of events.
Definition: MillepedeAlgorithm.h:19
Belle2::ProcessStatistics::m_modulesToStatsIndex
std::map< const Module *, int > m_modulesToStatsIndex
transient, maps Module* to m_stats index.
Definition: ProcessStatistics.h:218
Belle2::ModuleStatistics::getCalls
value_type getCalls(EStatisticCounters type=c_Total) const
return the number of calls for a given counter type
Definition: ModuleStatistics.h:91
Belle2::ModuleStatistics::getTimeMean
value_type getTimeMean(EStatisticCounters type=c_Total) const
return the mean execution time for a given counter
Definition: ModuleStatistics.h:102
Belle2::ProcessStatistics::m_suspendedTime
double m_suspendedTime
(transient)
Definition: ProcessStatistics.h:233
Belle2::ModuleStatistics::getTimeStddev
value_type getTimeStddev(EStatisticCounters type=c_Total) const
return the stddev of the execution times for a given counter
Definition: ModuleStatistics.h:107
Belle2::ProcessStatistics
Class to collect call statistics for all modules.
Definition: ProcessStatistics.h:94
Belle2::ModuleStatistics::getTimeSum
value_type getTimeSum(EStatisticCounters type=c_Total) const
return the sum of all execution times for a given counter
Definition: ModuleStatistics.h:97
Belle2::ProcessStatistics::m_moduleTime
double m_moduleTime
(transient)
Definition: ProcessStatistics.h:227
Belle2::ModuleStatistics
Keep track of time and memory consumption during processing.
Definition: ModuleStatistics.h:36
Belle2::Mergeable
Abstract base class for objects that can be merged.
Definition: Mergeable.h:33