chombo-discharge
CD_TimerImplem.H
Go to the documentation of this file.
1 /* chombo-discharge
2  * Copyright © 2021 SINTEF Energy Research.
3  * Please refer to Copyright.txt and LICENSE in the chombo-discharge root directory.
4  */
5 
12 #ifndef CD_TimerImplem_H
13 #define CD_TimerImplem_H
14 
15 // Std includes
16 #include <sstream>
17 #include <iostream>
18 #include <fstream>
19 #include <iomanip>
20 
21 // Chombo includes
22 #ifdef CH_MPI
23 #include <SPMD.H>
24 #endif
25 
26 // Our includes
27 #include <CD_Timer.H>
28 #include <CD_NamespaceHeader.H>
29 
30 inline Real
32 {
33 
34  // TLDR: This just returns the current time in seconds (since an arbitrary time in the past).
35  const auto currentTime = std::chrono::steady_clock::now();
36  const auto durationInSeconds = std::chrono::duration<Real>(currentTime.time_since_epoch());
37 
38  return durationInSeconds.count();
39 }
40 
41 inline Timer::Timer(const std::string a_processName)
42 {
43  m_processName = a_processName;
44  m_events.clear();
45 }
46 
47 inline Timer::~Timer()
48 {
49  m_events.clear();
50 }
51 
52 inline void
53 Timer::clear() noexcept
54 {
55  m_events.clear();
56 }
57 
58 inline void
59 Timer::startEvent(const std::string a_event) noexcept
60 {
61 #ifdef _OPENMP
62  if (omp_get_thread_num() == 0) {
63 #endif
64  if (m_events.find(a_event) == m_events.end()) { // Create a new event.
65  const TimePoint startTime = Clock::now();
66  const Duration elapsedTime = Duration(0.0);
67 
68  m_events.emplace(a_event, std::make_tuple(false, startTime, elapsedTime));
69  }
70  else { // If the event is not new, we just leave the elapsed time intact.
71  // const TimePoint startTime = Clock::now();
72  std::tuple<bool, TimePoint, Duration>& event = m_events.at(a_event);
73  const bool stoppedEvent = std::get<StoppedEvent>(event);
74 
75  if (stoppedEvent) { // Only start the event if it is not already running.
76  const TimePoint startTime = Clock::now();
77  const Duration& previousElapsedTime = std::get<ElapsedTime>(event);
78 
79  event = std::make_tuple(false, startTime, previousElapsedTime);
80  }
81  }
82 #ifdef _OPENMP
83  }
84 #endif
85 }
86 
87 inline void
88 Timer::stopEvent(const std::string a_event) noexcept
89 {
90 #ifdef _OPENMP
91  if (omp_get_thread_num() == 0) {
92 #endif
93  if (m_events.find(a_event) != m_events.end()) {
94  std::tuple<bool, TimePoint, Duration>& event = m_events.at(a_event);
95 
96  const TimePoint startTime = std::get<StartClock>(event);
97  const TimePoint stopTime = Clock::now();
98  const Duration curElapsedTime = stopTime - startTime;
99  const Duration previousElapsedTime = std::get<ElapsedTime>(event);
100  const Duration totalElapsedTime = previousElapsedTime + curElapsedTime;
101 
102  event = std::make_tuple(true, startTime, totalElapsedTime);
103  }
104  else {
105  std::cerr << "Timer::stopEvent -- event '" + a_event + "' has not been started\n";
106  }
107 #ifdef _OPENMP
108  }
109 #endif
110 }
111 
112 inline void
113 Timer::printReportHeader(std::ostream& a_outputStream) const noexcept
114 {
115 #ifdef _OPENMP
116  if (omp_get_thread_num() == 0) {
117 #endif
118  std::stringstream header;
119  header
120  << "| ---------------------------------------------------------------------------------------------------------|"
121  << "\n"
122  << "| " + m_processName + " kernel report: "
123  << "\n"
124  << "| ---------------------------------------------------------------------------------------------------------|"
125  << "\n"
126  << "| " << std::left << std::setw(25) << "Event"
127  << "| " << std::right << std::setw(8) << "Loc. (s)"
128  << "| " << std::right << std::setw(8) << "Loc. (%)"
129 #ifdef CH_MPI
130  << "| " << std::right << std::setw(8) << "Min. (s)"
131  << "| " << std::right << std::setw(8) << "Max. (s)"
132  << "| " << std::right << std::setw(8) << "Avg. (s)"
133  << "| " << std::right << std::setw(8) << "Dev. (s)"
134  << "| " << std::right << std::setw(8) << "Min rank"
135  << "| " << std::right << std::setw(8) << "Max rank"
136  << "| "
137 #endif
138  << "\n"
139  << "| ---------------------------------------------------------------------------------------------------------|"
140  << "\n";
141 
142  a_outputStream << header.str();
143 #ifdef _OPENMP
144  }
145 #endif
146 }
147 
148 inline void
149 Timer::printReportTail(std::ostream& a_outputStream, const std::pair<Real, Real> a_elapsedTime) const noexcept
150 {
151 #ifdef _OPENMP
152  if (omp_get_thread_num() == 0) {
153 #endif
154  std::stringstream tail;
155 
156  tail
157  << "| ---------------------------------------------------------------------------------------------------------|"
158  << "\n"
159  << "| Local elapsed time = " << std::fixed << std::setprecision(4) << a_elapsedTime.first << " seconds"
160  << "\n"
161  << "| Global elapsed time = " << std::fixed << std::setprecision(4) << a_elapsedTime.second << " seconds"
162  << "\n"
163  << "| ---------------------------------------------------------------------------------------------------------|"
164  << "\n";
165 
166  a_outputStream << tail.str();
167 #ifdef _OPENMP
168  }
169 #endif
170 }
171 
172 inline void
173 Timer::eventReport(std::ostream& a_outputStream, const bool a_localReportOnly) const noexcept
174 {
175  // This routine prints a header, the timing report for the various event, and a tail. All events are included
176  // but unfinished ones are not counted towards the elapsed time.
177 #ifdef _OPENMP
178  if (omp_get_thread_num() == 0) {
179 #endif
180 
181  this->printReportHeader(a_outputStream);
182 
183  const std::pair<Real, Real> totalTime = this->computeTotalElapsedTime(a_localReportOnly);
184  const Real totalTimeLocal = totalTime.first;
185 
186  for (const auto& e : m_events) {
187 
188  const std::string& eventName = e.first;
189  const bool finishedEvent = std::get<StoppedEvent>(e.second);
190 
191  std::stringstream ssLocalDuration;
192  std::stringstream ssPercentage;
193  std::stringstream ssMinDuration;
194  std::stringstream ssMaxDuration;
195  std::stringstream ssAvgDuration;
196  std::stringstream ssDevDuration;
197  std::stringstream ssMinRank;
198  std::stringstream ssMaxRank;
199 
200  if (finishedEvent) {
201  const Duration localDuration = std::get<ElapsedTime>(e.second);
202  const Real localDurationInSeconds = localDuration.count();
203 
204  ssLocalDuration << std::fixed << std::setprecision(4) << localDurationInSeconds;
205  ssPercentage << std::fixed << std::setprecision(4) << 100. * localDurationInSeconds / totalTimeLocal;
206 
207 #ifdef CH_MPI
208  if (!a_localReportOnly) {
209 
210  const int srcRank = 0;
211  const int numRank = numProc();
212 
213  Vector<Real> allDurations(numRank);
214  gather(allDurations, localDurationInSeconds, srcRank);
215 
216  // Compute average time and standard deviation
217  Real average;
218  Real sigma;
219 
220  Real maxTime = std::numeric_limits<Real>::min();
221  Real minTime = std::numeric_limits<Real>::max();
222 
223  int minRank;
224  int maxRank;
225 
226  if (procID() == 0) {
227 
228  average = 0.0;
229  for (int i = 0; i < allDurations.size(); i++) {
230  average += allDurations[i];
231 
232  if (allDurations[i] < minTime) {
233  minRank = i;
234  minTime = allDurations[i];
235  }
236  if (allDurations[i] > maxTime) {
237  maxRank = i;
238  maxTime = allDurations[i];
239  }
240  }
241  average *= 1. / numRank;
242 
243  sigma = 0.0;
244  for (int i = 0; i < allDurations.size(); i++) {
245  sigma += sqrt(std::pow(allDurations[i] - average, 2));
246  }
247  sigma = sqrt(sigma / numRank);
248  }
249 
250  broadcast(average, srcRank);
251  broadcast(sigma, srcRank);
252  broadcast(maxTime, srcRank);
253  broadcast(minTime, srcRank);
254  broadcast(minRank, srcRank);
255  broadcast(maxRank, srcRank);
256 
257  ssMinDuration << std::fixed << std::setprecision(4) << minTime;
258  ssMaxDuration << std::fixed << std::setprecision(4) << maxTime;
259  ssAvgDuration << std::fixed << std::setprecision(4) << average;
260  ssDevDuration << std::fixed << std::setprecision(4) << sigma;
261  ssMinRank << std::fixed << std::setprecision(4) << minRank;
262  ssMaxRank << std::fixed << std::setprecision(4) << maxRank;
263  }
264  else {
265  ssMinDuration << " N/A ";
266  ssMaxDuration << " N/A ";
267  ssAvgDuration << " N/A ";
268  ssDevDuration << " N/A ";
269  ssMinRank << " N/A ";
270  ssMaxRank << " N/A ";
271  }
272 #endif
273  }
274  else {
275  ssLocalDuration << " - ";
276  ssPercentage << " - ";
277  ssMinDuration << " - ";
278  ssMaxDuration << " - ";
279  ssAvgDuration << " - ";
280  ssDevDuration << " - ";
281  ssMinRank << " - ";
282  ssMaxRank << " - ";
283  }
284 
285  std::stringstream outputString;
286 
287  // Now print to pout
288  outputString << "| " << std::left << std::setw(25) << eventName << "| " << std::right << std::setw(8)
289  << ssLocalDuration.str() << "| " << std::right << std::setw(8) << ssPercentage.str()
290 #ifdef CH_MPI
291  << "| " << std::right << std::setw(8) << ssMinDuration.str() << "| " << std::right << std::setw(8)
292  << ssMaxDuration.str() << "| " << std::right << std::setw(8) << ssAvgDuration.str() << "| "
293  << std::right << std::setw(8) << ssDevDuration.str() << "| " << std::right << std::setw(8)
294  << ssMinRank.str() << "| " << std::right << std::setw(8) << ssMaxRank.str()
295 #endif
296  << "| "
297  << "\n";
298 
299  a_outputStream << outputString.str();
300  }
301 
302  this->printReportTail(a_outputStream, totalTime);
303 
304 #ifdef _OPENMP
305  }
306 #endif
307 }
308 
309 inline std::pair<Real, Real>
310 Timer::computeTotalElapsedTime(const bool a_localReportOnly) const noexcept
311 {
312 
313  Real elapsedTimeLocal = 0.0;
314  Real elapsedTimeGlobal = 0.0;
315 
316  for (const auto& e : m_events) {
317  const bool finishedEvent = std::get<StoppedEvent>(e.second);
318 
319  if (finishedEvent) {
320  const Duration elapsedTime = std::get<ElapsedTime>(e.second);
321  const Real localDurationInSeconds = elapsedTime.count();
322 
323  elapsedTimeLocal += localDurationInSeconds;
324  }
325  }
326 
327 #ifdef CH_MPI
328  if (!a_localReportOnly) {
329  MPI_Allreduce(&elapsedTimeLocal, &elapsedTimeGlobal, 1, MPI_CH_REAL, MPI_MAX, Chombo_MPI::comm);
330  }
331  else {
332  elapsedTimeGlobal = elapsedTimeLocal;
333  }
334 #else
335  elapsedTimeGlobal = elapsedTimeLocal;
336 #endif
337 
338  return std::make_pair(elapsedTimeLocal, elapsedTimeGlobal);
339 }
340 
341 inline void
342 Timer::writeReportToFile(const std::string a_fileName) const noexcept
343 {
344 
345  // First, go through all events and gather the times on the master rank.
346  std::vector<std::string> headerRow;
347  headerRow.emplace_back("# MPI rank");
348 
349  // These are all the times for the finished events. We organize this is a 2D vector which is M x numRank long. Here,
350  // finishedEvents[0] contains the elapsed times for one of the events. The length of finishedEvents[0] is exactly
351  // numRank long
352  std::vector<std::vector<Real>> finishedEvents;
353 
354 #if CH_MPI
355  constexpr int srcRank = 0;
356  const int numRanks = numProc();
357 #else
358  const int numRanks = 1;
359 #endif
360 
361  for (const auto& e : m_events) {
362  const std::string& eventName = e.first;
363  const std::tuple<bool, TimePoint, Duration>& eventData = e.second;
364 
365  const bool finishedEvent = std::get<StoppedEvent>(eventData);
366 
367  // If we had an event that finished, we gather all the data on the master rank.
368  if (finishedEvent) {
369 
370  const Real localEventDuration = std::get<ElapsedTime>(eventData).count();
371  Vector<Real> allTimes(numRanks, 0.0);
372 #ifdef CH_MPI
373  gather(allTimes, localEventDuration, srcRank);
374 #else
375  allTimes[0] = localEventDuration;
376 #endif
377 
378  finishedEvents.emplace_back(allTimes.stdVector());
379  headerRow.emplace_back(eventName);
380  }
381  }
382 
383  // Open a file and start writing.
384 #if CH_MPI
385  if (procID() == srcRank) {
386 #endif
387 #ifdef _OPENMP
388  if (omp_get_thread_num() == 0) {
389 #endif
390  std::ofstream f;
391  f.open(a_fileName, std::ios_base::trunc);
392 
393  const int width = 12;
394  for (int col = 0; col < headerRow.size(); col++) {
395  f << std::left << std::setw(width) << headerRow[col] << "\t";
396  }
397  f << "\n";
398 
399  // Write the data for rank = 0 etc
400  for (int irank = 0; irank < numRanks; irank++) {
401  f << std::left << std::setw(width) << irank << "\t";
402  for (int event = 0; event < finishedEvents.size(); event++) {
403  f << std::left << std::setw(width) << finishedEvents[event][irank] << "\t";
404  }
405  f << "\n";
406  }
407 
408  f.close();
409 #ifdef _OPENMP
410  }
411 #endif
412 #if CH_MPI
413  }
414 #endif
415 }
416 
417 #include <CD_NamespaceFooter.H>
418 
419 #endif
Implementation of CD_Timer.H.
void stopEvent(const std::string a_event) noexcept
Stop an event.
Definition: CD_TimerImplem.H:88
std::chrono::duration< Real > Duration
Duration in seconds.
Definition: CD_Timer.H:46
~Timer()
Destructor.
Definition: CD_TimerImplem.H:47
void startEvent(const std::string a_event) noexcept
Start an event.
Definition: CD_TimerImplem.H:59
std::string m_processName
Process name. Used for input/output.
Definition: CD_Timer.H:135
void writeReportToFile(const std::string a_fileName) const noexcept
Print all timed events to a file.
Definition: CD_TimerImplem.H:342
std::chrono::steady_clock::time_point TimePoint
Point in time.
Definition: CD_Timer.H:41
void printReportHeader(std::ostream &a_outputStream) const noexcept
Print report header.
Definition: CD_TimerImplem.H:113
std::map< std::string, std::tuple< bool, TimePoint, Duration > > m_events
Timer events. First entry is the name of the event.
Definition: CD_Timer.H:141
Timer()=default
Default constructor. This creates a timer without any timer events and without a name.
void printReportTail(std::ostream &a_outputStream, const std::pair< Real, Real > a_elapsedTime) const noexcept
Print report tail.
Definition: CD_TimerImplem.H:149
static Real wallClock()
Static function which returns the time (in seconds) between now and an arbitrary time in the past.
Definition: CD_TimerImplem.H:31
std::pair< Real, Real > computeTotalElapsedTime(const bool a_localReportOnly) const noexcept
Compute the total time for all finished events.
Definition: CD_TimerImplem.H:310
void clear() noexcept
Clear all events.
Definition: CD_TimerImplem.H:53
void eventReport(std::ostream &a_outputStream, const bool a_localReportOnly=false) const noexcept
Print all timed events to cout.
Definition: CD_TimerImplem.H:173
Real max(const Real &a_input) noexcept
Get the maximum of the input, reduced over MPI ranks (in the Chombo communicator)
Definition: CD_ParallelOpsImplem.H:176
std::pair< Real, int > maxRank(const Real &a_val) noexcept
Get the maximum value and the rank having the maximum value.
Definition: CD_ParallelOpsImplem.H:293
Real average(const Real &a_val) noexcept
Compute the average (across MPI ranks) of the input value.
Definition: CD_ParallelOpsImplem.H:500
std::pair< Real, int > minRank(const Real &a_val) noexcept
Get the minimum value and the rank having the minimum value.
Definition: CD_ParallelOpsImplem.H:323
Real min(const Real &a_input) noexcept
Get the minimum of the input, reduced over MPI ranks (in the Chombo communicator)
Definition: CD_ParallelOpsImplem.H:58