Homepage | Demos | Overview | Downloads | Dev. Resources | Reference | Credits |
Profiler.hGo 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 _PROFSECTION_timer(_PROFSECTION_id,&PROF.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.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 //! constructor, but you don't want to construct one of these! Use ProfilerOfSize<x> instead! 00090 Profiler(unsigned int mx); 00091 00092 //! maximum length of names of timers 00093 static const unsigned int MaxSectionNameLen=75; 00094 //! number of slots in the histograms 00095 static const unsigned int HistSize=32; 00096 //! the upper bound (exclusive) of the histograms, in milliseconds. 00097 static const unsigned int HistTime=10*1000; 00098 //! affects how linearly the buckets are distributed - 1 means linear, >1 causes higher resolution for short times 00099 static const float HistCurve; 00100 00101 00102 //! holds all the information needed for book keeping for each timer 00103 struct SectionInfo { 00104 SectionInfo(); //!< constructor 00105 void reset(); //!< resets profiling information 00106 char name[MaxSectionNameLen]; //!< the name of this timer 00107 TimeET totalTime; //!< the total time spent in this section 00108 TimeET lastTime; //!< time of last call, used to calculate #totalInterval, which gives idea of rate of calls 00109 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) 00110 TimeET childTime; //!< the total time spent in child sections 00111 float execExpAvg; //!< exponential average of execution time 00112 float interExpAvg; //!< exponential average of inter-call time 00113 unsigned int execHist[HistSize]; //!< histogram of execution times, uses logarithmic size bins (so high res for quick functions, low res for longer functions) 00114 unsigned int interHist[HistSize]; //!< histogram of inter-call time, uses logarithmic size bins (so high res for quick functions, low res for longer functions) 00115 unsigned int calls; //!< number of calls to this section 00116 }; 00117 00118 //! Measures the time that this class exists, reports result to a profiler 00119 /*! Don't bother trying to use this as a quick timer - just use TimeET directly. 00120 * But there are functions to get the elapsed time and such if you insist. */ 00121 class Timer { 00122 //! Profiler will need to read out some data that no one else should be depending on 00123 friend class Profiler; 00124 public: 00125 Timer() : _prof(NULL), _id(-1U), _parent(-1U), _t() {} //!< constructor - starts timer, but you can restart it... 00126 Timer(unsigned int id, Profiler* prof); //!< constructor - starts the timer, sets current timer in @a prof 00127 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 00128 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 00129 ~Timer(); //!< destructor - stops the timer, reports results 00130 void setID(unsigned int id, Profiler* prof); //!< sets the ID and profiler, also starts timer 00131 void start() { _t.Set(); } //!< starts timer (or resets it) 00132 const TimeET& startTime() { return _t; } //!< returns time of start 00133 TimeET elapsed() { return _t.Age(); } //!< returns time since start 00134 protected: 00135 Profiler* _prof; //!< the profiler this should report to 00136 unsigned int _id; //!< the id number for this code section (See example in beginning of class documentation for how these are assigned) 00137 unsigned int _parent; //!< the id number of the timer this timer is under 00138 TimeET _t; //!< the time this timer was created 00139 }; 00140 00141 //! call this to get a new ID number 00142 unsigned int getNewID(const char* name); 00143 00144 //! called during process init (before any profiled sections) 00145 static void initBuckets(); 00146 00147 //! returns the bucket boundaries 00148 float* getBuckets() { return buckets; } 00149 00150 //! outputs profiling information 00151 std::string report(); 00152 00153 //! resets profiling information 00154 void reset(); 00155 00156 unsigned int curSection; //!< the current timer 00157 TimeET startTime; //!< time of beginning profiling 00158 float gamma; //!< gamma to use with exponential averages (1 to freeze, 0 to set to last) 00159 const unsigned int maxSections; //!< so we can read the size of the infos array back again at runtime 00160 unsigned int sectionsUsed; //!< the number of timer IDs which have been assigned 00161 00162 //! gets the actual storage area of the SectionInfo's 00163 inline SectionInfo* getInfos() { return (SectionInfo*)((char*)this+infosOffset); } 00164 00165 protected: 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 //! Automatically causes initialization of the histogram buckets when the first Profiler is instantiated 00194 class AutoInit { 00195 public: 00196 AutoInit(); 00197 ~AutoInit(); 00198 protected: 00199 static unsigned int refcount; //!< the number of profilers in existance 00200 static unsigned int totalcount; //!< the number of profilers which have been created 00201 } autoInit; 00202 }; 00203 00204 //! templated subclass allows compile-time flexibility of how much memory to use. 00205 template<unsigned int MaxSections> 00206 class ProfilerOfSize { 00207 public: 00208 ProfilerOfSize() : prof(MaxSections) {} //!< constructor 00209 00210 //! call this to get a new ID number 00211 unsigned int getNewID(const char* name) { return prof.getNewID(name); } 00212 00213 //! outputs profiling information 00214 std::string report() { return prof.report(); } 00215 00216 Profiler prof; //!< the profiler that does the work, must immediately preceed #infos! 00217 Profiler::SectionInfo infos[MaxSections]; //!< the actual profiling information storage 00218 }; 00219 00220 /*! @file 00221 * @brief Describes Profiler, which managers a hierarchy of timers for profiling time spent in code 00222 * @author ejt (Creator) 00223 * 00224 * $Author: ejt $ 00225 * $Name: tekkotsu-2_4_1 $ 00226 * $Revision: 1.15 $ 00227 * $State: Exp $ 00228 * $Date: 2005/08/07 04:11:04 $ 00229 */ 00230 00231 #endif |
Tekkotsu v2.4.1 |
Generated Tue Aug 16 16:32:48 2005 by Doxygen 1.4.4 |