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

Tekkotsu v2.2.1
Generated Tue Nov 23 16:36:39 2004 by Doxygen 1.3.9.1