Tekkotsu Homepage
Demos
Overview
Downloads
Dev. Resources
Reference
Credits

Profiler.h

Go to the documentation of this file.
00001 //-*-c++-*-
00002 #ifndef INCLUDED_Profiler_h_
00003 #define INCLUDED_Profiler_h_
00004 
00005 #include "TimeET.h"
00006 #include "mathutils.h"
00007 #include <string>
00008 
00009 //! put this at the beginning of any function for which you wish to collect profiling information
00010 /*! Uses a variable named _PROFSECTION_id to store a static ID number - don't redefine or modify that...
00011  *  @param NAME the name of this section for reporting
00012  *  @param PROF the actual profiler to use
00013  */
00014 #ifndef PROFSECTION
00015 #define PROFSECTION(NAME,PROF) \
00016   static unsigned int _PROFSECTION_id=(PROF).getNewID(NAME);\
00017   Profiler::Timer _PROFSECTION_timer(_PROFSECTION_id,&(PROF).prof);
00018 #endif
00019 
00020 //! Manages a hierarchy of timers for profiling time spent in code, gives microsecond resolution
00021 /*! Doesn't use any pointers so it's safe to put this in shared memory regions.\n
00022  *  That's handy so one process can collate all the profiling information across processes
00023  *  to give a summary report to the user.\n
00024  *  
00025  *  Example usage:
00026  *  - Use a static variable to hold an id number for the code section (doesn't necessarily have to be static, but is faster that way)
00027  *  - Create a Profiler::Timer object - its construction marks the 'start' time, and its destructor marks the 'stop' time.
00028  *
00029  *  @code
00030  *  ProfilerOfSize<2> prof; //A global manager for all the sections
00031  *  
00032  *  void f() {
00033  *    static unsigned int id=prof.getNewID("f"); // <== Get the ID number 
00034  *    Profiler::Timer timer(id,&prof.prof);      // <== start the timer
00035  *    //...
00036  *    if(rand()>RAND_MAX/2)
00037  *      return; // destruction of timer occurs automatically!
00038  *    //...
00039  *  } // if we didn't hit the return, timer will otherwise destruct here!
00040  *  @endcode
00041  *
00042  *  However, there's a macro that makes this a one liner:
00043  *  
00044  *  @code
00045  *  void g() {
00046  *    PROFSECTION("g",prof);   // <== Most of the time, this is all you need
00047  *    //...                    // (unless you're doing something fancy like conditional timers)
00048  *    f(); // will note f's time as g's child time, as well as g's own time
00049  *    //...
00050  *  }
00051  *  @endcode
00052  *
00053  *  The idea is similar to that used by MMAccessor.  If you want to profile a section at smaller
00054  *  resolution than a function, you can use tricks shown in MMAccessor's documentation to limit
00055  *  the timer's scope.
00056  *
00057  *  For convenience, there are three global profilers predefined: #mainProfiler, #motionProfiler,
00058  *  and #soundProfiler.  These are what are polled by the ProfilerCheckControl -- if you instantiate
00059  *  a new profiler, you will have to call its report() function yourself to get the results.  (If it's simply
00060  *  a matter of running out of sections in mainProfiler, increase the template parameter at the end
00061  *  of Profiler.h)  Keep in mind however, that these global profilers are pointers, and need to be
00062  *  dereferenced to use with the macro, e.g. <code>PROFSECTION("g2",*mainProfiler)</code>
00063  *
00064  *  Here were the constraints I set for myself:
00065  *  - Processes can read each other's Profilers - so must be able to live in shared memory\n
00066  *    This is so one process can generate a report on performance of the entire system at once
00067  *  - Flexible memory footprint\n
00068  *    MainObject will probably have a lot of subsections.  MotionObject won't.  Since SectionInfo
00069  *    takes some significant memory space, we don't want to force MotionObject to needlessly make
00070  *    a lot of them.
00071  *  - Flexible usage - can have a single generic global, as well as creating multiple
00072  *  - Fast - don't want to kill performance of profiled sections, or throw off reported results
00073  *
00074  *  Consessions made:
00075  *  - Sections are not dynamically allocated
00076  *  - Sections within a Profiler are mutually exclusive (otherwise curSection won't be reliable)
00077  *  - Results don't try to take affects of pre-emptive multitasking into account.
00078  *
00079  *  Global readability is first priority since generating reports is the primary usage, thus
00080  *  we have to be able to handle being in shared memory space.  This means no virtual functions and
00081  *  no pointer storage.  Unfortunately, this makes the second constraint rather difficult.\n
00082  *  Run-time dynamic allocation is right out.  But the number of sections is set at compile time
00083  *  anyway, so it should be OK to set this at compile time, using a template parameter.\n
00084  *  That gets us 99% of the way there, but it can be burdensome to use this since the template
00085  *  means there's no consistant type for all profilers - you can't have a generic Profiler type
00086  *  if it's templated - you would have to know the size of the profiler you're referring to.\n
00087  *  That kind of brings in the third constraint... Instead of accepting a single global, I
00088  *  decided to make a general base (Profiler) and then a templated subclass to hold the bulk data
00089  *  section.  This has the nice side affect of not having to specify the bulk of the code in the
00090  *  header, but has the downside that accessing the info stored in the subclass from the super class
00091  *  is very much a hack.  If you think you can get around this, good luck!
00092  *
00093  *  @note This could be made much prettier if we didn't care about the virtual function-shared
00094  *  memory problems... sigh
00095  */
00096 class Profiler {
00097 public:
00098   //! constructor, but you don't want to construct one of these!  Use ProfilerOfSize<x> instead!
00099   Profiler(unsigned int mx);
00100   
00101   //! maximum length of names of timers
00102   static const unsigned int MaxSectionNameLen=75;
00103   //! number of slots in the histograms
00104   static const unsigned int HistSize=32;
00105   //! the upper bound (exclusive) of the histograms, in milliseconds.
00106   static const unsigned int HistTime=10*1000;
00107   //! affects how linearly the buckets are distributed - 1 means linear, >1 causes higher resolution for short times
00108   static const float HistCurve;
00109   
00110     
00111   //! holds all the information needed for book keeping for each timer
00112   struct SectionInfo {
00113     SectionInfo();                     //!< constructor
00114     void reset();                      //!< resets profiling information
00115     char name[MaxSectionNameLen];      //!< the name of this timer
00116     TimeET totalTime;                  //!< the total time spent in this section
00117     TimeET lastTime;                   //!< time of last call, used to calculate #totalInterval, which gives idea of rate of calls
00118     TimeET totalInterval;              //!< the total time spent between calls (not time between end of one and start of next, is time between start of one and start of next)
00119     TimeET childTime;                  //!< the total time spent in child sections
00120     float execExpAvg;                 //!< exponential average of execution time
00121     float interExpAvg;                //!< exponential average of inter-call time
00122     unsigned int execHist[HistSize];   //!< histogram of execution times, uses logarithmic size bins (so high res for quick functions, low res for longer functions)
00123     unsigned int interHist[HistSize];  //!< histogram of inter-call time, uses logarithmic size bins (so high res for quick functions, low res for longer functions)
00124     unsigned int calls;                //!< number of calls to this section
00125   };    
00126   
00127   //! Measures the time that this class exists, reports result to a profiler
00128   /*! Don't bother trying to use this as a quick timer - just use TimeET directly.
00129    *  But there are functions to get the elapsed time and such if you insist.  */
00130   class Timer {
00131     //! Profiler will need to read out some data that no one else should be depending on
00132     friend class Profiler;
00133   public:
00134     Timer() : _prof(NULL), _id(-1U), _parent(-1U), _t() {} //!< constructor - starts timer, but you can restart it...
00135     Timer(unsigned int id, Profiler* prof); //!< constructor - starts the timer, sets current timer in @a prof
00136     Timer(const Timer& t) : _prof(t._prof), _id(t._id), _parent(t._parent),_t(t._t) { } //!< copy constructor, not that you should need it, does same as default
00137     Timer& operator=(const Timer& t) { _prof=t._prof; _id=t._id; _parent=t._parent; _t=t._t; return *this; } //!< not that you should need it, does same as default
00138     ~Timer(); //!< destructor - stops the timer, reports results
00139     void setID(unsigned int id, Profiler* prof); //!< sets the ID and profiler, also starts timer
00140     void start() { _t.Set(); } //!< starts timer (or resets it)
00141     const TimeET& startTime() { return _t; } //!< returns time of start
00142     TimeET elapsed() { return _t.Age(); } //!< returns time since start
00143   protected:
00144     Profiler* _prof;      //!< the profiler this should report to
00145     unsigned int _id;     //!< the id number for this code section (See example in beginning of class documentation for how these are assigned)
00146     unsigned int _parent; //!< the id number of the timer this timer is under
00147     TimeET _t;            //!< the time this timer was created
00148   };
00149   
00150   //! call this to get a new ID number
00151   unsigned int getNewID(const char* name);
00152 
00153   //! called during process init (before any profiled sections)
00154   static void initBuckets();
00155 
00156   //! returns the bucket boundaries
00157   float* getBuckets() { return buckets; }
00158 
00159   //! outputs profiling information
00160   std::string report();
00161 
00162   //! resets profiling information
00163   void reset();
00164     
00165   unsigned int curSection;         //!< the current timer
00166   TimeET startTime;                //!< time of beginning profiling
00167   float gamma;                    //!< gamma to use with exponential averages (1 to freeze, 0 to set to last)
00168   const unsigned int maxSections;  //!< so we can read the size of the infos array back again at runtime
00169   unsigned int sectionsUsed;       //!< the number of timer IDs which have been assigned
00170 
00171   //! gets the actual storage area of the SectionInfo's
00172   inline SectionInfo* getInfos() { return (SectionInfo*)((char*)this+infosOffset); }
00173     
00174 protected:
00175   //! Only the Timer's should be calling setCurrent() and finished() upon the Timer's construction and destruction
00176   friend class Timer;
00177   //! called automatically by Timer() - sets the current timer
00178   void setCurrent(Timer& tr);
00179   //! called automatically by ~Timer() - notes the specified timer as finished (doesn't check if the timer is actually the current one - don't screw up!)
00180   void finished(Timer& tr);
00181   
00182   //! returns which bucket a time should go in, does a binary search over buckets (unless someone things a log() call would be faster...)
00183   unsigned int getBucket(float t) {
00184     unsigned int l=0;          //inclusive
00185     unsigned int h=HistSize-1; //inclusive
00186     unsigned int c=(h+l)/2;    //current bucket
00187     while(l!=h) {
00188       //      std::cout << this << ' ' << t << '\t' << l << ' ' << c << ' ' << h <<std::endl;
00189       if(t>buckets[c])
00190         l=c+1;
00191       else
00192         h=c;
00193       c=(h+l)/2;
00194     }
00195     return h;
00196   }
00197     
00198   static float buckets[HistSize];  //!< holds boundaries for each bucket
00199 
00200   static unsigned int infosOffset; //!< NASTY HACK - this is how we get around using virtual functions
00201   
00202   //! Automatically causes initialization of the histogram buckets when the first Profiler is instantiated
00203   class AutoInit {
00204   public:
00205     AutoInit(); //!< constructor, adds to #refcount and #totalcount, causes initalization if was 0
00206     ~AutoInit(); //!< destructor, decrements #refcount, does teardown if it hits 0
00207   protected:
00208     static unsigned int refcount; //!< the number of profilers in existance
00209     static unsigned int totalcount; //!< the number of profilers which have been created
00210   } autoInit;
00211 };
00212 
00213 //! templated subclass allows compile-time flexibility of how much memory to use.
00214 template<unsigned int MaxSections>
00215 class ProfilerOfSize {
00216 public:
00217   ProfilerOfSize() : prof(MaxSections) {} //!< constructor
00218   
00219   //! call this to get a new ID number
00220   unsigned int getNewID(const char* name) { return prof.getNewID(name); }
00221   
00222   //! outputs profiling information
00223   std::string report() { return prof.report(); }
00224   
00225   Profiler prof; //!< the profiler that does the work, must immediately preceed #infos!
00226   Profiler::SectionInfo infos[MaxSections]; //!< the actual profiling information storage
00227 };
00228 
00229 // feel free to ignore these or add your own as well -- these are the ones that framework-included code will use
00230 typedef ProfilerOfSize<20> mainProfiler_t; //!< defines type for code profiling information for MainObject; use this instead of the templated type so you don't rely on a particular size
00231 typedef ProfilerOfSize<6> motionProfiler_t; //!< defines type for code profiling information for MotionObject; use this instead of the templated type so you don't rely on a particular size
00232 typedef ProfilerOfSize<6> soundProfiler_t; //!< defines type for code profiling information for SoundPlay; use this instead of the templated type so you don't rely on a particular size
00233 extern mainProfiler_t * mainProfiler; //!< holds code profiling information for MainObject
00234 extern motionProfiler_t * motionProfiler; //!< holds code profiling information for MotoObject
00235 extern soundProfiler_t * soundProfiler; //!< holds code profiling information for SoundPlay
00236 
00237 /*! @file
00238  * @brief Describes Profiler, which managers a hierarchy of timers for profiling time spent in code
00239  * @author ejt (Creator)
00240  *
00241  * $Author: ejt $
00242  * $Name: tekkotsu-4_0 $
00243  * $Revision: 1.17 $
00244  * $State: Exp $
00245  * $Date: 2006/09/16 17:32:40 $
00246  */
00247 
00248 #endif

Tekkotsu v4.0
Generated Thu Nov 22 00:54:55 2007 by Doxygen 1.5.4