1 /*
2 * Software License Agreement (BSD License)
3 *
4 * Copyright (c) 2008-2014, Willow Garage, Inc.
5 * Copyright (c) 2014-2016, Open Source Robotics Foundation
6 * All rights reserved.
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
10 * are met:
11 *
12 * * Redistributions of source code must retain the above copyright
13 * notice, this list of conditions and the following disclaimer.
14 * * Redistributions in binary form must reproduce the above
15 * copyright notice, this list of conditions and the following
16 * disclaimer in the documentation and/or other materials provided
17 * with the distribution.
18 * * Neither the name of Open Source Robotics Foundation nor the names of its
19 * contributors may be used to endorse or promote products derived
20 * from this software without specific prior written permission.
21 *
22 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
23 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
24 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
25 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
26 * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
27 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
28 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
29 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
30 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
31 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
32 * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
33 * POSSIBILITY OF SUCH DAMAGE.
34 */
35
36 /** @author Ioan Sucan */
37
38 #include "fcl/common/detail/profiler.h"
39
40 namespace fcl {
41 namespace detail {
42
43 //==============================================================================
Instance(void)44 Profiler& Profiler::Instance(void)
45 {
46 static Profiler p(true, false);
47 return p;
48 }
49
50 //==============================================================================
Profiler(bool printOnDestroy,bool autoStart)51 Profiler::Profiler(bool printOnDestroy, bool autoStart)
52 : running_(false), printOnDestroy_(printOnDestroy)
53 {
54 if (autoStart)
55 start();
56 }
57
58 //==============================================================================
~Profiler()59 Profiler::~Profiler()
60 {
61 if (printOnDestroy_ && !data_.empty())
62 status();
63 }
64
65 //==============================================================================
Start()66 void Profiler::Start()
67 {
68 Instance().start();
69 }
70
71 //==============================================================================
Stop()72 void Profiler::Stop()
73 {
74 Instance().stop();
75 }
76
77 //==============================================================================
Clear()78 void Profiler::Clear()
79 {
80 Instance().clear();
81 }
82
83 //==============================================================================
start(void)84 void Profiler::start(void)
85 {
86 lock_.lock();
87 if (!running_)
88 {
89 tinfo_.set();
90 running_ = true;
91 }
92 lock_.unlock();
93 }
94
95 //==============================================================================
stop(void)96 void Profiler::stop(void)
97 {
98 lock_.lock();
99 if (running_)
100 {
101 tinfo_.update();
102 running_ = false;
103 }
104 lock_.unlock();
105 }
106
107 //==============================================================================
clear(void)108 void Profiler::clear(void)
109 {
110 lock_.lock();
111 data_.clear();
112 tinfo_ = TimeInfo();
113 if (running_)
114 tinfo_.set();
115 lock_.unlock();
116 }
117
118 //==============================================================================
Event(const std::string & name,const unsigned int times)119 void Profiler::Event(const std::string& name, const unsigned int times)
120 {
121 Instance().event(name, times);
122 }
123
124 //==============================================================================
event(const std::string & name,const unsigned int times)125 void Profiler::event(const std::string &name, const unsigned int times)
126 {
127 lock_.lock();
128 data_[std::this_thread::get_id()].events[name] += times;
129 lock_.unlock();
130 }
131
132 //==============================================================================
Average(const std::string & name,const double value)133 void Profiler::Average(const std::string& name, const double value)
134 {
135 Instance().average(name, value);
136 }
137
138 //==============================================================================
average(const std::string & name,const double value)139 void Profiler::average(const std::string &name, const double value)
140 {
141 lock_.lock();
142 AvgInfo &a = data_[std::this_thread::get_id()].avg[name];
143 a.total += value;
144 a.totalSqr += value*value;
145 a.parts++;
146 lock_.unlock();
147 }
148
149 //==============================================================================
Begin(const std::string & name)150 void Profiler::Begin(const std::string& name)
151 {
152 Instance().begin(name);
153 }
154
155 //==============================================================================
End(const std::string & name)156 void Profiler::End(const std::string& name)
157 {
158 Instance().end(name);
159 }
160
161 //==============================================================================
begin(const std::string & name)162 void Profiler::begin(const std::string &name)
163 {
164 lock_.lock();
165 data_[std::this_thread::get_id()].time[name].set();
166 lock_.unlock();
167 }
168
169 //==============================================================================
end(const std::string & name)170 void Profiler::end(const std::string &name)
171 {
172 lock_.lock();
173 data_[std::this_thread::get_id()].time[name].update();
174 lock_.unlock();
175 }
176
177 //==============================================================================
Status(std::ostream & out,bool merge)178 void Profiler::Status(std::ostream& out, bool merge)
179 {
180 Instance().status(out, merge);
181 }
182
183 //==============================================================================
status(std::ostream & out,bool merge)184 void Profiler::status(std::ostream &out, bool merge)
185 {
186 stop();
187 lock_.lock();
188 printOnDestroy_ = false;
189
190 out << std::endl;
191 out << " *** Profiling statistics. Total counted time : " << time::seconds(tinfo_.total) << " seconds" << std::endl;
192
193 if (merge)
194 {
195 PerThread combined;
196 for (std::map<std::thread::id, PerThread>::const_iterator it = data_.begin() ; it != data_.end() ; ++it)
197 {
198 for (std::map<std::string, unsigned long int>::const_iterator iev = it->second.events.begin() ; iev != it->second.events.end(); ++iev)
199 combined.events[iev->first] += iev->second;
200 for (std::map<std::string, AvgInfo>::const_iterator iavg = it->second.avg.begin() ; iavg != it->second.avg.end(); ++iavg)
201 {
202 combined.avg[iavg->first].total += iavg->second.total;
203 combined.avg[iavg->first].totalSqr += iavg->second.totalSqr;
204 combined.avg[iavg->first].parts += iavg->second.parts;
205 }
206 for (std::map<std::string, TimeInfo>::const_iterator itm = it->second.time.begin() ; itm != it->second.time.end(); ++itm)
207 {
208 TimeInfo &tc = combined.time[itm->first];
209 tc.total = tc.total + itm->second.total;
210 tc.parts = tc.parts + itm->second.parts;
211 if (tc.shortest > itm->second.shortest)
212 tc.shortest = itm->second.shortest;
213 if (tc.longest < itm->second.longest)
214 tc.longest = itm->second.longest;
215 }
216 }
217 printThreadInfo(out, combined);
218 }
219 else
220 for (std::map<std::thread::id, PerThread>::const_iterator it = data_.begin() ; it != data_.end() ; ++it)
221 {
222 out << "Thread " << it->first << ":" << std::endl;
223 printThreadInfo(out, it->second);
224 }
225 lock_.unlock();
226 }
227
228 //==============================================================================
running() const229 bool Profiler::running() const
230 {
231 return running_;
232 }
233
234 //==============================================================================
Running()235 bool Profiler::Running()
236 {
237 return Instance().running();
238 }
239
240 //==============================================================================
241 struct FCL_EXPORT dataIntVal
242 {
243 std::string name;
244 unsigned long int value;
245 };
246
247 //==============================================================================
248 struct FCL_EXPORT SortIntByValue
249 {
operator ()fcl::detail::SortIntByValue250 bool operator()(const dataIntVal &a, const dataIntVal &b) const
251 {
252 return a.value > b.value;
253 }
254 };
255
256 //==============================================================================
257 struct FCL_EXPORT dataDoubleVal
258 {
259 std::string name;
260 double value;
261 };
262
263 //==============================================================================
264 struct FCL_EXPORT SortDoubleByValue
265 {
operator ()fcl::detail::SortDoubleByValue266 bool operator()(const dataDoubleVal &a, const dataDoubleVal &b) const
267 {
268 return a.value > b.value;
269 }
270 };
271
272 //==============================================================================
printThreadInfo(std::ostream & out,const PerThread & data)273 void Profiler::printThreadInfo(std::ostream &out, const PerThread &data)
274 {
275 double total = time::seconds(tinfo_.total);
276
277 std::vector<detail::dataIntVal> events;
278 for (std::map<std::string, unsigned long int>::const_iterator iev = data.events.begin() ; iev != data.events.end() ; ++iev)
279 {
280 detail::dataIntVal next = {iev->first, iev->second};
281 events.push_back(next);
282 }
283 std::sort(events.begin(), events.end(), SortIntByValue());
284 if (!events.empty())
285 out << "Events:" << std::endl;
286 for (unsigned int i = 0 ; i < events.size() ; ++i)
287 out << events[i].name << ": " << events[i].value << std::endl;
288
289 std::vector<detail::dataDoubleVal> avg;
290 for (std::map<std::string, AvgInfo>::const_iterator ia = data.avg.begin() ; ia != data.avg.end() ; ++ia)
291 {
292 detail::dataDoubleVal next = {ia->first, ia->second.total / (double)ia->second.parts};
293 avg.push_back(next);
294 }
295 std::sort(avg.begin(), avg.end(), SortDoubleByValue());
296 if (!avg.empty())
297 out << "Averages:" << std::endl;
298 for (unsigned int i = 0 ; i < avg.size() ; ++i)
299 {
300 const AvgInfo &a = data.avg.find(avg[i].name)->second;
301 out << avg[i].name << ": " << avg[i].value << " (stddev = " <<
302 std::sqrt(std::abs(a.totalSqr - (double)a.parts * avg[i].value * avg[i].value) / ((double)a.parts - 1.)) << ")" << std::endl;
303 }
304
305 std::vector<detail::dataDoubleVal> time;
306
307 for (std::map<std::string, TimeInfo>::const_iterator itm = data.time.begin() ; itm != data.time.end() ; ++itm)
308 {
309 detail::dataDoubleVal next = {itm->first, time::seconds(itm->second.total)};
310 time.push_back(next);
311 }
312
313 std::sort(time.begin(), time.end(), detail::SortDoubleByValue());
314 if (!time.empty())
315 out << "Blocks of time:" << std::endl;
316
317 double unaccounted = total;
318 for (unsigned int i = 0 ; i < time.size() ; ++i)
319 {
320 const TimeInfo &d = data.time.find(time[i].name)->second;
321
322 double tS = time::seconds(d.shortest);
323 double tL = time::seconds(d.longest);
324 out << time[i].name << ": " << time[i].value << "s (" << (100.0 * time[i].value/total) << "%), ["
325 << tS << "s --> " << tL << " s], " << d.parts << " parts";
326 if (d.parts > 0)
327 out << ", " << (time::seconds(d.total) / (double)d.parts) << " s on average";
328 out << std::endl;
329 unaccounted -= time[i].value;
330 }
331 out << "Unaccounted time : " << unaccounted;
332 if (total > 0.0)
333 out << " (" << (100.0 * unaccounted / total) << " %)";
334 out << std::endl;
335
336 out << std::endl;
337 }
338
339 //==============================================================================
TimeInfo()340 Profiler::TimeInfo::TimeInfo()
341 : total(time::seconds(0.)),
342 shortest(time::duration::max()),
343 longest(time::duration::min()),
344 parts(0)
345 {
346 // Do nothing
347 }
348
349 //==============================================================================
set()350 void Profiler::TimeInfo::set()
351 {
352 start = time::now();
353 }
354
355 //==============================================================================
update()356 void Profiler::TimeInfo::update()
357 {
358 const time::duration &dt = time::now() - start;
359
360 if (dt > longest)
361 longest = dt;
362
363 if (dt < shortest)
364 shortest = dt;
365
366 total = total + dt;
367 ++parts;
368 }
369
370 //==============================================================================
ScopedStart(Profiler & prof)371 Profiler::ScopedStart::ScopedStart(Profiler& prof)
372 : prof_(prof), wasRunning_(prof_.running())
373 {
374 if (!wasRunning_)
375 prof_.start();
376 }
377
378 //==============================================================================
~ScopedStart()379 Profiler::ScopedStart::~ScopedStart()
380 {
381 if (!wasRunning_)
382 prof_.stop();
383 }
384
385 //==============================================================================
ScopedBlock(const std::string & name,Profiler & prof)386 Profiler::ScopedBlock::ScopedBlock(const std::string& name, Profiler& prof)
387 : name_(name), prof_(prof)
388 {
389 prof_.begin(name);
390 }
391
392 //==============================================================================
~ScopedBlock()393 Profiler::ScopedBlock::~ScopedBlock()
394 {
395 prof_.end(name_);
396 }
397
398 } // namespace detail
399 } // namespace fcl
400