From: Mikko Rasa Date: Fri, 12 Oct 2007 19:20:54 +0000 (+0000) Subject: Add Debug::Profiler X-Git-Tag: 1.0~17 X-Git-Url: http://git.tdb.fi/?p=libs%2Fcore.git;a=commitdiff_plain;h=e7638f74d3e4869020a19dfa1cc700d52373f01c Add Debug::Profiler --- diff --git a/source/debug/profiler.cpp b/source/debug/profiler.cpp new file mode 100644 index 0000000..4251797 --- /dev/null +++ b/source/debug/profiler.cpp @@ -0,0 +1,96 @@ +/* $Id$ + +This file is part of libmspcore +Copyright © 2007 Mikko Rasa, Mikkosoft Productions +Distributed under the LGPL +*/ + +#include "../core/except.h" +#include "../time/units.h" +#include "profiler.h" + +using namespace std; + +namespace Msp { +namespace Debug { + +Profiler::Profiler(): + period(0), + inner(0) +{ } + +void Profiler::set_period(unsigned p) +{ + if(p==period) + return; + + period=p; + for(map::iterator i=scopes.begin(); i!=scopes.end(); ++i) + { + ScopeInfo &si=i->second; + if(p==0) + si.history.clear(); + else + si.history.assign(period, Time::zero); + si.hist_pos=0; + } +} + +void Profiler::add_scope(const std::string &name) +{ + if(!scopes.count(name)) + { + map::iterator i=scopes.insert(map::value_type(name, ScopeInfo())).first; + i->second.history.resize(period); + } +} + +ProfilingScope *Profiler::enter(ProfilingScope *ps) +{ + ProfilingScope *old=inner; + inner=ps; + return old; +} + +void Profiler::record(const string &scope_name, const string &parent, const Time::TimeDelta &time, const Time::TimeDelta &child_t) +{ + map::iterator i=scopes.find(scope_name); + if(i==scopes.end()) + { + i=scopes.insert(map::value_type(scope_name, ScopeInfo())).first; + i->second.history.resize(period); + } + + ScopeInfo &si=i->second; + ++si.calls; + ++si.called_from[parent]; + si.total_time+=time; + si.self_time+=time-child_t; + if(period) + { + si.avg_time+=(time-si.history[si.hist_pos])/period; + si.history[si.hist_pos++]=time; + if(si.hist_pos>=period) + si.hist_pos-=period; + } + else + si.avg_time=si.total_time/si.calls; +} + +const Profiler::ScopeInfo &Profiler::scope(const string &sn) const +{ + map::const_iterator i=scopes.find(sn); + if(i==scopes.end()) + throw KeyError("Unknown scope"); + + return i->second; +} + + +Profiler::ScopeInfo::ScopeInfo(): + calls(0), + hist_pos(0) +{ } + +} // namespace Debug +} // namespace Msp diff --git a/source/debug/profiler.h b/source/debug/profiler.h new file mode 100644 index 0000000..f2d5812 --- /dev/null +++ b/source/debug/profiler.h @@ -0,0 +1,95 @@ +/* $Id$ + +This file is part of libmspcore +Copyright © 2007 Mikko Rasa, Mikkosoft Productions +Distributed under the LGPL +*/ + +#ifndef MSP_DEBUG_PROFILER_H_ +#define MSP_DEBUG_PROFILER_H_ + +#include +#include +#include +#include "../time/timedelta.h" + +namespace Msp { +namespace Debug { + +class ProfilingScope; + +/** +A class for collecting timing data from a program. It's not as efficient as +external profilers, but allows profiling of custom scopes and retrieving the +profiling data at run time. An example usage could be showing realtime +performance statistics of a game or a simulation. + +See also class ProfilingScope. + +Note: This is not thread-safe. To profile multiple threads, create a separate +Profiler for each thread. +*/ +class Profiler +{ +public: + struct ScopeInfo + { + unsigned calls; + Time::TimeDelta total_time; + Time::TimeDelta self_time; + Time::TimeDelta avg_time; + std::vector history; + unsigned hist_pos; + std::map called_from; + + ScopeInfo(); + }; + +private: + unsigned period; + std::map scopes; + ProfilingScope *inner; + +public: + Profiler(); + + /** + Sets the averaging period for timing data, measured in calls. Previous + average timings are cleared. + */ + void set_period(unsigned p); + + /** + Adds a scope without recording any calls to it. Useful if you might need to + access a scope before it has finished for the first time. + */ + void add_scope(const std::string &name); + + /** + Changes the recorded innermost scope pointer and returns the old one. This + is used by ProfilingScope to track child time and should not be called + manually. + */ + ProfilingScope *enter(ProfilingScope *ps); + + /** + Records a call to a scope. You'll probably want to use a ProfilingScope + instead of calling this manually. + + @param sn Scope name + @param pn Parent scope name + @param t Time spent in the scope + @param ct Time spent in child scopes + */ + void record(const std::string &sn, const std::string &pn, const Time::TimeDelta &t, const Time::TimeDelta &ct); + + /** + Returns informations about a scope. + */ + const ScopeInfo &scope(const std::string &) const; +}; + +} // namespace Debug +} // namespace Msp + +#endif diff --git a/source/debug/profilingscope.cpp b/source/debug/profilingscope.cpp new file mode 100644 index 0000000..f92749b --- /dev/null +++ b/source/debug/profilingscope.cpp @@ -0,0 +1,30 @@ +#include "../time/utils.h" +#include "profilingscope.h" + +using namespace std; + +namespace Msp { +namespace Debug { + +ProfilingScope::ProfilingScope(Profiler &p, const string &n): + profiler(p), + name(n), + parent(profiler.enter(this)), + start_t(Time::now()) +{ } + +ProfilingScope::~ProfilingScope() +{ + const Time::TimeDelta dt=start_t-Time::now(); + if(parent) + { + parent->child_t+=dt; + profiler.record(name, parent->name, dt, child_t); + } + else + profiler.record(name, string(), dt, child_t); + profiler.enter(parent); +} + +} // namespace Debug +} // namespace Msp diff --git a/source/debug/profilingscope.h b/source/debug/profilingscope.h new file mode 100644 index 0000000..132c418 --- /dev/null +++ b/source/debug/profilingscope.h @@ -0,0 +1,41 @@ +/* $Id$ + +This file is part of libmspcore +Copyright © 2007 Mikko Rasa, Mikkosoft Productions +Distributed under the LGPL +*/ + +#ifndef MSP_DEBUG_PROFILINGSCOPE_H_ +#define MSP_DEBUG_PROFILINGSCOPE_H_ + +#include "../time/timestamp.h" +#include "profiler.h" + +namespace Msp { +namespace Debug { + +/** +RAII timing class to accompany Profiler. Timing starts when an object is +created and ends when it goes out of scope. If there was another object in an +outer scope, it is notified of the time used in inner scopes. +*/ +class ProfilingScope +{ +private: + Profiler &profiler; + std::string name; + ProfilingScope *parent; + Time::TimeStamp start_t; + Time::TimeDelta child_t; + + ProfilingScope(const ProfilingScope &); + ProfilingScope &operator=(const ProfilingScope &); +public: + ProfilingScope(Profiler &p, const std::string &n); + ~ProfilingScope(); +}; + +} // namespace Debug +} // namespace Msp + +#endif