gKitGL
|
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