Profiler.cpp
1 /*********************************************************************
2 * Software License Agreement (BSD License)
3 *
4 * Copyright (c) 2008, Willow Garage, Inc.
5 * All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 *
11 * * Redistributions of source code must retain the above copyright
12 * notice, this list of conditions and the following disclaimer.
13 * * Redistributions in binary form must reproduce the above
14 * copyright notice, this list of conditions and the following
15 * disclaimer in the documentation and/or other materials provided
16 * with the distribution.
17 * * Neither the name of the Willow Garage nor the names of its
18 * contributors may be used to endorse or promote products derived
19 * from this software without specific prior written permission.
20 *
21 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
22 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
23 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
24 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
25 * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
26 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
27 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
28 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
29 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
30 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
31 * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
32 * POSSIBILITY OF SUCH DAMAGE.
33 *********************************************************************/
34 
35 
38 #include "ompl/tools/debug/Profiler.h"
39 #include <cmath>
40 
42 {
43  static Profiler p(true, false);
44  return p;
45 }
46 
47 #if ENABLE_PROFILING
48 
49 #include "ompl/util/Console.h"
50 #include <vector>
51 #include <algorithm>
52 #include <sstream>
53 
55 {
56  lock_.lock();
57  if (!running_)
58  {
59  tinfo_.set();
60  running_ = true;
61  }
62  lock_.unlock();
63 }
64 
66 {
67  lock_.lock();
68  if (running_)
69  {
70  tinfo_.update();
71  running_ = false;
72  }
73  lock_.unlock();
74 }
75 
77 {
78  lock_.lock();
79  data_.clear();
80  tinfo_ = TimeInfo();
81  if (running_)
82  tinfo_.set();
83  lock_.unlock();
84 }
85 
86 void ompl::tools::Profiler::event(const std::string &name, const unsigned int times)
87 {
88  lock_.lock();
89  data_[std::this_thread::get_id()].events[name] += times;
90  lock_.unlock();
91 }
92 
93 void ompl::tools::Profiler::average(const std::string &name, const double value)
94 {
95  lock_.lock();
96  AvgInfo &a = data_[std::this_thread::get_id()].avg[name];
97  a.total += value;
98  a.totalSqr += value*value;
99  a.parts++;
100  lock_.unlock();
101 }
102 
103 void ompl::tools::Profiler::begin(const std::string &name)
104 {
105  lock_.lock();
106  data_[std::this_thread::get_id()].time[name].set();
107  lock_.unlock();
108 }
109 
110 void ompl::tools::Profiler::end(const std::string &name)
111 {
112  lock_.lock();
113  data_[std::this_thread::get_id()].time[name].update();
114  lock_.unlock();
115 }
116 
117 void ompl::tools::Profiler::status(std::ostream &out, bool merge)
118 {
119  stop();
120  lock_.lock();
121  printOnDestroy_ = false;
122 
123  out << std::endl;
124  out << " *** Profiling statistics. Total counted time : " << time::seconds(tinfo_.total) << " seconds" << std::endl;
125 
126  if (merge)
127  {
128  PerThread combined;
129  for (std::map<std::thread::id, PerThread>::const_iterator it = data_.begin() ; it != data_.end() ; ++it)
130  {
131  for (std::map<std::string, unsigned long int>::const_iterator iev = it->second.events.begin() ; iev != it->second.events.end(); ++iev)
132  combined.events[iev->first] += iev->second;
133  for (std::map<std::string, AvgInfo>::const_iterator iavg = it->second.avg.begin() ; iavg != it->second.avg.end(); ++iavg)
134  {
135  combined.avg[iavg->first].total += iavg->second.total;
136  combined.avg[iavg->first].totalSqr += iavg->second.totalSqr;
137  combined.avg[iavg->first].parts += iavg->second.parts;
138  }
139  for (std::map<std::string, TimeInfo>::const_iterator itm = it->second.time.begin() ; itm != it->second.time.end(); ++itm)
140  {
141  TimeInfo &tc = combined.time[itm->first];
142  tc.total = tc.total + itm->second.total;
143  tc.parts = tc.parts + itm->second.parts;
144  if (tc.shortest > itm->second.shortest)
145  tc.shortest = itm->second.shortest;
146  if (tc.longest < itm->second.longest)
147  tc.longest = itm->second.longest;
148  }
149  }
150  printThreadInfo(out, combined);
151  }
152  else
153  for (std::map<std::thread::id, PerThread>::const_iterator it = data_.begin() ; it != data_.end() ; ++it)
154  {
155  out << "Thread " << it->first << ":" << std::endl;
156  printThreadInfo(out, it->second);
157  }
158  lock_.unlock();
159 }
160 
162 {
163  std::stringstream ss;
164  ss << std::endl;
165  status(ss, true);
166  OMPL_INFORM(ss.str().c_str());
167 }
168 
170 namespace ompl
171 {
172 
173  struct dataIntVal
174  {
175  std::string name;
176  unsigned long int value;
177  };
178 
179  struct SortIntByValue
180  {
181  bool operator()(const dataIntVal &a, const dataIntVal &b) const
182  {
183  return a.value > b.value;
184  }
185  };
186 
187  struct dataDoubleVal
188  {
189  std::string name;
190  double value;
191  };
192 
193  struct SortDoubleByValue
194  {
195  bool operator()(const dataDoubleVal &a, const dataDoubleVal &b) const
196  {
197  return a.value > b.value;
198  }
199  };
200 }
202 
203 void ompl::tools::Profiler::printThreadInfo(std::ostream &out, const PerThread &data)
204 {
205  double total = time::seconds(tinfo_.total);
206 
207  std::vector<dataIntVal> events;
208  for (std::map<std::string, unsigned long int>::const_iterator iev = data.events.begin() ; iev != data.events.end() ; ++iev)
209  {
210  dataIntVal next = {iev->first, iev->second};
211  events.push_back(next);
212  }
213  std::sort(events.begin(), events.end(), SortIntByValue());
214  if (!events.empty())
215  out << "Events:" << std::endl;
216  for (unsigned int i = 0 ; i < events.size() ; ++i)
217  out << events[i].name << ": " << events[i].value << std::endl;
218 
219  std::vector<dataDoubleVal> avg;
220  for (std::map<std::string, AvgInfo>::const_iterator ia = data.avg.begin() ; ia != data.avg.end() ; ++ia)
221  {
222  dataDoubleVal next = {ia->first, ia->second.total / (double)ia->second.parts};
223  avg.push_back(next);
224  }
225  std::sort(avg.begin(), avg.end(), SortDoubleByValue());
226  if (!avg.empty())
227  out << "Averages:" << std::endl;
228  for (unsigned int i = 0 ; i < avg.size() ; ++i)
229  {
230  const AvgInfo &a = data.avg.find(avg[i].name)->second;
231  out << avg[i].name << ": " << avg[i].value << " (stddev = " <<
232  std::sqrt(std::abs(a.totalSqr - (double)a.parts * avg[i].value * avg[i].value) / ((double)a.parts - 1.)) << ")" << std::endl;
233  }
234 
235  std::vector<dataDoubleVal> time;
236 
237  for (std::map<std::string, TimeInfo>::const_iterator itm = data.time.begin() ; itm != data.time.end() ; ++itm)
238  {
239  dataDoubleVal next = {itm->first, time::seconds(itm->second.total)};
240  time.push_back(next);
241  }
242 
243  std::sort(time.begin(), time.end(), SortDoubleByValue());
244  if (!time.empty())
245  out << "Blocks of time:" << std::endl;
246 
247  double unaccounted = total;
248  for (unsigned int i = 0 ; i < time.size() ; ++i)
249  {
250  const TimeInfo &d = data.time.find(time[i].name)->second;
251 
252  double tS = time::seconds(d.shortest);
253  double tL = time::seconds(d.longest);
254  out << time[i].name << ": " << time[i].value << "s (" << (100.0 * time[i].value/total) << "%), ["
255  << tS << "s --> " << tL << " s], " << d.parts << " parts";
256  if (d.parts > 0)
257  out << ", " << (time::seconds(d.total) / (double)d.parts) << " s on average";
258  out << std::endl;
259  unaccounted -= time[i].value;
260  }
261  // if we do not appear to have counted time multiple times, print the unaccounted time too
262  if (unaccounted >= 0.0)
263  {
264  out << "Unaccounted time : " << unaccounted;
265  if (total > 0.0)
266  out << " (" << (100.0 * unaccounted / total) << " %)";
267  out << std::endl;
268  }
269 
270  out << std::endl;
271 }
272 
273 #endif
void stop()
Stop counting time.
void begin(const std::string &name)
Begin counting time for a specific chunk of code.
void start()
Start counting time.
void end(const std::string &name)
Stop counting time for a specific chunk of code.
void status(std::ostream &out=std::cout, bool merge=true)
Print the status of the profiled code chunks and events. Optionally, computation done by different th...
duration seconds(double sec)
Return the time duration representing a given number of seconds.
Definition: Time.h:78
void event(const std::string &name, const unsigned int times=1)
Count a specific event for a number of times.
This is a simple thread-safe tool for counting time spent in various chunks of code. This is different from external profiling tools in that it allows the user to count time spent in various bits of code (sub-function granularity) or count how many times certain pieces of code are executed.
Definition: Profiler.h:75
void console()
Print the status of the profiled code chunks and events to the console (using msg::Console) ...
void clear()
Clear counted time and events.
static Profiler & Instance()
Return an instance of the class.
Definition: Profiler.cpp:41
void average(const std::string &name, const double value)
Maintain the average of a specific value.
#define OMPL_INFORM(fmt,...)
Log a formatted information string.
Definition: Console.h:68