1 /************************************************************************
4 * Tyler Streeter tylerstreeter@gmail.com *
5 * All rights reserved. *
6 * Web: http://quickprof.sourceforge.net *
8 * This library is free software; you can redistribute it and/or *
9 * modify it under the terms of EITHER: *
10 * (1) The GNU Lesser General Public License as published by the Free *
11 * Software Foundation; either version 2.1 of the License, or (at *
12 * your option) any later version. The text of the GNU Lesser *
13 * General Public License is included with this library in the *
14 * file license-LGPL.txt. *
15 * (2) The BSD-style license that is included with this library in *
16 * the file license-BSD.txt. *
18 * This library is distributed in the hope that it will be useful, *
19 * but WITHOUT ANY WARRANTY; without even the implied warranty of *
20 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the files *
21 * license-LGPL.txt and license-BSD.txt for more details. *
22 ************************************************************************/
24 // Please visit the project website (http://quickprof.sourceforge.net)
25 // for usage instructions.
27 // Credits: The Clock class was inspired by the Timer classes in
28 // Ogre (www.ogre3d.org).
31 //#define USE_QUICKPROF 1
44 #include <sys/sys_time.h>
46 typedef uint64_t __int64;
49 #if defined(WIN32) || defined(_WIN32)
50 #define USE_WINDOWS_TIMERS
57 #define mymin(a,b) (a > b ? a : b)
60 /// A simple data structure representing a single timed block
66 currentBlockStartMicroseconds = 0;
67 currentCycleTotalMicroseconds = 0;
68 lastCycleTotalMicroseconds = 0;
69 totalMicroseconds = 0;
72 /// The starting time (in us) of the current block update.
73 unsigned long int currentBlockStartMicroseconds;
75 /// The accumulated time (in us) spent in this block during the
76 /// current profiling cycle.
77 unsigned long int currentCycleTotalMicroseconds;
79 /// The accumulated time (in us) spent in this block during the
80 /// past profiling cycle.
81 unsigned long int lastCycleTotalMicroseconds;
83 /// The total accumulated time (in us) spent in this block.
84 unsigned long int totalMicroseconds;
92 #ifdef USE_WINDOWS_TIMERS
93 QueryPerformanceFrequency(&mClockFrequency);
102 /// Resets the initial reference time.
105 #ifdef USE_WINDOWS_TIMERS
106 QueryPerformanceCounter(&mStartTime);
107 mStartTick = GetTickCount();
108 mPrevElapsedTime = 0;
112 typedef uint64_t __int64;
113 typedef __int64 ClockSize;
115 __asm __volatile__( "mftb %0" : "=r" (newTime) : : "memory");
116 mStartTime = newTime;
118 gettimeofday(&mStartTime, NULL);
124 /// Returns the time in ms since the last call to reset or since
125 /// the Clock was created.
126 unsigned long int getTimeMilliseconds()
128 #ifdef USE_WINDOWS_TIMERS
129 LARGE_INTEGER currentTime;
130 QueryPerformanceCounter(¤tTime);
131 LONGLONG elapsedTime = currentTime.QuadPart -
134 // Compute the number of millisecond ticks elapsed.
135 unsigned long msecTicks = (unsigned long)(1000 * elapsedTime /
136 mClockFrequency.QuadPart);
138 // Check for unexpected leaps in the Win32 performance counter.
139 // (This is caused by unexpected data across the PCI to ISA
140 // bridge, aka south bridge. See Microsoft KB274323.)
141 unsigned long elapsedTicks = GetTickCount() - mStartTick;
142 signed long msecOff = (signed long)(msecTicks - elapsedTicks);
143 if (msecOff < -100 || msecOff > 100)
145 // Adjust the starting time forwards.
146 LONGLONG msecAdjustment = mymin(msecOff *
147 mClockFrequency.QuadPart / 1000, elapsedTime -
149 mStartTime.QuadPart += msecAdjustment;
150 elapsedTime -= msecAdjustment;
152 // Recompute the number of millisecond ticks elapsed.
153 msecTicks = (unsigned long)(1000 * elapsedTime /
154 mClockFrequency.QuadPart);
157 // Store the current elapsed time for adjustments next time.
158 mPrevElapsedTime = elapsedTime;
164 __int64 freq=sys_time_get_timebase_frequency();
165 double dFreq=((double) freq) / 1000.0;
166 typedef uint64_t __int64;
167 typedef __int64 ClockSize;
169 __asm __volatile__( "mftb %0" : "=r" (newTime) : : "memory");
171 return (newTime-mStartTime) / dFreq;
174 struct timeval currentTime;
175 gettimeofday(¤tTime, NULL);
176 return (currentTime.tv_sec - mStartTime.tv_sec) * 1000 +
177 (currentTime.tv_usec - mStartTime.tv_usec) / 1000;
182 /// Returns the time in us since the last call to reset or since
183 /// the Clock was created.
184 unsigned long int getTimeMicroseconds()
186 #ifdef USE_WINDOWS_TIMERS
187 LARGE_INTEGER currentTime;
188 QueryPerformanceCounter(¤tTime);
189 LONGLONG elapsedTime = currentTime.QuadPart -
192 // Compute the number of millisecond ticks elapsed.
193 unsigned long msecTicks = (unsigned long)(1000 * elapsedTime /
194 mClockFrequency.QuadPart);
196 // Check for unexpected leaps in the Win32 performance counter.
197 // (This is caused by unexpected data across the PCI to ISA
198 // bridge, aka south bridge. See Microsoft KB274323.)
199 unsigned long elapsedTicks = GetTickCount() - mStartTick;
200 signed long msecOff = (signed long)(msecTicks - elapsedTicks);
201 if (msecOff < -100 || msecOff > 100)
203 // Adjust the starting time forwards.
204 LONGLONG msecAdjustment = mymin(msecOff *
205 mClockFrequency.QuadPart / 1000, elapsedTime -
207 mStartTime.QuadPart += msecAdjustment;
208 elapsedTime -= msecAdjustment;
211 // Store the current elapsed time for adjustments next time.
212 mPrevElapsedTime = elapsedTime;
214 // Convert to microseconds.
215 unsigned long usecTicks = (unsigned long)(1000000 * elapsedTime /
216 mClockFrequency.QuadPart);
222 __int64 freq=sys_time_get_timebase_frequency();
223 double dFreq=((double) freq)/ 1000000.0;
224 typedef uint64_t __int64;
225 typedef __int64 ClockSize;
227 __asm __volatile__( "mftb %0" : "=r" (newTime) : : "memory");
229 return (newTime-mStartTime) / dFreq;
232 struct timeval currentTime;
233 gettimeofday(¤tTime, NULL);
234 return (currentTime.tv_sec - mStartTime.tv_sec) * 1000000 +
235 (currentTime.tv_usec - mStartTime.tv_usec);
241 #ifdef USE_WINDOWS_TIMERS
242 LARGE_INTEGER mClockFrequency;
244 LONGLONG mPrevElapsedTime;
245 LARGE_INTEGER mStartTime;
250 struct timeval mStartTime;
257 /// A static class that manages timing for a set of profiling blocks.
261 /// A set of ways to retrieve block timing data.
262 enum BlockTimingMethod
264 /// The total time spent in the block (in seconds) since the
265 /// profiler was initialized.
268 /// The total time spent in the block (in ms) since the
269 /// profiler was initialized.
270 BLOCK_TOTAL_MILLISECONDS,
272 /// The total time spent in the block (in us) since the
273 /// profiler was initialized.
274 BLOCK_TOTAL_MICROSECONDS,
276 /// The total time spent in the block, as a % of the total
277 /// elapsed time since the profiler was initialized.
280 /// The time spent in the block (in seconds) in the most recent
284 /// The time spent in the block (in ms) in the most recent
286 BLOCK_CYCLE_MILLISECONDS,
288 /// The time spent in the block (in us) in the most recent
290 BLOCK_CYCLE_MICROSECONDS,
292 /// The time spent in the block (in seconds) in the most recent
293 /// profiling cycle, as a % of the total cycle time.
297 /// Initializes the profiler. This must be called first. If this is
298 /// never called, the profiler is effectively disabled; all other
299 /// functions will return immediately. The first parameter
300 /// is the name of an output data file; if this string is not empty,
301 /// data will be saved on every profiling cycle; if this string is
302 /// empty, no data will be saved to a file. The second parameter
303 /// determines which timing method is used when printing data to the
305 inline static void init(const std::string outputFilename="",
306 BlockTimingMethod outputMethod=BLOCK_CYCLE_MILLISECONDS);
308 /// Cleans up allocated memory.
309 inline static void destroy();
311 /// Begins timing the named block of code.
312 inline static void beginBlock(const std::string& name);
314 /// Updates the accumulated time spent in the named block by adding
315 /// the elapsed time since the last call to startBlock for this block
317 inline static void endBlock(const std::string& name);
319 /// Returns the time spent in the named block according to the
320 /// given timing method. See comments on BlockTimingMethod for details.
321 inline static double getBlockTime(const std::string& name,
322 BlockTimingMethod method=BLOCK_CYCLE_MILLISECONDS);
324 /// Defines the end of a profiling cycle. Use this regularly if you
325 /// want to generate detailed timing information. This must not be
326 /// called within a timing block.
327 inline static void endProfilingCycle();
329 /// A helper function that creates a string of statistics for
330 /// each timing block. This is mainly for printing an overall
331 /// summary to the command line.
332 inline static std::string createStatsString(
333 BlockTimingMethod method=BLOCK_TOTAL_PERCENT);
340 /// Prints an error message to standard output.
341 inline static void printError(const std::string& msg)
343 std::cout << "[QuickProf error] " << msg << std::endl;
346 /// Determines whether the profiler is enabled.
347 static bool mEnabled;
349 /// The clock used to time profile blocks.
350 static hidden::Clock mClock;
352 /// The starting time (in us) of the current profiling cycle.
353 static unsigned long int mCurrentCycleStartMicroseconds;
355 /// The duration (in us) of the most recent profiling cycle.
356 static unsigned long int mLastCycleDurationMicroseconds;
358 /// Internal map of named profile blocks.
359 static std::map<std::string, hidden::ProfileBlock*> mProfileBlocks;
361 /// The data file used if this feature is enabled in 'init.'
362 static std::ofstream mOutputFile;
364 /// Tracks whether we have begun print data to the output file.
365 static bool mFirstFileOutput;
367 /// The method used when printing timing data to an output file.
368 static BlockTimingMethod mFileOutputMethod;
370 /// The number of the current profiling cycle.
371 static unsigned long int mCycleNumber;
377 // This never gets called because a Profiler instance is never
381 Profiler::~Profiler()
383 // This never gets called because a Profiler instance is never
387 void Profiler::init(const std::string outputFilename,
388 BlockTimingMethod outputMethod)
392 if (!outputFilename.empty())
394 mOutputFile.open(outputFilename.c_str());
397 mFileOutputMethod = outputMethod;
401 // Set the start time for the first cycle.
402 mCurrentCycleStartMicroseconds = mClock.getTimeMicroseconds();
405 void Profiler::destroy()
412 if (mOutputFile.is_open())
417 // Destroy all ProfileBlocks.
418 while (!mProfileBlocks.empty())
420 delete (*mProfileBlocks.begin()).second;
421 mProfileBlocks.erase(mProfileBlocks.begin());
425 void Profiler::beginBlock(const std::string& name)
434 printError("Cannot allow unnamed profile blocks.");
438 hidden::ProfileBlock* block = mProfileBlocks[name];
442 // Create a new ProfileBlock.
443 mProfileBlocks[name] = new hidden::ProfileBlock();
444 block = mProfileBlocks[name];
447 // We do this at the end to get more accurate results.
448 block->currentBlockStartMicroseconds = mClock.getTimeMicroseconds();
451 void Profiler::endBlock(const std::string& name)
458 // We do this at the beginning to get more accurate results.
459 unsigned long int endTick = mClock.getTimeMicroseconds();
461 hidden::ProfileBlock* block = mProfileBlocks[name];
465 // The named block does not exist. Print an error.
466 printError("The profile block named '" + name +
467 "' does not exist.");
471 unsigned long int blockDuration = endTick -
472 block->currentBlockStartMicroseconds;
473 block->currentCycleTotalMicroseconds += blockDuration;
474 block->totalMicroseconds += blockDuration;
477 double Profiler::getBlockTime(const std::string& name,
478 BlockTimingMethod method)
485 hidden::ProfileBlock* block = mProfileBlocks[name];
489 // The named block does not exist. Print an error.
490 printError("The profile block named '" + name +
491 "' does not exist.");
499 case BLOCK_TOTAL_SECONDS:
500 result = (double)block->totalMicroseconds * (double)0.000001;
502 case BLOCK_TOTAL_MILLISECONDS:
503 result = (double)block->totalMicroseconds * (double)0.001;
505 case BLOCK_TOTAL_MICROSECONDS:
506 result = (double)block->totalMicroseconds;
508 case BLOCK_TOTAL_PERCENT:
510 double timeSinceInit = (double)mClock.getTimeMicroseconds();
511 if (timeSinceInit <= 0)
517 result = 100.0 * (double)block->totalMicroseconds /
522 case BLOCK_CYCLE_SECONDS:
523 result = (double)block->lastCycleTotalMicroseconds *
526 case BLOCK_CYCLE_MILLISECONDS:
527 result = (double)block->lastCycleTotalMicroseconds *
530 case BLOCK_CYCLE_MICROSECONDS:
531 result = (double)block->lastCycleTotalMicroseconds;
533 case BLOCK_CYCLE_PERCENT:
535 if (0 == mLastCycleDurationMicroseconds)
537 // We have not yet finished a cycle, so just return zero
538 // percent to avoid a divide by zero error.
543 result = 100.0 * (double)block->lastCycleTotalMicroseconds /
544 mLastCycleDurationMicroseconds;
555 void Profiler::endProfilingCycle()
562 // Store the duration of the cycle that just finished.
563 mLastCycleDurationMicroseconds = mClock.getTimeMicroseconds() -
564 mCurrentCycleStartMicroseconds;
566 // For each block, update data for the cycle that just finished.
567 std::map<std::string, hidden::ProfileBlock*>::iterator iter;
568 for (iter = mProfileBlocks.begin(); iter != mProfileBlocks.end(); ++iter)
570 hidden::ProfileBlock* block = (*iter).second;
571 block->lastCycleTotalMicroseconds =
572 block->currentCycleTotalMicroseconds;
573 block->currentCycleTotalMicroseconds = 0;
576 if (mOutputFile.is_open())
578 // Print data to the output file.
579 if (mFirstFileOutput)
581 // On the first iteration, print a header line that shows the
582 // names of each profiling block.
583 mOutputFile << "#cycle; ";
585 for (iter = mProfileBlocks.begin(); iter != mProfileBlocks.end();
588 mOutputFile << (*iter).first << "; ";
591 mOutputFile << std::endl;
592 mFirstFileOutput = false;
595 mOutputFile << mCycleNumber << "; ";
597 for (iter = mProfileBlocks.begin(); iter != mProfileBlocks.end();
600 mOutputFile << getBlockTime((*iter).first, mFileOutputMethod)
604 mOutputFile << std::endl;
608 mCurrentCycleStartMicroseconds = mClock.getTimeMicroseconds();
611 std::string Profiler::createStatsString(BlockTimingMethod method)
623 case BLOCK_TOTAL_SECONDS:
626 case BLOCK_TOTAL_MILLISECONDS:
629 case BLOCK_TOTAL_MICROSECONDS:
632 case BLOCK_TOTAL_PERCENT:
637 case BLOCK_CYCLE_SECONDS:
640 case BLOCK_CYCLE_MILLISECONDS:
643 case BLOCK_CYCLE_MICROSECONDS:
646 case BLOCK_CYCLE_PERCENT:
655 std::map<std::string, hidden::ProfileBlock*>::iterator iter;
656 for (iter = mProfileBlocks.begin(); iter != mProfileBlocks.end(); ++iter)
658 if (iter != mProfileBlocks.begin())
664 sprintf(blockTime, "%lf", getBlockTime((*iter).first, method));
678 #endif //USE_QUICKPROF