00001 
00002 
00003 
00004 
00005 
00006 
00007 
00008 
00009 
00010 
00011 
00012 
00013 
00014 
00015 
00016 
00017 
00018 
00019 
00020 
00021 
00022 
00023 
00024 
00025 
00026 
00027 
00028 
00029 #ifndef QUICK_PROF_H
00030 #define QUICK_PROF_H
00031 
00032 #include <iostream>
00033 #include <fstream>
00034 #include <sstream>
00035 #include <map>
00036 #include <math.h>
00037 
00038 #if defined(WIN32) || defined(_WIN32)
00039         #define USE_WINDOWS_TIMERS
00040         #include <windows.h>
00041         #include <time.h>
00042 #else
00043         #include <sys/time.h>
00044 #endif
00045 
00046 
00047 
00048 
00049 
00050 
00051 
00052 #define PROFILER quickprof::Profiler::instance()
00053 
00054 
00055 namespace quickprof
00056 {
00057 
00058 
00059         struct ProfileBlock
00060         {
00061                 ProfileBlock()
00062                 {
00063                         currentBlockStartMicroseconds = 0;
00064                         currentCycleTotalMicroseconds = 0;
00065                         avgCycleTotalMicroseconds = 0;
00066                         totalMicroseconds = 0;
00067                 }
00068 
00069 
00070                 unsigned long long int currentBlockStartMicroseconds;
00071 
00072 
00073 
00074                 unsigned long long int currentCycleTotalMicroseconds;
00075 
00076 
00077 
00078                 double avgCycleTotalMicroseconds;
00079 
00080 
00081                 unsigned long long int totalMicroseconds;
00082         };
00083 
00084 
00085 
00086         class Clock
00087         {
00088         public:
00089                 Clock()
00090                 {
00091 #ifdef USE_WINDOWS_TIMERS
00092                         QueryPerformanceFrequency(&mClockFrequency);
00093 #endif
00094                         reset();
00095                 }
00096 
00097                 ~Clock()
00098                 {
00099                 }
00100 
00101 
00102 
00103 
00104                 void reset()
00105                 {
00106 #ifdef USE_WINDOWS_TIMERS
00107                         QueryPerformanceCounter(&mStartTime);
00108                         mStartTick = GetTickCount();
00109                         mPrevClockCycles = 0;
00110 #else
00111                         gettimeofday(&mStartTime, NULL);
00112 #endif
00113                 }
00114 
00115 
00116 
00117 
00118 
00119 
00120 
00121 
00122 
00123                 unsigned long long int getTimeMicroseconds()
00124                 {
00125 #ifdef USE_WINDOWS_TIMERS
00126                         
00127                         
00128                         
00129                         
00130                         LARGE_INTEGER currentTime;
00131                         QueryPerformanceCounter(¤tTime);
00132                         LONGLONG clockCycles = currentTime.QuadPart - 
00133                                 mStartTime.QuadPart;
00134 
00135                         
00136                         
00137                         LONGLONG sec = clockCycles / mClockFrequency.QuadPart;
00138 
00139                         
00140                         
00141                         
00142                         
00143                         
00144                         
00145                         LONGLONG msec1 = sec * 1000 + (clockCycles - sec * 
00146                                 mClockFrequency.QuadPart) * 1000 / mClockFrequency.QuadPart;
00147                         DWORD tickCount = GetTickCount();
00148                         if (tickCount < mStartTick)
00149                         {
00150                                 mStartTick = tickCount;
00151                         }
00152                         LONGLONG msec2 = (LONGLONG)(tickCount - mStartTick);
00153                         LONGLONG msecDiff = msec1 - msec2;
00154                         if (msecDiff < -100 || msecDiff > 100)
00155                         {
00156                                 
00157                                 LONGLONG adjustment = (std::min)(msecDiff * 
00158                                         mClockFrequency.QuadPart / 1000, clockCycles - 
00159                                         mPrevClockCycles);
00160                                 mStartTime.QuadPart += adjustment;
00161                                 clockCycles -= adjustment;
00162 
00163                                 
00164                                 sec = clockCycles / mClockFrequency.QuadPart;
00165                         }
00166 
00167                         
00168                         
00169                         LONGLONG usec = (clockCycles - sec * mClockFrequency.QuadPart) * 
00170                                 1000000 / mClockFrequency.QuadPart;
00171 
00172                         
00173                         
00174                         mPrevClockCycles = clockCycles;
00175 
00176                         
00177                         
00178                         return sec * 1000000 + usec;
00179 #else
00180                         
00181                         
00182                         
00183                         struct timeval currentTime;
00184                         gettimeofday(¤tTime, NULL);
00185                         return (unsigned long long int)(currentTime.tv_sec - 
00186                                 mStartTime.tv_sec) * 1000000 + (currentTime.tv_usec - 
00187                                 mStartTime.tv_usec);
00188 #endif
00189                 }
00190 
00191         private:
00192 #ifdef USE_WINDOWS_TIMERS
00193                 LARGE_INTEGER mClockFrequency;
00194                 DWORD mStartTick;
00195                 LONGLONG mPrevClockCycles;
00196                 LARGE_INTEGER mStartTime;
00197 #else
00198                 struct timeval mStartTime;
00199 #endif
00200         };
00201 
00202 
00203         enum TimeFormat
00204         {
00205                 SECONDS,
00206                 MILLISECONDS,
00207                 MICROSECONDS,
00208                 PERCENT
00209         };
00210 
00211 
00212         class Profiler
00213         {
00214         public:
00215 
00216 
00217 
00218 
00219 
00220 
00221 
00222 
00223                 inline Profiler();
00224 
00225                 inline ~Profiler();
00226 
00227 
00228 
00229 
00230 
00231 
00232                 inline static Profiler& instance();
00233 
00234 
00235 
00236 
00237 
00238 
00239 
00240 
00241 
00242 
00243 
00244 
00245 
00246 
00247 
00248 
00249 
00250 
00251 
00252 
00253 
00254 
00255 
00256 
00257 
00258 
00259 
00260 
00261 
00262 
00263 
00264 
00265 
00266 
00267 
00268 
00269 
00270 
00271 
00272 
00273 
00274 
00275                 inline void init(double smoothing=0.0, 
00276                         const std::string outputFilename="", size_t printPeriod=1,
00277                         TimeFormat printFormat=MILLISECONDS);
00278 
00279 
00280 
00281 
00282 
00283 
00284                 inline void beginBlock(const std::string& name);
00285 
00286 
00287 
00288 
00289 
00290 
00291                 inline void endBlock(const std::string& name);
00292 
00293 
00294 
00295 
00296 
00297 
00298 
00299 
00300 
00301                 inline void endCycle();
00302 
00303 
00304 
00305 
00306 
00307 
00308 
00309 
00310 
00311 
00312 
00313                 inline double getAvgDuration(const std::string& name, 
00314                         TimeFormat format)const;
00315 
00316 
00317 
00318 
00319 
00320 
00321 
00322 
00323 
00324                 inline double getTotalDuration(const std::string& name, 
00325                         TimeFormat format);
00326 
00327 
00328 
00329 
00330 
00331 
00332 
00333                 inline double getTimeSinceInit(TimeFormat format);
00334 
00335 
00336 
00337 
00338 
00339 
00340 
00341                 inline std::string getSummary(TimeFormat format=PERCENT);
00342 
00343         private:
00344 
00345 
00346 
00347 
00348 
00349 
00350 
00351                 inline void destroy();
00352 
00353 
00354 
00355 
00356 
00357 
00358                 inline void printError(const std::string& msg)const;
00359 
00360 
00361 
00362 
00363 
00364 
00365 
00366                 inline ProfileBlock* getProfileBlock(const std::string& name)const;
00367 
00368 
00369 
00370 
00371 
00372 
00373                 inline std::string getSuffixString(TimeFormat format)const;
00374 
00375 
00376                 bool mEnabled;
00377 
00378 
00379                 Clock mClock;
00380 
00381 
00382                 unsigned long long int mCurrentCycleStartMicroseconds;
00383 
00384 
00385 
00386 
00387                 double mAvgCycleDurationMicroseconds;
00388 
00389 
00390                 std::map<std::string, ProfileBlock*> mProfileBlocks;
00391 
00392 
00393                 std::ofstream mOutputFile;
00394 
00395 
00396                 bool mFirstFileOutput;
00397 
00398 
00399 
00400                 double mMovingAvgScalar;
00401 
00402 
00403 
00404                 size_t mPrintPeriod;
00405 
00406 
00407                 TimeFormat mPrintFormat;
00408 
00409 
00410                 size_t mCycleCounter;
00411 
00412 
00413                 bool mFirstCycle;
00414         };
00415 
00416         Profiler::Profiler()
00417         {
00418                 mEnabled = false;
00419                 mCurrentCycleStartMicroseconds = 0;
00420                 mAvgCycleDurationMicroseconds = 0;
00421                 mFirstFileOutput = true;
00422                 mMovingAvgScalar = 0;
00423                 mPrintPeriod = 1;
00424                 mPrintFormat = SECONDS;
00425                 mCycleCounter = 0;
00426                 mFirstCycle = true;
00427         }
00428 
00429         Profiler::~Profiler()
00430         {
00431                 
00432                 
00433 
00434                 destroy();
00435         }
00436 
00437         Profiler& Profiler::instance()
00438         {
00439                 static Profiler self;
00440                 return self;
00441         }
00442 
00443         void Profiler::destroy()
00444         {
00445                 mEnabled = false;
00446                 mClock.reset();
00447                 mCurrentCycleStartMicroseconds = 0;
00448                 mAvgCycleDurationMicroseconds = 0;
00449                 while (!mProfileBlocks.empty())
00450                 {
00451                         delete (*mProfileBlocks.begin()).second;
00452                         mProfileBlocks.erase(mProfileBlocks.begin());
00453                 }
00454                 if (mOutputFile.is_open())
00455                 {
00456                         mOutputFile.close();
00457                 }
00458                 mFirstFileOutput = true;
00459                 mMovingAvgScalar = 0;
00460                 mPrintPeriod = 1;
00461                 mPrintFormat = SECONDS;
00462                 mCycleCounter = 0;
00463                 mFirstCycle = true;
00464         }
00465 
00466         void Profiler::init(double smoothing, const std::string outputFilename, 
00467                 size_t printPeriod, TimeFormat printFormat)
00468         {
00469                 if (mEnabled)
00470                 {
00471                         
00472                         destroy();
00473                         std::cout << "[QuickProf] Re-initializing profiler, " 
00474                                 << "erasing all profiling blocks" << std::endl;
00475                 }
00476 
00477                 mEnabled = true;
00478 
00479                 if (smoothing <= 0)
00480                 {
00481                         if (smoothing < 0)
00482                         {
00483                                 printError("Smoothing parameter must be >= 0. Using 0.");
00484                         }
00485 
00486                         mMovingAvgScalar = 0;
00487                 }
00488                 else
00489                 {
00490                         
00491                         mMovingAvgScalar = ::exp(-1 / smoothing);
00492                 }
00493 
00494                 if (!outputFilename.empty())
00495                 {
00496                         mOutputFile.open(outputFilename.c_str());
00497                 }
00498 
00499                 if (printPeriod < 1)
00500                 {
00501                         printError("Print period must be >= 1. Using 1.");
00502                         mPrintPeriod = 1;
00503                 }
00504                 else
00505                 {
00506                         mPrintPeriod = printPeriod;
00507                 }
00508                 mPrintFormat = printFormat;
00509 
00510                 mClock.reset();
00511 
00512                 
00513                 mCurrentCycleStartMicroseconds = mClock.getTimeMicroseconds();
00514         }
00515 
00516         void Profiler::beginBlock(const std::string& name)
00517         {
00518                 if (!mEnabled)
00519                 {
00520                         return;
00521                 }
00522 
00523                 if (name.empty())
00524                 {
00525                         printError("Cannot allow unnamed profile blocks.");
00526                         return;
00527                 }
00528 
00529                 ProfileBlock* block = NULL;
00530 
00531                 std::map<std::string, ProfileBlock*>::iterator iter = 
00532                         mProfileBlocks.find(name);
00533                 if (mProfileBlocks.end() == iter)
00534                 {
00535                         
00536                         block = new ProfileBlock();
00537                         mProfileBlocks[name] = block;
00538                 }
00539                 else
00540                 {
00541                         block = iter->second;
00542                 }
00543 
00544                 
00545                 block->currentBlockStartMicroseconds = mClock.getTimeMicroseconds();
00546         }
00547 
00548         void Profiler::endBlock(const std::string& name)
00549         {
00550                 if (!mEnabled)
00551                 {
00552                         return;
00553                 }
00554 
00555                 
00556                 unsigned long long int endTick = mClock.getTimeMicroseconds();
00557 
00558                 ProfileBlock* block = getProfileBlock(name);
00559                 if (!block)
00560                 {
00561                         return;
00562                 }
00563 
00564                 unsigned long long int blockDuration = endTick - 
00565                         block->currentBlockStartMicroseconds;
00566                 block->currentCycleTotalMicroseconds += blockDuration;
00567                 block->totalMicroseconds += blockDuration;
00568         }
00569 
00570         void Profiler::endCycle()
00571         {
00572                 if (!mEnabled)
00573                 {
00574                         return;
00575                 }
00576 
00577                 
00578                 
00579                 
00580                 
00581                 unsigned long long int currentCycleDurationMicroseconds = 
00582                         mClock.getTimeMicroseconds() - mCurrentCycleStartMicroseconds;
00583                 if (mFirstCycle)
00584                 {
00585                         mAvgCycleDurationMicroseconds = 
00586                                 (double)currentCycleDurationMicroseconds;
00587                 }
00588                 else
00589                 {
00590                         mAvgCycleDurationMicroseconds = mMovingAvgScalar * 
00591                                 mAvgCycleDurationMicroseconds + (1 - mMovingAvgScalar) 
00592                                 * (double)currentCycleDurationMicroseconds;
00593                 }
00594 
00595                 
00596                 std::map<std::string, ProfileBlock*>::iterator blocksBegin = 
00597                         mProfileBlocks.begin();
00598                 std::map<std::string, ProfileBlock*>::iterator blocksEnd = 
00599                         mProfileBlocks.end();
00600                 std::map<std::string, ProfileBlock*>::iterator iter = blocksBegin;
00601                 for (; iter != blocksEnd; ++iter)
00602                 {
00603                         ProfileBlock* block = iter->second;
00604 
00605                         
00606                         
00607                         
00608                         if (mFirstCycle)
00609                         {
00610                                 block->avgCycleTotalMicroseconds = 
00611                                         (double)block->currentCycleTotalMicroseconds;
00612                         }
00613                         else
00614                         {
00615                                 block->avgCycleTotalMicroseconds = mMovingAvgScalar * 
00616                                         block->avgCycleTotalMicroseconds + (1 - mMovingAvgScalar) * 
00617                                         (double)block->currentCycleTotalMicroseconds;
00618                         }
00619 
00620                         block->currentCycleTotalMicroseconds = 0;
00621                 }
00622 
00623                 if (mFirstCycle)
00624                 {
00625                         mFirstCycle = false;
00626                 }
00627 
00628                 
00629                 if (mOutputFile.is_open() && mCycleCounter % mPrintPeriod == 0)
00630                 {
00631                         mCycleCounter = 0;
00632 
00633                         if (mFirstFileOutput)
00634                         {
00635                                 
00636                                 
00637                                 mOutputFile << "# t(s)";
00638 
00639                                 std::string suffix = getSuffixString(mPrintFormat);
00640                                 for (iter = blocksBegin; iter != blocksEnd; ++iter)
00641                                 {
00642                                         mOutputFile  << " " << (*iter).first << "(" << suffix << ")";
00643                                 }
00644 
00645                                 mOutputFile << std::endl;
00646                                 mFirstFileOutput = false;
00647                         }
00648 
00649                         mOutputFile << getTimeSinceInit(SECONDS) * (double)0.000001;
00650 
00651                         
00652                         for (iter = blocksBegin; iter != blocksEnd; ++iter)
00653                         {
00654                                 mOutputFile << " " << getAvgDuration((*iter).first, 
00655                                         mPrintFormat);
00656                         }
00657 
00658                         mOutputFile << std::endl;
00659                 }
00660 
00661                 ++mCycleCounter;
00662                 mCurrentCycleStartMicroseconds = mClock.getTimeMicroseconds();
00663         }
00664 
00665         double Profiler::getAvgDuration(const std::string& name, 
00666                 TimeFormat format)const
00667         {
00668                 if (!mEnabled)
00669                 {
00670                         return 0;
00671                 }
00672 
00673                 ProfileBlock* block = getProfileBlock(name);
00674                 if (!block)
00675                 {
00676                         return 0;
00677                 }
00678 
00679                 double result = 0;
00680 
00681                 switch(format)
00682                 {
00683                         case SECONDS:
00684                                 result = block->avgCycleTotalMicroseconds * (double)0.000001;
00685                                 break;
00686                         case MILLISECONDS:
00687                                 result = block->avgCycleTotalMicroseconds * (double)0.001;
00688                                 break;
00689                         case MICROSECONDS:
00690                                 result = block->avgCycleTotalMicroseconds;
00691                                 break;
00692                         case PERCENT:
00693                         {
00694                                 if (0 == mAvgCycleDurationMicroseconds)
00695                                 {
00696                                         result = 0;
00697                                 }
00698                                 else
00699                                 {
00700                                         result = 100.0 * block->avgCycleTotalMicroseconds / 
00701                                                 mAvgCycleDurationMicroseconds;
00702                                 }
00703                                 break;
00704                         }
00705                         default:
00706                                 break;
00707                 }
00708 
00709                 return result;
00710         }
00711 
00712         double Profiler::getTotalDuration(const std::string& name, 
00713                 TimeFormat format)
00714         {
00715                 if (!mEnabled)
00716                 {
00717                         return 0;
00718                 }
00719 
00720                 ProfileBlock* block = getProfileBlock(name);
00721                 if (!block)
00722                 {
00723                         return 0;
00724                 }
00725 
00726                 double blockTotalMicroseconds = (double)block->totalMicroseconds;
00727                 double result = 0;
00728 
00729                 switch(format)
00730                 {
00731                         case SECONDS:
00732                                 result = blockTotalMicroseconds * (double)0.000001;
00733                                 break;
00734                         case MILLISECONDS:
00735                                 result = blockTotalMicroseconds * (double)0.001;
00736                                 break;
00737                         case MICROSECONDS:
00738                                 result = blockTotalMicroseconds;
00739                                 break;
00740                         case PERCENT:
00741                                 {
00742                                         double microsecondsSinceInit = getTimeSinceInit(MICROSECONDS);
00743                                         if (0 == microsecondsSinceInit)
00744                                         {
00745                                                 result = 0;
00746                                         }
00747                                         else
00748                                         {
00749                                                 result = 100.0 * blockTotalMicroseconds / 
00750                                                         microsecondsSinceInit;
00751                                         }
00752                                 }
00753                                 break;
00754                         default:
00755                                 break;
00756                 }
00757 
00758                 return result;
00759         }
00760 
00761         double Profiler::getTimeSinceInit(TimeFormat format)
00762         {
00763                 double timeSinceInit = 0;
00764 
00765                 switch(format)
00766                 {
00767                         case SECONDS:
00768                                 timeSinceInit = (double)mClock.getTimeMicroseconds() * (double)0.000001;
00769                                 break;
00770                         case MILLISECONDS:
00771                                 timeSinceInit = (double)mClock.getTimeMicroseconds() * (double)0.001;
00772                                 break;
00773                         case MICROSECONDS:
00774                                 timeSinceInit = (double)mClock.getTimeMicroseconds();
00775                                 break;
00776                         case PERCENT:
00777                         {
00778                                 timeSinceInit = 100;
00779                                 break;
00780                         }
00781                         default:
00782                                 break;
00783                 }
00784 
00785                 if (timeSinceInit < 0)
00786                 {
00787                         timeSinceInit = 0;
00788                 }
00789 
00790                 return timeSinceInit;
00791         }
00792 
00793         std::string Profiler::getSummary(TimeFormat format)
00794         {
00795                 if (!mEnabled)
00796                 {
00797                         return "";
00798                 }
00799 
00800                 std::ostringstream oss;
00801                 std::string suffix = getSuffixString(format);
00802 
00803                 std::map<std::string, ProfileBlock*>::iterator blocksBegin = 
00804                         mProfileBlocks.begin();
00805                 std::map<std::string, ProfileBlock*>::iterator blocksEnd = 
00806                         mProfileBlocks.end();
00807                 std::map<std::string, ProfileBlock*>::iterator iter = blocksBegin;
00808                 for (; iter != blocksEnd; ++iter)
00809                 {
00810                         if (iter != blocksBegin)
00811                         {
00812                                 oss << "\n";
00813                         }
00814 
00815                         oss << iter->first;
00816                         oss << ": ";
00817                         oss << getTotalDuration(iter->first, format);
00818                         oss << " ";
00819                         oss << suffix;
00820                 }
00821 
00822                 return oss.str();
00823         }
00824 
00825         void Profiler::printError(const std::string& msg)const
00826         {
00827                 std::cout << "[QuickProf error] " << msg << std::endl;
00828         }
00829 
00830         ProfileBlock* Profiler::getProfileBlock(const std::string& name)const
00831         {
00832                 std::map<std::string, ProfileBlock*>::const_iterator iter = 
00833                         mProfileBlocks.find(name);
00834                 if (mProfileBlocks.end() == iter)
00835                 {
00836                         
00837                         printError("The profile block named '" + name + 
00838                                 "' does not exist.");
00839                         return NULL;
00840                 }
00841                 else
00842                 {
00843                         return iter->second;
00844                 }
00845         }
00846 
00847         std::string Profiler::getSuffixString(TimeFormat format)const
00848         {
00849                 std::string suffix;
00850                 switch(format)
00851                 {
00852                         case SECONDS:
00853                                 suffix = "s";
00854                                 break;
00855                         case MILLISECONDS:
00856                                 suffix = "ms";
00857                                 break;
00858                         case MICROSECONDS:
00859                                 suffix = "us";
00860                                 break;
00861                         case PERCENT:
00862                         {
00863                                 suffix = "%";
00864                                 break;
00865                         }
00866                         default:
00867                                 break;
00868                 }
00869 
00870                 return suffix;
00871         }
00872 };
00873 
00874 #endif