Homepage | Demos | Overview | Downloads | Tutorials | Reference | Credits |
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 |