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

Tekkotsu v2.0
Generated Wed Jan 21 03:20:29 2004 by Doxygen 1.3.4