Tekkotsu 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 #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 |