gKitGL
ProfilerClock.h
00001 
00002 #ifndef _GK_PROFILER_CLOCK
00003 #define _GK_PROFILER_CLOCK
00004 
00005 #include <string>
00006 #include <map>
00007 #include <vector>
00008 #include <cstdio>
00009 #include <cassert>
00010 #include <limits.h>
00011 
00012 #ifdef APPLE_OSX
00013     #include <sys/time.h>
00014     
00015 #elif defined WIN32
00016     #include <windows.h>
00017     
00018 #else
00019     #include <sys/time.h>
00020 #endif
00021 
00022 #include <cstdlib>
00023 
00024 namespace gk {
00025     
00026 #ifndef WIN32
00027 // posix version
00028 //! horloge systeme, duree mesuree en micro secondes.
00029 class ProfilerClock
00030 {
00031     // non copyable
00032     ProfilerClock( const ProfilerClock& );
00033     ProfilerClock& operator=( const ProfilerClock& );
00034     
00035 protected:
00036     struct timeval m_start;
00037 
00038     //! constructeur prive, singleton, cf manager().
00039     ProfilerClock( )
00040     {
00041         gettimeofday(&m_start, NULL);
00042     }
00043     
00044     //! destructeur.
00045     ~ProfilerClock() {}
00046 
00047     int delay( const struct timeval& x, const struct timeval& base ) const
00048     {
00049         struct timeval y= base;
00050         
00051         /* Perform the carry for the later subtraction by updating y. */
00052         if(x.tv_usec < y.tv_usec)
00053         {
00054             int nsec = (y.tv_usec - x.tv_usec) / 1000000 + 1;
00055             y.tv_usec -= 1000000 * nsec;
00056             y.tv_sec += nsec;
00057         }
00058         
00059         if (x.tv_usec - y.tv_usec > 1000000)
00060         {
00061             int nsec = (x.tv_usec - y.tv_usec) / 1000000;
00062             y.tv_usec += 1000000 * nsec;
00063             y.tv_sec -= nsec;
00064         }
00065         
00066         int sec = x.tv_sec - y.tv_sec;
00067         int usec = x.tv_usec - y.tv_usec;
00068         
00069         return sec * 1000000 + usec;
00070     }
00071 
00072     int get_ticks( )
00073     {
00074         struct timeval ticks;
00075         gettimeofday(&ticks, NULL);
00076         
00077         //~ printf("ticks %llus %dus - %llus %dus= %dus\n",
00078             //~ (long long unsigned int) ticks.tv_sec, (int) ticks.tv_usec,
00079             //~ (long long unsigned int) m_start.tv_sec, (int) m_start.tv_usec,
00080             //~ delay(ticks, m_start));
00081         
00082         return delay(ticks, m_start);
00083     }
00084 
00085     int get_delay( const unsigned int start )
00086     {
00087         int stop= get_ticks();
00088         return stop - start;
00089     }
00090     
00091 public:
00092     //! type des mesures renvoyee par l'horloge systeme.
00093     typedef int Ticks;
00094 
00095     //! renvoie l'horloge systeme.
00096     static
00097     Ticks getTicks( )
00098     {
00099         return manager().get_ticks();
00100     }
00101 
00102     //! renvoie un delai en micro secondes entre le moment actuel et 'base'.
00103     static 
00104     int getDelay( const Ticks base )
00105     {
00106         return manager().get_delay(base);
00107     }
00108     
00109     //! calcule un delai en micro-secondes entre 'start' et 'stop'.
00110     static 
00111     int delay( const Ticks stop, const Ticks start )
00112     {
00113         return stop - start;
00114     }
00115     
00116     //! interface du singleton.
00117     static
00118     ProfilerClock& manager( )  // singleton
00119     {
00120         static ProfilerClock manager;
00121         return manager;
00122     }    
00123 };
00124 
00125 #else
00126 // win32 version
00127 class ProfilerClock
00128 {
00129     // non copyable
00130     ProfilerClock( const ProfilerClock& );
00131     ProfilerClock& operator=( const ProfilerClock& );
00132     
00133     struct clock_val
00134     {
00135         int sec;
00136         int usec;
00137     };
00138 
00139     int delay( const clock_val& x, const clock_val& base ) const
00140     {
00141         clock_val y= base;
00142         
00143         /* Perform the carry for the later subtraction by updating y. */
00144         if(x.usec < y.usec)
00145         {
00146             int nsec = (y.usec - x.usec) / 1000000 + 1;
00147             y.usec -= 1000000 * nsec;
00148             y.sec += nsec;
00149         }
00150         
00151         if (x.usec - y.usec > 1000000)
00152         {
00153             int nsec = (x.usec - y.usec) / 1000000;
00154             y.usec += 1000000 * nsec;
00155             y.sec -= nsec;
00156         }
00157         
00158         int sec = x.sec - y.sec;
00159         int usec = x.usec - y.usec;
00160         
00161         return sec * 1000000 + usec;
00162     }
00163     
00164 protected:
00165     clock_val m_start;
00166     LARGE_INTEGER m_last_frequency;
00167 
00168     ProfilerClock( ) 
00169     {
00170         LARGE_INTEGER ticks;
00171         QueryPerformanceCounter(&ticks);
00172         
00173         LARGE_INTEGER frequency;
00174         QueryPerformanceFrequency(&frequency);
00175         m_last_frequency= frequency;
00176         
00177         m_start.sec= ticks.QuadPart / frequency.QuadPart;
00178         //~ m_start.usec= (ticks.QuadPart % frequency.QuadPart) * 1000000 / frequency.QuadPart;
00179         m_start.usec= (ticks.QuadPart % frequency.QuadPart) / (frequency.QuadPart / 1000000);
00180     }
00181     
00182     ~ProfilerClock() {}
00183 
00184     int get_ticks( )
00185     {
00186         LARGE_INTEGER ticks;
00187         QueryPerformanceCounter(&ticks);
00188         
00189         LARGE_INTEGER frequency;
00190         QueryPerformanceFrequency(&frequency);
00191         if(frequency.QuadPart != m_last_frequency.QuadPart)
00192         {
00193         #if VERBOSE
00194             printf("ProfilerClock( ): frequency scaling...\n");
00195         #endif
00196             m_last_frequency= frequency;
00197         }
00198         
00199         clock_val stop;
00200         stop.sec= ticks.QuadPart / frequency.QuadPart;
00201         //~ stop.usec= (ticks.QuadPart % frequency.QuadPart) * 1000000 / frequency.QuadPart;
00202         stop.usec= (ticks.QuadPart % frequency.QuadPart) / (frequency.QuadPart / 1000000);
00203         return delay(stop, m_start);
00204     }
00205     
00206     int get_delay( const int base )
00207     {
00208         int stop= get_ticks();
00209         return stop - base;
00210     }
00211     
00212 public:
00213     //! type des mesures renvoyee par l'horloge systeme.
00214     typedef int Ticks;
00215 
00216     static
00217     Ticks getTicks( )
00218     {
00219         return manager().get_ticks();
00220     }
00221 
00222     static 
00223     int getDelay( const Ticks base )
00224     {
00225         return manager().get_delay(base);
00226     }
00227     
00228     static 
00229     int delay( const Ticks stop, const Ticks start )
00230     {
00231         return stop - start;
00232     }
00233     
00234     static
00235     ProfilerClock& manager( )  // singleton
00236     {
00237         static ProfilerClock manager;
00238         return manager;
00239     }    
00240 };
00241 
00242 #endif
00243 
00244 //! classe de base des donnees utilisateur associees a un historique de valeurs / cf StatsCounter.
00245 //! permet d'accrocher des informations supplementaires a un StatCounter.
00246 //! les "donnees", une classe derivee de IStatsUserData, sont allouees par l'application et deviennent propriete du StatsCounter.
00247 class IStatsUserData
00248 {
00249 protected:
00250     std::string m_name;
00251     
00252 public:
00253     IStatsUserData( const std::string& name )
00254         :
00255         m_name(name)
00256     {}
00257 
00258     const std::string& name( ) const
00259     {
00260         return m_name;
00261     }
00262     
00263     virtual ~IStatsUserData( ) {}
00264 };
00265 
00266 //! stocke un historique de valeurs.
00267 class StatsCounter
00268 {
00269     std::vector<IStatsUserData *> m_data;
00270     
00271     long long int *m_stats;
00272     int m_head;
00273     int m_tail;
00274     int m_size;
00275     int m_n;
00276     
00277     int m_min;
00278     int m_max;
00279     long long int m_sum;
00280 
00281     // non copyable
00282     StatsCounter( const StatsCounter& );
00283     StatsCounter& operator=( const StatsCounter& );
00284     
00285 public:
00286     //! constructeur, indique la taille de l'historique, le nombre de mesures conservees.
00287     StatsCounter( const int n= 100 )
00288         :
00289         m_head(0),
00290         m_tail(0),
00291         m_size(n),
00292         m_n(0),
00293         m_min(INT_MAX),
00294         m_max(INT_MIN),
00295         m_sum(0)
00296     {
00297         m_stats= new long long int[n];
00298     }
00299     
00300     //! destructeur. detruit egalement les donnees utilisateur, si necessaire, cf attachUserData().
00301     ~StatsCounter( )
00302     {
00303         delete [] m_stats;
00304         
00305         const int n= m_data.size();
00306         for(int i= 0; i < n; i++)
00307             delete m_data[i];
00308     }
00309     
00310     //! attache une donnee utilisateur a l'historique. renvoie son identifiant pour un acces direct, cf userData().
00311     int attachUserData( IStatsUserData *data )
00312     {
00313         if(data == NULL || findUserData(data->name()) != NULL)
00314             return -1;
00315         
00316         m_data.push_back(data);
00317         return 0;
00318     }
00319     
00320     //! recherche une donnee utilisateur, renvoie l'identifiant de la donnee -1 ou en cas d'echec.
00321     IStatsUserData *findUserData( const std::string& name )
00322     {
00323         return findUserData(name.c_str());
00324     }
00325     
00326     //! recherche une donnee utilisateur, renvoie l'identifiant de la donnee -1 ou en cas d'echec.
00327     IStatsUserData *findUserData( const char *name )
00328     {
00329         const int n= (int) m_data.size();
00330         for(int i= 0; i < n; i++)
00331             if(m_data[i]->name() == name)
00332                 return m_data[i];
00333             
00334         return NULL;
00335     }
00336     
00337     //! renvoie la donnee utilisateur associee a l'historique, ou null en cas d'echec.
00338     IStatsUserData *userData( const int id ) 
00339     {
00340         if(id < 0 || id >= (int) m_data.size())
00341             return NULL;
00342         return m_data[id];
00343     }
00344     
00345     //! ajoute une mesure a l'historique.
00346     void push( const int value )
00347     {
00348         if(value < m_min)
00349             m_min= value;
00350         if(value > m_max)
00351             m_max= value;
00352         
00353         m_sum= m_sum + (long long int) value;
00354         if(m_n >= m_size)
00355         {
00356             // la file est pleine, retirer un element de la tete
00357             m_head= (m_head + 1) % m_size;
00358             m_n--;
00359             assert(m_n >= 0);
00360         }
00361         
00362         // ajouter le nouvel element en queue de file
00363         m_stats[m_tail]= m_sum;
00364         m_tail= (m_tail + 1) % m_size;
00365         m_n++;
00366         assert(m_n <= m_size);
00367     }
00368     
00369     //! renvoie les stats sur les mesures inserees.
00370     //! renvoie la mesure min, max et la moyenne glissante sur l'historique (les n dernieres mesures).
00371     //! les parametres peuvent etre NULL pour indiquer que la valeur n'est pas voulue.
00372     void getStats( int *min, float *average, int *max ) const
00373     {
00374         if(min != NULL)
00375             *min= m_min;
00376         if(max != NULL)
00377             *max= m_max;
00378         
00379         if(average != NULL)
00380             // moyenne glissante sur les n dernieres mesures
00381             *average= (m_n != 0) ? (float) (m_stats[(m_tail -1 + m_size) % m_size] - m_stats[m_head]) / (float) m_n : 0.f;
00382     }
00383     
00384     //! renvoie la moyenne glissante calculee sur les n dernieres mesures.
00385     float average( const unsigned int n= 30 ) const
00386     {
00387         if(n >= (unsigned int) m_n)
00388             return 0.f;
00389         
00390         const int i= (m_tail -1 + m_size) % m_size;      // indice 
00391         const int i1= (i - n + m_size) % m_size; // n valeurs avant
00392         return (m_stats[i] - m_stats[i1]) / (float) n;
00393     }
00394     
00395     //! renvoie la derniere mesure.
00396     int last( ) const
00397     {
00398         const int i= (m_tail -1 + m_size) % m_size;      // indice 
00399         const int i1= (i -1 + m_size) % m_size; // valeur precedente
00400         const long long int value= m_stats[i] - m_stats[i1];
00401         return (int) value;
00402     }
00403     
00404     //! renvoie le nombre de mesures dans l'historique.
00405     int statCount( ) const
00406     {
00407         return m_n -1;
00408     }
00409     
00410     //! renvoie la ieme mesure de l'historique. 0 pour la plus ancienne,  statCount() -1 pour la plus recente.
00411     int stat( const int id ) const
00412     {
00413         if(id < 0 || id >= m_n -1)
00414             return 0;
00415         
00416         const int i= (m_head + id +1) % m_size;      // indice 
00417         const int i1= (i -1 + m_size) % m_size; // valeur precedente
00418         return (int) (m_stats[i] - m_stats[i1]);
00419     }
00420     
00421     //! ecrit l'historique dans un fichier texte, une mesure par ligne, utilisable avec gnuplot, par exemple.
00422     int write( const std::string& name ) const
00423     {
00424         FILE *out= fopen(std::string(name+ ".txt").c_str(), "wt");
00425         if(out == NULL)
00426         {
00427             printf("error writing counter data '%s.txt'.\n", name.c_str());
00428             return -1;
00429         }
00430         
00431         int min, max;
00432         float av;
00433         getStats(&min, &av, &max);
00434         
00435         printf("counter '%s': min %d < %f < max %d,  ", name.c_str(), min, av, max);
00436         printf("writing counter history to '%s.txt'.\n", name.c_str());
00437         
00438         fprintf(out, "# min %d < %f < max %d\n", min, av, max);
00439         for(int id= 1; id < m_n; id++)
00440         {
00441             const int i= (m_head + id ) % m_size;      // indice 
00442             const int i1= (i -1 + m_size) % m_size; // valeur precedente
00443             const long long int value= m_stats[i] - m_stats[i1];
00444             
00445             fprintf(out, "%d\n", (int) value);
00446         }
00447         
00448         fclose(out);
00449         return 0;
00450     }
00451 };
00452 
00453 
00454 //! manager de compteurs : conserve l'ensemble des compteurs crees par 
00455 //! l'application et ecrit leur historique dans des fichiers textes portant le nom es compteurs. 
00456 class StatsCounterIO
00457 {
00458     // non copyable
00459     StatsCounterIO( const StatsCounterIO& );
00460     StatsCounterIO& operator=( const StatsCounterIO& );
00461 
00462     std::map<std::string, StatsCounter *> m_counters_map;
00463     std::vector<StatsCounter *> m_counters;
00464     
00465     StatsCounterIO( ) {}
00466     
00467     ~StatsCounterIO( )
00468     {
00469         for(std::map<std::string, StatsCounter *>::iterator 
00470             i= m_counters_map.begin(); i != m_counters_map.end(); ++i)
00471         {
00472             // ecrit les stats dans un fichier texte ... gnuplot ?
00473             assert(i->second != NULL);
00474             i->second->write(i->first);
00475             delete i->second;
00476         }
00477     }
00478     
00479 public:
00480     //! retrouve un compteur d'apres son nom.
00481     StatsCounter *find( const std::string& name )
00482     {
00483         std::map<std::string, StatsCounter *>::iterator found= m_counters_map.find(name);
00484         
00485         if(found == m_counters_map.end())
00486             return NULL;
00487         else
00488             return found->second;        
00489     }
00490     
00491     //! cree un nouveau compteur nomme conservant 'n' mesures.
00492     StatsCounter *create( const std::string& name, const int n )
00493     {
00494         StatsCounter *counter= new StatsCounter(n);
00495         m_counters_map.insert( std::make_pair( name, counter) );
00496         m_counters.push_back(counter);
00497         
00498         return counter;
00499     }
00500 
00501     //! renvoie le nombre de compteurs.
00502     int counterCount( ) const
00503     {
00504         return (int) m_counters.size();
00505     }
00506     
00507     //! renvoie le ieme compteur.
00508     StatsCounter *counter( const int id )
00509     {
00510         return m_counters[id];
00511     }
00512     
00513     //! renvoie un resume de tous les compteurs
00514     std::string getSummaryString( const int n= 30 ) const
00515     {
00516         char tmp[1024];
00517         std::string summary;
00518         
00519         for(std::map<std::string, StatsCounter *>::const_iterator 
00520             i= m_counters_map.begin(); i != m_counters_map.end(); ++i)
00521         {
00522             assert(i->second != NULL);
00523             
00524             const int value= i->second->last();
00525             float av= i->second->average(n);
00526             sprintf(tmp, "%s: %d [%.3f : %d]\n",
00527                 i->first.c_str(), value, av, n);
00528             
00529             summary.append(tmp);
00530         }
00531         
00532         return summary;
00533     }
00534     
00535     //! interface du singleton.
00536     static
00537     StatsCounterIO& manager( )
00538     {
00539         static StatsCounterIO manager;
00540         return manager;
00541     }
00542 };
00543 
00544 
00545 //! mesure le temps d'execution d'un bloc et enregistre la mesure dans un compteur nomme.
00546 /*! exemple d'utilisation :
00547 \code
00548     {
00549         ScopedTimer timer("frame_time");
00550 
00551         // afficher l'image ...
00552     }
00553 \endcode
00554 
00555     lorsque l'application se termine, StatsCounterIO ecrit l'historique des compteurs dans des fichiers texte et affiche un resume de leurs mesures.
00556 */
00557 
00558 class ScopedTimer
00559 {
00560     ProfilerClock::Ticks m_base;
00561     StatsCounter *m_counter;
00562     
00563     // non copyable
00564     ScopedTimer( const ScopedTimer& );
00565     ScopedTimer& operator=( const ScopedTimer& );
00566     
00567     ScopedTimer( );
00568     
00569 public:
00570     //! constructeur, nom et taille de l'historique du compteur (gk::StatsCounter) associe au timer.
00571     ScopedTimer( const std::string& name, const int n= 100 )
00572     {
00573         StatsCounter *counter= StatsCounterIO::manager().find(name);
00574         if(counter == NULL)
00575             counter= StatsCounterIO::manager().create(name, n);
00576         
00577         m_counter= counter;
00578         m_base= ProfilerClock::getTicks();
00579     }
00580     
00581     //! arrete la mesure de temps, l'enregistre dans le compteur et la renvoie.
00582     int stop( )
00583     {
00584         int time= ProfilerClock::getDelay(m_base);
00585         if(m_counter != NULL)
00586             m_counter->push(time);
00587         m_counter= NULL;        // ne comptabilise pas 2 fois la mesure
00588         
00589         return time;
00590     }
00591     
00592     //! destructeur, termine la mesure et l'insere dans le compteur.
00593     ~ScopedTimer( )
00594     {
00595         int time= ProfilerClock::getDelay(m_base);
00596         if(m_counter != NULL)
00597             m_counter->push(time);
00598     }
00599 };
00600 
00601 
00602 //! creation / acces simplifie a un compteur nomme, gk::StatsCounter.
00603 class ScopedCounter
00604 {
00605     StatsCounter *m_counter;
00606     
00607     // non copyable
00608     ScopedCounter( const ScopedCounter& );
00609     ScopedCounter& operator=( const ScopedCounter& );
00610 
00611     ScopedCounter( );
00612     
00613 public:
00614     //! constructeur, nomme le compteur.
00615     ScopedCounter( const std::string& name, const int n= 100 )
00616     {
00617         StatsCounter *counter= StatsCounterIO::manager().find(name);
00618         if(counter == NULL)
00619             counter= StatsCounterIO::manager().create(name, n);
00620         
00621         m_counter= counter;
00622     }
00623 
00624     //! ajoute une valeur a l'historique du compteur.
00625     void push( const int value )
00626     {
00627         if(m_counter != NULL)
00628             m_counter->push(value);
00629     }
00630    
00631     //! destructeur.
00632     ~ScopedCounter( ) {}
00633 };
00634 
00635 
00636 //! retrouve un compteur gk::StatsCounter et renvoie un resume des mesures du compteur. les mesures sont interpretees comme des durees en micro-secondes.
00637 class TimerSummary
00638 {
00639     int m_min;
00640     int m_max;
00641     float m_average;
00642     int m_last;
00643     
00644 public:
00645     //! constructeur, nom du compteur a retrouver.
00646     TimerSummary( const std::string& name )
00647         :
00648         m_min(0),
00649         m_max(0),
00650         m_average(0.f),
00651         m_last(0)
00652     {
00653         StatsCounter *counter= StatsCounterIO::manager().find(name);
00654         if(counter == NULL)
00655             return;
00656         
00657         counter->getStats(&m_min, &m_average, &m_max);
00658         m_last= counter->last();
00659     }
00660     
00661     //! destructeur.
00662     ~TimerSummary( ) {}
00663     
00664     //! renvoie une chaine de caracteres : derniere mesure + stats du compteur.
00665     std::string getSummaryString( ) const
00666     {
00667         char tmp[1024];
00668         sprintf(tmp, "% 5dms % 4dus (min % 5dus < %.3fus < % 5dus)", m_last / 1000, m_last % 1000, 
00669             m_min, m_average, m_max);
00670         return std::string(tmp);
00671     }
00672 };
00673 
00674 //! retrouve un compteur et renvoie un resume des mesures du compteur. les mesures ne sont pas "interpretees", cf TimerSummary
00675 class CounterSummary
00676 {
00677     int m_min;
00678     int m_max;
00679     float m_average;
00680     int m_last;
00681     
00682 public:
00683     //! constructeur, nom du compteur a retrouver.
00684     CounterSummary( const std::string& name )
00685         :
00686         m_min(0),
00687         m_max(0),
00688         m_average(0.f),
00689         m_last(0)
00690     {
00691         StatsCounter *counter= StatsCounterIO::manager().find(name);
00692         if(counter == NULL)
00693             return;
00694         
00695         counter->getStats(&m_min, &m_average, &m_max);
00696         m_last= counter->last();
00697     }
00698     
00699     //! destructeur.
00700     ~CounterSummary( ) {}
00701     
00702     //! renvoie une chaine de caracteres : derniere mesure + stats du compteur.
00703     std::string getSummaryString( ) const
00704     {
00705         char tmp[1024];
00706         sprintf(tmp, "%d (min %d < %.3f < %d)", m_last, 
00707             m_min, m_average, m_max);
00708         return std::string(tmp);
00709     }
00710 };
00711 
00712 }
00713 
00714 #endif
 All Classes Namespaces Functions Variables Typedefs Enumerator Friends