MRPT  1.9.9
CTimeLogger.cpp
Go to the documentation of this file.
1 /* +------------------------------------------------------------------------+
2  | Mobile Robot Programming Toolkit (MRPT) |
3  | https://www.mrpt.org/ |
4  | |
5  | Copyright (c) 2005-2019, Individual contributors, see AUTHORS file |
6  | See: https://www.mrpt.org/Authors - All rights reserved. |
7  | Released under BSD License. See: https://www.mrpt.org/License |
8  +------------------------------------------------------------------------+ */
9 
10 #include "system-precomp.h" // Precompiled headers
11 
12 #include <mrpt/core/bits_math.h>
14 #include <mrpt/system/datetime.h>
15 #include <mrpt/system/filesystem.h>
17 #include <algorithm>
18 #include <fstream>
19 #include <iostream>
20 
21 using namespace mrpt;
22 using namespace mrpt::system;
23 using namespace std;
24 
26 {
27  MyGlobalProfiler() : mrpt::system::CTimeLogger("MRPT_global_profiler") {}
28  ~MyGlobalProfiler() override
29  {
30  if (!m_data.empty())
31  {
32  const std::string sFil("mrpt-global-profiler.csv");
33  this->saveToCSVFile(sFil);
34  std::cout << "[MRPT global profiler] Write stats to: " << sFil
35  << std::endl;
36  }
37  }
38  MyGlobalProfiler(const MyGlobalProfiler&) = delete;
40  MyGlobalProfiler& operator=(const MyGlobalProfiler&) = delete;
41  MyGlobalProfiler& operator=(MyGlobalProfiler&&) = delete;
42 };
44 
45 namespace mrpt::system
46 {
48 void global_profiler_enter(const char* func_name) noexcept
49 {
50  global_profiler.enter(func_name);
51 }
52 void global_profiler_leave(const char* func_name) noexcept
53 {
54  global_profiler.leave(func_name);
55 }
56 } // namespace mrpt::system
57 
59  bool enabled, const std::string& name, const bool keep_whole_history)
60  : COutputLogger("CTimeLogger"),
61  m_tictac(),
62  m_enabled(enabled),
63  m_name(name),
64  m_keep_whole_history(keep_whole_history)
65 {
66  m_tictac.Tic();
67 }
68 
70 {
71  // Dump all stats:
72  if (!m_data.empty()) // If logging is disabled, do nothing...
73  dumpAllStats();
74 }
75 
77 {
78  if (deep_clear)
79  m_data.clear();
80  else
81  {
82  for (auto& e : m_data) e.second = TCallData();
83  }
84 }
85 
87 {
88  std::string ret;
89 
90  for (size_t p = 0; p < s.size(); p += len)
91  {
92  ret += rightPad(s.substr(p), len, true);
93  if (p + len < s.size()) ret += "\n";
94  }
95  return ret;
96 }
97 
98 void CTimeLogger::getStats(std::map<std::string, TCallStats>& out_stats) const
99 {
100  out_stats.clear();
101  for (const auto& e : m_data)
102  {
103  TCallStats& cs = out_stats[std::string(e.first)];
104  cs.min_t = e.second.min_t;
105  cs.max_t = e.second.max_t;
106  cs.total_t = e.second.mean_t;
107  cs.mean_t = e.second.n_calls ? e.second.mean_t / e.second.n_calls : 0;
108  cs.n_calls = e.second.n_calls;
109  cs.last_t = e.second.last_t;
110  }
111 }
112 
113 std::string CTimeLogger::getStatsAsText(const size_t column_width) const
114 {
115  using std::string;
116  using namespace std::string_literals;
117 
118  string stats_text;
119  string name_tmp = m_name.size() != 0 ? " "s + m_name + ": "s : " "s;
120  string mrpt_string = "MRPT CTimeLogger report "s;
121 
122  string top_header(name_tmp + mrpt_string);
123  // append dashes to the header to reach column_width
124  {
125  const auto space_to_fill = top_header.size() < column_width
126  ? (column_width - top_header.size()) / 2
127  : 2;
128  std::string dashes_half(space_to_fill, '-');
129  top_header = dashes_half + top_header + dashes_half;
130  if (dashes_half.size() % 2)
131  { // what if column_width-top_header.size() is odd?
132  top_header += '-';
133  }
134  }
135 
136  std::string middle_header(
137  " FUNCTION #CALLS MIN.T MEAN.T "
138  "MAX.T TOTAL ");
139  std::string bottom_header(column_width, '-');
140 
141  stats_text += top_header + "\n"s;
142  stats_text += middle_header + "\n"s;
143  stats_text += bottom_header + "\n"s;
144 
145  // for all the timed sections: sort by inserting into a std::map
146  using NameAndCallData = std::map<std::string_view, TCallData>;
147  NameAndCallData stat_strs;
148  for (const auto& i : m_data) stat_strs[i.first] = i.second;
149 
150  // format tree-like patterns like:
151  // ----------
152  // foobar
153  // foobar.a
154  // foobar.b
155  // ----------
156  // like:
157  // ----------
158  // foobar
159  // +-> a
160  // +-> b
161  // ----------
162  std::string last_parent;
163  for (const auto& i : stat_strs)
164  {
165  string line = string(i.first); // make a copy
166 
167  const auto dot_pos = line.find(".");
168  if (dot_pos == std::string::npos)
169  {
170  last_parent = line;
171  }
172  else
173  {
174  const auto parent_pos = line.find(last_parent);
175  if (parent_pos != std::string::npos)
176  {
177  line = "+-> "s + line.substr(dot_pos);
178  }
179  }
180 
181  const string sMinT = unitsFormat(i.second.min_t, 1, false);
182  const string sMaxT = unitsFormat(i.second.max_t, 1, false);
183  const string sTotalT = unitsFormat(i.second.mean_t, 1, false);
184  const string sMeanT = unitsFormat(
185  i.second.n_calls ? i.second.mean_t / i.second.n_calls : 0, 1,
186  false);
187 
188  stats_text += mrpt::format(
189  "%s %7u %6s%c %6s%c %6s%c %6s%c\n",
190  aux_format_string_multilines(line, 39).c_str(),
191  static_cast<unsigned int>(i.second.n_calls), sMinT.c_str(),
192  i.second.has_time_units ? 's' : ' ', sMeanT.c_str(),
193  i.second.has_time_units ? 's' : ' ', sMaxT.c_str(),
194  i.second.has_time_units ? 's' : ' ', sTotalT.c_str(),
195  i.second.has_time_units ? 's' : ' ');
196  }
197 
198  std::string footer(top_header);
199  stats_text += footer + "\n";
200 
201  return stats_text;
202 }
203 
204 void CTimeLogger::saveToCSVFile(const std::string& csv_file) const
205 {
206  std::string s;
207  s += "FUNCTION, #CALLS, LAST.T, MIN.T, MEAN.T, MAX.T, TOTAL.T [, "
208  "WHOLE_HISTORY]\n";
209  for (const auto& i : m_data)
210  {
211  s += format(
212  "\"%.*s\",%7u,%e,%e,%e,%e,%e", static_cast<int>(i.first.size()),
213  i.first.data(), static_cast<unsigned int>(i.second.n_calls),
214  i.second.last_t, i.second.min_t,
215  i.second.n_calls ? i.second.mean_t / i.second.n_calls : 0,
216  i.second.max_t, i.second.mean_t);
217 
218  if (i.second.whole_history)
219  {
220  const auto& wh = i.second.whole_history.value();
221  for (const double v : wh)
222  {
223  s += ", ";
224  s += std::to_string(v);
225  }
226  }
227  s += "\n";
228  }
229  std::ofstream(csv_file) << s;
230 }
231 
232 void CTimeLogger::saveToMFile(const std::string& file) const
233 {
234  using std::string;
235  using namespace std::string_literals;
236 
237  string s;
238  s += "function [s] = "s + mrpt::system::extractFileName(file) +
239  "()\n"
240  "s = struct();\n"
241  "s.whole = struct();\n\n"s;
242 
243  std::string s_names = "s.names={"s;
244  std::string s_counts = "s.count=["s;
245  std::string s_mins = "s.min=["s;
246  std::string s_maxs = "s.max=["s;
247  std::string s_means = "s.mean=["s;
248 
249  for (const auto& i : m_data)
250  {
251  s_names += "'"s + i.first + "',"s;
252  s_counts += std::to_string(i.second.n_calls) + ","s;
253  s_mins += mrpt::format("%e,", i.second.min_t);
254  s_maxs += mrpt::format("%e,", i.second.max_t);
255  s_means += mrpt::format("%e,", i.second.mean_t);
256 
257  if (i.second.whole_history)
258  {
259  string clean_name =
261  std::replace(clean_name.begin(), clean_name.end(), '.', '_');
262  std::replace(clean_name.begin(), clean_name.end(), '-', '_');
263  std::replace(clean_name.begin(), clean_name.end(), '+', '_');
264  std::replace(clean_name.begin(), clean_name.end(), '*', '_');
265 
266  s += "s.whole."s + clean_name + "=[";
267  const auto& wh = i.second.whole_history.value();
268  for (const double v : wh) s += mrpt::format("%e,", v);
269  s += "];\n";
270  }
271  }
272  s_names += "};\n"s;
273  s_counts += "];\n"s;
274  s_mins += "];\n"s;
275  s_maxs += "];\n"s;
276  s_means += "];\n"s;
277 
278  s += s_names;
279  s += s_counts;
280  s += s_mins;
281  s += s_maxs;
282  s += s_means;
283 
284  s += "\n"s;
285  std::ofstream(file) << s;
286 }
287 
288 void CTimeLogger::dumpAllStats(const size_t column_width) const
289 {
290  MRPT_LOG_INFO_STREAM("dumpAllStats:\n" << getStatsAsText(column_width));
291 }
292 
293 void CTimeLogger::do_enter(const std::string_view& func_name)
294 {
295  TCallData& d = m_data[std::string(func_name)];
296 
297  d.n_calls++;
298  d.open_calls.push(0); // Dummy value, it'll be written below
299  d.open_calls.top() = m_tictac.Tac(); // to avoid possible delays.
300 }
301 
302 double CTimeLogger::do_leave(const std::string_view& func_name)
303 {
304  const double tim = m_tictac.Tac();
305 
306  TCallData& d = m_data[std::string(func_name)];
307 
308  if (!d.open_calls.empty())
309  {
310  const double At = tim - d.open_calls.top();
311  d.open_calls.pop();
312 
313  d.last_t = At;
314  d.mean_t += At;
315  if (d.n_calls == 1)
316  {
317  d.min_t = At;
318  d.max_t = At;
319  }
320  else
321  {
322  mrpt::keep_min(d.min_t, At);
323  mrpt::keep_max(d.max_t, At);
324  }
326  {
327  // Init the first time:
328  if (!d.whole_history)
329  d.whole_history = decltype(d.whole_history)::value_type();
330  // Append to history:
331  d.whole_history.value().push_back(At);
332  }
333  return At;
334  }
335  else
336  return 0; // This shouldn't happen!
337 }
338 
340  const std::string_view& event_name, const double value, const bool is_time)
341 {
342  if (!m_enabled) return;
343  TCallData& d = m_data[std::string(event_name)];
344 
345  d.has_time_units = is_time;
346  d.last_t = value;
347  d.mean_t += value;
348  if (++d.n_calls == 1)
349  {
350  d.min_t = value;
351  d.max_t = value;
352  }
353  else
354  {
357  }
359  {
360  // Init the first time:
361  if (!d.whole_history)
362  d.whole_history = decltype(d.whole_history)::value_type();
363  // Append to history:
364  d.whole_history.value().push_back(value);
365  }
366 }
367 
369 
371 {
372  TDataMap::const_iterator it = m_data.find(name);
373  if (it == m_data.end())
374  return 0;
375  else
376  return it->second.n_calls ? it->second.mean_t / it->second.n_calls : 0;
377 }
379 {
380  TDataMap::const_iterator it = m_data.find(name);
381  if (it == m_data.end())
382  return 0;
383  else
384  return it->second.last_t;
385 }
386 
388  const CTimeLogger& logger, const std::string_view& section_name)
389  : m_logger(const_cast<CTimeLogger&>(logger)), m_section_name(section_name)
390 {
392 }
394 {
395  if (stopped_) return;
396  const auto leave = mrpt::Clock::now();
397  const double dt = mrpt::system::timeDifference(m_entry, leave);
398 
400  stopped_ = true;
401 }
402 
404 
406 {
407  using namespace std::string_literals;
408  auto name = m_tm.getName() + ".m"s;
410 
411  m_tm.logStr(
412  LVL_INFO, "[CTimeLoggerSaveAtDtor] Saving stats to: `"s + name + "`"s);
414 }
double Tac() noexcept
Stops the stopwatch.
Definition: CTicTac.cpp:86
const std::string & getName() const
bool stopped_
void clear(bool deep_clear=false)
Resets all stats.
Definition: CTimeLogger.cpp:76
CTimeLogger & m_logger
mrpt::system::CTimeLogger & m_tm
void dumpAllStats(const size_t column_width=80) const
Dump all stats through the COutputLogger interface.
STL namespace.
std::string fileNameStripInvalidChars(const std::string &filename, const char replacement_to_invalid_chars='_')
Replace invalid filename chars by underscores (&#39;_&#39;) or any other user-given char. ...
Definition: filesystem.cpp:329
~MyGlobalProfiler() override
Definition: CTimeLogger.cpp:28
mrpt::system::CTimeLogger & global_profiler_getref() noexcept
Definition: CTimeLogger.cpp:47
void keep_min(T &var, const K test_val)
If the second argument is below the first one, set the first argument to this lower value...
double getLastTime(const std::string &name) const
Return the last execution time of the given "section", or 0 if it hasn&#39;t ever been called "enter" wit...
void getStats(std::map< std::string, TCallStats > &out_stats) const
Returns all the current stats as a map: section_name => stats.
Definition: CTimeLogger.cpp:98
static time_point now() noexcept
Returns the current time, with the highest resolution available.
Definition: Clock.cpp:46
GLdouble s
Definition: glext.h:3682
GLenum GLsizei len
Definition: glext.h:4756
void registerUserMeasure(const std::string_view &event_name, const double value, const bool is_time=false)
void stop()
for correct use, see docs for CTimeLoggerEntry
std::string getStatsAsText(const size_t column_width=80) const
Dump all stats to a multi-line text string.
static MyGlobalProfiler global_profiler
Definition: CTimeLogger.cpp:43
std::stack< double, std::vector< double > > open_calls
void global_profiler_enter(const char *func_name) noexcept
Definition: CTimeLogger.cpp:48
void keep_max(T &var, const K test_val)
If the second argument is above the first one, set the first argument to this higher value...
void saveToMFile(const std::string &m_file) const
Dump all stats to a Matlab/Octave (.m) file.
std::optional< std::deque< double > > whole_history
Versatile class for consistent logging and management of output messages.
const std::string m_section_name
double do_leave(const std::string_view &func_name)
CTimeLogger(bool enabled=true, const std::string &name="", const bool keep_whole_history=false)
Definition: CTimeLogger.cpp:58
mrpt::Clock::time_point m_entry
GLsizei const GLchar ** string
Definition: glext.h:4116
double getMeanTime(const std::string &name) const
Return the mean execution time of the given "section", or 0 if it hasn&#39;t ever been called "enter" wit...
#define MRPT_LOG_INFO_STREAM(__CONTENTS)
void global_profiler_leave(const char *func_name) noexcept
Definition: CTimeLogger.cpp:52
const GLdouble * v
Definition: glext.h:3684
This is the global namespace for all Mobile Robot Programming Toolkit (MRPT) libraries.
std::string unitsFormat(const double val, int nDecimalDigits=2, bool middle_space=true)
This function implements formatting with the appropriate SI metric unit prefix: 1e-12->&#39;p&#39;, 1e-9->&#39;n&#39;, 1e-6->&#39;u&#39;, 1e-3->&#39;m&#39;, 1->&#39;&#39;, 1e3->&#39;K&#39;, 1e6->&#39;M&#39;, 1e9->&#39;G&#39;, 1e12->&#39;T&#39;.
A versatile "profiler" that logs the time spent within each pair of calls to enter(X)-leave(X), among other stats.
void saveToCSVFile(const std::string &csv_file) const
Dump all stats to a Comma Separated Values (CSV) file.
void deep_clear(CONTAINER &c)
Deep clear for a std vector container.
std::string format(const char *fmt,...) MRPT_printf_format_check(1
A std::string version of C sprintf.
Definition: format.cpp:16
Data of each call section: # of calls, minimum, maximum, average and overall execution time (in secon...
std::string aux_format_string_multilines(const std::string &s, const size_t len)
Definition: CTimeLogger.cpp:86
GLuint const GLchar * name
Definition: glext.h:4068
~CTimeLoggerEntry()
std::string rightPad(const std::string &str, const size_t total_len, bool truncate_if_larger=false)
Enlarge the string with spaces up to the given length.
std::string std::string to_string(T v)
Just like std::to_string(), but with an overloaded version for std::string arguments.
Definition: format.h:30
GLsizei const GLfloat * value
Definition: glext.h:4134
double timeDifference(const mrpt::system::TTimeStamp t_first, const mrpt::system::TTimeStamp t_later)
Returns the time difference from t1 to t2 (positive if t2 is posterior to t1), in seconds...
Definition: datetime.h:123
void Tic() noexcept
Starts the stopwatch.
Definition: CTicTac.cpp:75
void do_enter(const std::string_view &func_name)
std::string extractFileName(const std::string &filePath)
Extract just the name (without extension) of a filename from a complete path plus name plus extension...
Definition: filesystem.cpp:62
void logStr(const VerbosityLevel level, const std::string &msg_str) const
Main method to add the specified message string to the logger.
GLfloat GLfloat p
Definition: glext.h:6398
void enter(const std::string_view &func_name)
Start of a named section.
CTimeLoggerEntry(const CTimeLogger &logger, const std::string_view &section_name)
double leave(const std::string_view &func_name)
End of a named section.
~CTimeLogger() override
Destructor.
Definition: CTimeLogger.cpp:69



Page generated by Doxygen 1.8.14 for MRPT 1.9.9 Git: 8fe78517f Sun Jul 14 19:43:28 2019 +0200 at lun oct 28 02:10:00 CET 2019