Homepage | Demos | Overview | Downloads | Tutorials | 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 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_1 $ 00208 * $Revision: 1.11 $ 00209 * $State: Exp $ 00210 * $Date: 2004/02/04 23:06:47 $ 00211 */ 00212 00213 #endif |
Tekkotsu v2.1 |
Generated Tue Mar 16 23:19:15 2004 by Doxygen 1.3.5 |