]> git.tdb.fi Git - libs/core.git/commitdiff
Add Debug::Profiler
authorMikko Rasa <tdb@tdb.fi>
Fri, 12 Oct 2007 19:20:54 +0000 (19:20 +0000)
committerMikko Rasa <tdb@tdb.fi>
Fri, 12 Oct 2007 19:20:54 +0000 (19:20 +0000)
source/debug/profiler.cpp [new file with mode: 0644]
source/debug/profiler.h [new file with mode: 0644]
source/debug/profilingscope.cpp [new file with mode: 0644]
source/debug/profilingscope.h [new file with mode: 0644]

diff --git a/source/debug/profiler.cpp b/source/debug/profiler.cpp
new file mode 100644 (file)
index 0000000..4251797
--- /dev/null
@@ -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<string, ScopeInfo>::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<string, ScopeInfo>::iterator i=scopes.insert(map<string, ScopeInfo>::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<string, ScopeInfo>::iterator i=scopes.find(scope_name);
+       if(i==scopes.end())
+       {
+               i=scopes.insert(map<string, ScopeInfo>::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<string, ScopeInfo>::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 (file)
index 0000000..f2d5812
--- /dev/null
@@ -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 <map>
+#include <string>
+#include <vector>
+#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<Time::TimeDelta> history;
+               unsigned hist_pos;
+               std::map<std::string, unsigned> called_from;
+
+               ScopeInfo();
+       };
+
+private:
+       unsigned period;
+       std::map<std::string, ScopeInfo> 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 (file)
index 0000000..f92749b
--- /dev/null
@@ -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 (file)
index 0000000..132c418
--- /dev/null
@@ -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