EAStopwatch.cpp 25 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723
  1. ///////////////////////////////////////////////////////////////////////////////
  2. // Copyright (c) Electronic Arts Inc. All rights reserved.
  3. ///////////////////////////////////////////////////////////////////////////////
  4. ///////////////////////////////////////////////////////////////////////////////
  5. // Implements a stopwatch-style stopwatch. This is useful for both benchmarking
  6. // and for implementing runtime timing.
  7. /////////////////////////////////////////////////////////////////////////////
  8. #include <EAStdC/EAStopwatch.h>
  9. #include <EAAssert/eaassert.h>
  10. #if defined(EA_PLATFORM_MICROSOFT)
  11. #pragma warning(push, 0)
  12. #include <Windows.h>
  13. #pragma warning(pop)
  14. #if (defined(EA_PLATFORM_WINDOWS) && !EA_WINAPI_FAMILY_PARTITION(EA_WINAPI_PARTITION_DESKTOP)) || defined(EA_PLATFORM_WINDOWS_PHONE)
  15. EA_DISABLE_ALL_VC_WARNINGS()
  16. #include <chrono>
  17. #include <thread>
  18. EA_RESTORE_ALL_VC_WARNINGS()
  19. #endif
  20. #elif defined(EA_PLATFORM_SONY)
  21. #include <kernel.h>
  22. #elif defined(EA_PLATFORM_POSIX)
  23. #include <unistd.h>
  24. #endif
  25. ///////////////////////////////////////////////////////////////////////////////
  26. // Auto-setup code
  27. //
  28. // The code below is for setting up the stopwatch system, and it's needed
  29. // because it allows us to know at runtime what the stopwatch frequency is
  30. // without having to spend CPU cycles repeatedly calculating it at runtime.
  31. // The code below is set up to auto-execute on startup when possible, but
  32. // it's OK if it doesn't because the Stopwatch constructor auto-checks for
  33. // initialization and calls if it not already. However, the auto code below
  34. // is still useful because it's better to get this taken care of on startup
  35. // rather than unexpectedly during runtime (as it takes a few milliseconds
  36. // on some platforms).
  37. ///////////////////////////////////////////////////////////////////////////////
  38. #if defined(_MSC_VER)
  39. #ifndef EASTDC_INIT_SEG_DEFINED
  40. #define EASTDC_INIT_SEG_DEFINED
  41. // Set initialization order between init_seg(compiler) (.CRT$XCC) and
  42. // init_seg(lib) (.CRT$XCL). The MSVC linker sorts the .CRT sections
  43. // alphabetically so we simply need to pick a name that is between
  44. // XCC and XCL. This works on both Windows and XBox.
  45. #pragma warning(disable: 4075) // "initializers put in unrecognized initialization area"
  46. #pragma init_seg(".CRT$XCF")
  47. #endif
  48. #elif defined(__GNUC__)
  49. // By adding the 'constructor' attribute to this function, we tell GCC
  50. // to have this function be called on startup before main(). We have the
  51. // AutoStopwatchSetup mechanism below, but GCC ignores it because the
  52. // object isn't externally reference and GCC thinks it can thus be eliminated.
  53. void EAStdCStopwatchSetupCoefficients();
  54. void EAStdCStopwatchSetup() __attribute__ ((constructor));
  55. void EAStdCStopwatchDisableCPUCalibration(uint64_t cpuFrequency);
  56. #else
  57. // Some compilers require these functions to be pre-declared.
  58. void EAStdCStopwatchSetupCoefficients();
  59. void EAStdCStopwatchSetup();
  60. void EAStdCStopwatchDisableCPUCalibration(uint64_t cpuFrequency);
  61. #endif
  62. // Anonymous namespace
  63. // Has the effect of making things declared within it be static, but with some improvements.
  64. namespace
  65. {
  66. // Stopwatch cycle metrics
  67. uint64_t mnStopwatchFrequency(1); // Set to one to prevent possible div/0 errors.
  68. float mfStopwatchCyclesToNanosecondsCoefficient;
  69. float mfStopwatchCyclesToMicrosecondsCoefficient;
  70. float mfStopwatchCyclesToMillisecondsCoefficient;
  71. float mfStopwatchCyclesToSecondsCoefficient;
  72. float mfStopwatchCyclesToMinutesCoefficient;
  73. // CPU cycle metrics
  74. uint64_t mnCPUFrequency(1); // Set to one to prevent possible div/0 errors.
  75. float mfCPUCyclesToNanosecondsCoefficient;
  76. float mfCPUCyclesToMicrosecondsCoefficient;
  77. float mfCPUCyclesToMillisecondsCoefficient;
  78. float mfCPUCyclesToSecondsCoefficient;
  79. float mfCPUCyclesToMinutesCoefficient;
  80. #if EASTDC_STOPWATCH_OVERHEAD_ENABLED
  81. uint64_t mnCPUCycleReadingOverhead(0);
  82. uint64_t mnStopwatchCycleReadingOverhead(0);
  83. #endif
  84. #if defined(EA_PLATFORM_MICROSOFT)
  85. void EAStdCThreadSleep(int ms)
  86. {
  87. #if (defined(EA_PLATFORM_WINDOWS) && !EA_WINAPI_FAMILY_PARTITION(EA_WINAPI_PARTITION_DESKTOP)) || defined(EA_PLATFORM_WINDOWS_PHONE)
  88. std::chrono::milliseconds duration(ms);
  89. std::this_thread::sleep_for(duration);
  90. #else
  91. ::SleepEx((DWORD)ms, TRUE);
  92. #endif
  93. }
  94. #endif
  95. }
  96. void EAStdCStopwatchSetupCoefficients()
  97. {
  98. // Calculate coefficients.
  99. mfStopwatchCyclesToMinutesCoefficient = 1.f / 60.f / (int64_t)mnStopwatchFrequency; // Some compilers require the
  100. mfStopwatchCyclesToSecondsCoefficient = 1.f / (int64_t)mnStopwatchFrequency; // conversion to int64_t for the math.
  101. mfStopwatchCyclesToMillisecondsCoefficient = 1000.f / (int64_t)mnStopwatchFrequency;
  102. mfStopwatchCyclesToMicrosecondsCoefficient = 1000000.f / (int64_t)mnStopwatchFrequency;
  103. mfStopwatchCyclesToNanosecondsCoefficient = 1000000000.f / (int64_t)mnStopwatchFrequency;
  104. #if EASTDC_STOPWATCH_OVERHEAD_ENABLED
  105. // Here we make a rough measurement of the start and stop overhead of
  106. // the stopwatch code. It is hard to say what a good way to determine this is,
  107. // as the runtime use of the stopwatch will actually cause the overhead to
  108. // vary somewhat between uses. It is perhaps even debateable whether we
  109. // should even be attempting to do such overhead calculations.
  110. uint64_t nCurrentStopwatchCycleValue1;
  111. uint64_t nCurrentStopwatchCycleValue2;
  112. uint64_t nLowestStopwatchCycleReadingOverhead(UINT64_MAX);
  113. uint64_t nCurrentStopwatchCycleReadingOverhead;
  114. for(int t(0); t < 8; t++)
  115. {
  116. nCurrentStopwatchCycleValue1 = EA::StdC::Stopwatch::GetStopwatchCycle();
  117. nCurrentStopwatchCycleValue2 = EA::StdC::Stopwatch::GetStopwatchCycle();
  118. nCurrentStopwatchCycleReadingOverhead = nCurrentStopwatchCycleValue2 - nCurrentStopwatchCycleValue1;
  119. if(nLowestStopwatchCycleReadingOverhead > nCurrentStopwatchCycleReadingOverhead)
  120. nLowestStopwatchCycleReadingOverhead = nCurrentStopwatchCycleReadingOverhead;
  121. }
  122. mnStopwatchCycleReadingOverhead = nLowestStopwatchCycleReadingOverhead;
  123. #endif
  124. // CPU Frequencies
  125. mfCPUCyclesToMinutesCoefficient = 1.f / 60.f / (int64_t)mnCPUFrequency;
  126. mfCPUCyclesToSecondsCoefficient = 1.f / (int64_t)mnCPUFrequency;
  127. mfCPUCyclesToMillisecondsCoefficient = 1000.f / (int64_t)mnCPUFrequency;
  128. mfCPUCyclesToMicrosecondsCoefficient = 1000000.f / (int64_t)mnCPUFrequency;
  129. mfCPUCyclesToNanosecondsCoefficient = 1000000000.f / (int64_t)mnCPUFrequency;
  130. #if EASTDC_STOPWATCH_OVERHEAD_ENABLED
  131. uint64_t nCurrentCPUCycleValue1, nCurrentCPUCycleValue2;
  132. uint64_t nLowestCPUCycleReadingOverhead(UINT64_MAX);
  133. uint64_t nCurrentCPUCycleReadingOverhead;
  134. for(int c = 0; c < 8; c++)
  135. {
  136. nCurrentCPUCycleValue1 = EA::StdC::Stopwatch::GetCPUCycle();
  137. nCurrentCPUCycleValue2 = EA::StdC::Stopwatch::GetCPUCycle();
  138. nCurrentCPUCycleReadingOverhead = nCurrentCPUCycleValue2 - nCurrentCPUCycleValue1;
  139. if(nLowestCPUCycleReadingOverhead > nCurrentCPUCycleReadingOverhead)
  140. nLowestCPUCycleReadingOverhead = nCurrentCPUCycleReadingOverhead;
  141. }
  142. mnCPUCycleReadingOverhead = nLowestCPUCycleReadingOverhead;
  143. #endif
  144. }
  145. void EAStdCStopwatchSetup()
  146. {
  147. if(mnStopwatchFrequency <= 1) // If we haven't already calculated this...
  148. {
  149. #if defined(EA_PLATFORM_SONY)
  150. // According to Sony: A time stamp counter exists for each CPU core, but the frequency is the same
  151. // value for all CPU cores. This frequency will not change during the lifetime of a process.
  152. mnCPUFrequency = sceKernelGetProcessTimeCounterFrequency();
  153. mnStopwatchFrequency = mnCPUFrequency;
  154. #elif defined(__APPLE__)
  155. mach_timebase_info_data_t timebaseInfo;
  156. mach_timebase_info(&timebaseInfo);
  157. mnCPUFrequency = (UINT64_C(1000000000) * (uint64_t)timebaseInfo.denom) / (uint64_t)timebaseInfo.numer;
  158. ////////////////////////////////////////////////////
  159. // Stopwatch Frequency
  160. mnStopwatchFrequency = mnCPUFrequency;
  161. #elif defined(EA_PLATFORM_XBOXONE) || defined(CS_UNDEFINED_STRING)
  162. // Microsoft has stated (https://forums.xboxlive.com/AnswerPage.aspx?qid=c3fcdad5-f3e4-46d9-85f9-d337506f0d6b&tgt=1) that
  163. // QueryPerformanceCounter / QueryPerformanceFrequency map to rdtsc and they are stable throughout the life of the process.
  164. // Thus we can use QueryPerformanceFrequency to portable tell the CPU frequency for our usage of rdtsc.
  165. QueryPerformanceFrequency(reinterpret_cast<LARGE_INTEGER*>(&mnStopwatchFrequency));
  166. mnCPUFrequency = mnStopwatchFrequency;
  167. #elif defined(EA_PLATFORM_MICROSOFT)
  168. // On Windows, the only way to tell the CPU-based timer frequency is to manually
  169. // time it. There is no function in Windows which tells you this information.
  170. // Indeed such a function might not be useful for CPU frequency-switching systems.
  171. // SetPriorityClass / SetThreadPriority APIs not available for Windows 8 Metro apps
  172. #if EA_WINAPI_FAMILY_PARTITION(EA_WINAPI_PARTITION_DESKTOP)
  173. HANDLE process = ::GetCurrentProcess();
  174. DWORD oldPriorityClass = ::GetPriorityClass(process);
  175. HANDLE thread = ::GetCurrentThread();
  176. int oldThreadPriority = ::GetThreadPriority(thread);
  177. // Set process/thread priorities.
  178. ::SetPriorityClass(process, REALTIME_PRIORITY_CLASS);
  179. ::SetThreadPriority(thread, THREAD_PRIORITY_TIME_CRITICAL);
  180. #endif
  181. ////////////////////////////////////////////////////
  182. // Stopwatch Frequency
  183. ////////////////////////////////////////////////////
  184. // CPU Frequency
  185. // Unfortunately, you can't call Win32 QueryPerformanceFrequency and
  186. // expect that it will give you the processor frequency in megahertz.
  187. // Doing such a thing would work for some CPUs and not others. In fact,
  188. // under Windows 2000, it works for my PIII-733 but not for my PII-300.
  189. // So what we do instead is simply find the ratio of the QueryPerformanceCounter
  190. // and our Stopwatch::GetCPUCycle functions.
  191. mnCPUFrequency = 0;
  192. for(int i(0); i < 5; i++) // Give N chances at this, in case OS pre-emption occurs.
  193. {
  194. uint64_t nQPCCounter; ::QueryPerformanceCounter((LARGE_INTEGER*)&nQPCCounter);
  195. uint64_t nCPUCounter = EA::StdC::Stopwatch::GetCPUCycle();
  196. double dRatio = (double)nCPUCounter / (double)nQPCCounter;
  197. if((dRatio > 0.98 && dRatio < 1.02))
  198. {
  199. ::QueryPerformanceFrequency((LARGE_INTEGER*)&mnCPUFrequency);
  200. break;
  201. }
  202. }
  203. if(!mnCPUFrequency)
  204. {
  205. // Do our own manual timing of clock ticks.
  206. // We use Win32 QueryPerformanceCounter and QueryPerformanceFrequency to
  207. // calibrate our CPU cycle counter.
  208. uint64_t nQPFrequency, nQPCCounter1, nQPCCounter2;
  209. uint64_t nCPUCounter1, nCPUCounter2=0;
  210. double dQPCSeconds(0);
  211. ::QueryPerformanceFrequency((LARGE_INTEGER*)&nQPFrequency);
  212. ::QueryPerformanceCounter((LARGE_INTEGER*)&nQPCCounter1);
  213. nCPUCounter1 = EA::StdC::Stopwatch::GetCPUCycle();
  214. #if EASTDC_STOPWATCH_FORCE_CPU_CYCLE_USAGE
  215. static const double TIME_IN_SECONDS_TO_MEASURE_TICKS = 0.300;
  216. #else
  217. // Given that ticks are considered to be unreliable we can tolerate lower accuracy measurement
  218. // of number of ticks per second. https://en.wikipedia.org/wiki/Time_Stamp_Counter
  219. // Here the largest limitation will be the MS Perf counter accuracy which is less than 1us.
  220. static const double TIME_IN_SECONDS_TO_MEASURE_TICKS = 0.005;
  221. #endif
  222. while(dQPCSeconds < TIME_IN_SECONDS_TO_MEASURE_TICKS)
  223. {
  224. ::QueryPerformanceCounter((LARGE_INTEGER*)&nQPCCounter2);
  225. nCPUCounter2 = EA::StdC::Stopwatch::GetCPUCycle();
  226. dQPCSeconds = (nQPCCounter2 - nQPCCounter1) / (double)nQPFrequency;
  227. }
  228. mnCPUFrequency = (uint64_t)((nCPUCounter2 - nCPUCounter1) / dQPCSeconds);
  229. }
  230. #if EASTDC_STOPWATCH_FORCE_CPU_CYCLE_USAGE
  231. mnStopwatchFrequency = mnCPUFrequency;
  232. #else
  233. ::QueryPerformanceFrequency((LARGE_INTEGER*)&mnStopwatchFrequency);
  234. #endif
  235. // Reset process/thread priorities.
  236. #if EA_WINAPI_FAMILY_PARTITION(EA_WINAPI_PARTITION_DESKTOP)
  237. ::SetPriorityClass(process, oldPriorityClass);
  238. ::SetThreadPriority(thread, oldThreadPriority);
  239. #endif
  240. #else
  241. ////////////////////////////////////////////////////
  242. // CPU Frequency
  243. #ifdef EASTDC_CPU_FREQ_CALCULATED
  244. // On Unix, the only way to tell the CPU-based timer frequency is to manually
  245. // time it. There is no function in Unix which tells you this information.
  246. // Indeed such a function might not be useful for CPU frequency-switching systems.
  247. uint64_t nTimeCounter1 = EA::StdC::Stopwatch::GetStopwatchCycle();
  248. uint64_t nCPUCounter1 = EA::StdC::Stopwatch::GetCPUCycle();
  249. usleep(250000); // Sleep for a ~quarter second.
  250. uint64_t nCPUCounter2 = EA::StdC::Stopwatch::GetCPUCycle();
  251. uint64_t nTimeCounter2 = EA::StdC::Stopwatch::GetStopwatchCycle();
  252. uint64_t nTimeDeltaUs = nTimeCounter2 - nTimeCounter1;
  253. uint64_t nCPUDeltaTicks = nCPUCounter2 - nCPUCounter1;
  254. // GetStopwatchCycle will have varying resolution so we need to account for that accordingly
  255. #if EASTDC_STOPWATCH_USE_CLOCK_GETTIME
  256. mnCPUFrequency = (nCPUDeltaTicks * 1000000000 / nTimeDeltaUs); // We are using clock_gettime, which works in nanoseconds.
  257. #elif EASTDC_STOPWATCH_USE_GETTIMEOFDAY
  258. mnCPUFrequency = (nCPUDeltaTicks * 1000000 / nTimeDeltaUs); // We are using gettimeofday, which works in microseconds.
  259. #else
  260. mnCPUFrequency = (nCPUDeltaTicks * 1000000 / nTimeDeltaUs);
  261. #endif
  262. #if EASTDC_STOPWATCH_OVERHEAD_ENABLED
  263. // To do. Slightly less accuracy without this implemented.
  264. #endif
  265. #elif EASTDC_STOPWATCH_USE_CLOCK_GETTIME
  266. mnCPUFrequency = UINT64_C(1000000000); // We are using clock_gettime, which works in nanoseconds.
  267. #elif EASTDC_STOPWATCH_USE_GETTIMEOFDAY
  268. mnCPUFrequency = 1000000; // We are using gettimeofday, which works in microseconds.
  269. #else
  270. #error
  271. mnCPUFrequency = 1;
  272. #endif
  273. ////////////////////////////////////////////////////
  274. // Stopwatch Frequency
  275. #if EASTDC_STOPWATCH_USE_CLOCK_GETTIME
  276. mnStopwatchFrequency = UINT64_C(1000000000); // We are using clock_gettime, which works in nanoseconds.
  277. #elif EASTDC_STOPWATCH_USE_GETTIMEOFDAY
  278. mnStopwatchFrequency = 1000000; // We are using gettimeofday, which works in microseconds.
  279. #else
  280. mnStopwatchFrequency = mnCPUFrequency;
  281. #endif
  282. #endif
  283. EAStdCStopwatchSetupCoefficients();
  284. }
  285. }
  286. ///////////////////////////////////////////////////////////////////////////////
  287. // EAStdCStopwatchDisableCPUCalibration
  288. //
  289. // This function circumvents the EAStdCStopwatchSetup function for the purpose
  290. // of making it so that EAStdCStopwatchSetup doesn't take a long time to execute
  291. // on startup. If this function is executed before EAStdCStopwatchSetup then
  292. // if EAStdCStopwatchSetup is later executed then it will just immediately exit.
  293. // This function can be considered an alternative to the EAStdCStopwatchSetup
  294. // function that executes much faster. The downside is that the CPU frequency
  295. // will not be calculated or known and thus the CPU-based timing functions won't
  296. // be available (though the system time-based timing functions will be). If you
  297. // have a utility app that you need to run which needs to execute quickly and
  298. // doesn't need CPU-based clock tick timing precision, you might want to use
  299. // this function. See the EASTDC_STOPWATCH_DISABLE_CPU_CALIBRATION function for
  300. // how to do this. The most important thing is that this function needs to be
  301. // called before the EAStdCStopwatchSetup function, which may require some
  302. // compiler/platform-specific trickery as documented with EASTDC_STOPWATCH_DISABLE_CPU_CALIBRATION.
  303. //
  304. EASTDC_API void EAStdCStopwatchDisableCPUCalibration(uint64_t cpuFrequency)
  305. {
  306. if(cpuFrequency)
  307. mnCPUFrequency = cpuFrequency;
  308. else
  309. mnCPUFrequency = UINT64_C(2000000000); // We use a moderate guess here of 2GHz. To consider: Make this a very inaccurate value so that it's obvious it's wrong at runtime.
  310. ////////////////////////////////////////////////////
  311. // Stopwatch Frequency
  312. #if EASTDC_STOPWATCH_FORCE_CPU_CYCLE_USAGE
  313. mnStopwatchFrequency = mnCPUFrequency;
  314. #else
  315. #if defined(EA_PLATFORM_MICROSOFT)
  316. ::QueryPerformanceFrequency((LARGE_INTEGER*)&mnStopwatchFrequency);
  317. #else
  318. // To do.
  319. #endif
  320. #endif
  321. EAStdCStopwatchSetupCoefficients();
  322. }
  323. namespace EA
  324. {
  325. namespace StdC
  326. {
  327. //We have this #if !defined() commented out because we aren't sure that all GCC versions act the same across all platforms.
  328. //#if !defined(__GNUC__) // GCC uses __attribute__((constructor)) instead of this to call EAStdCStopwatchSetup.
  329. // AutoStopwatchSetup
  330. // We create this static class in order to trigger
  331. // automatic calling of the code below.
  332. struct AutoStopwatchSetup
  333. {
  334. AutoStopwatchSetup()
  335. { EAStdCStopwatchSetup(); }
  336. };
  337. AutoStopwatchSetup gAutoStopwatchSetup EA_INIT_PRIORITY(1000);
  338. //#endif
  339. }
  340. }
  341. ///////////////////////////////////////////////////////////////////////////////
  342. // Stopwatch
  343. ///////////////////////////////////////////////////////////////////////////////
  344. EA::StdC::Stopwatch::Stopwatch(int nUnits, bool bStartImmediately)
  345. : mnStartTime(0),
  346. mnTotalElapsedTime(0),
  347. mnUnits(0),
  348. mfStopwatchCyclesToUnitsCoefficient(1.f)
  349. {
  350. SetUnits(nUnits);
  351. if(mnStopwatchFrequency <= 1) // If not already initialized...
  352. EAStdCStopwatchSetup();
  353. if(bStartImmediately)
  354. Start();
  355. }
  356. void EA::StdC::Stopwatch::SetUnits(int nUnits)
  357. {
  358. mnUnits = nUnits;
  359. mfStopwatchCyclesToUnitsCoefficient = 1;
  360. switch (mnUnits)
  361. {
  362. case kUnitsCPUCycles:
  363. mfStopwatchCyclesToUnitsCoefficient = 1; // Timing is reported directly with GetCPUCycle().
  364. break;
  365. case kUnitsCycles:
  366. mfStopwatchCyclesToUnitsCoefficient = 1; // Timing is reported directly with GetStopwatchCycle().
  367. break;
  368. case kUnitsNanoseconds:
  369. mfStopwatchCyclesToUnitsCoefficient = mfStopwatchCyclesToNanosecondsCoefficient;
  370. break;
  371. case kUnitsMicroseconds:
  372. mfStopwatchCyclesToUnitsCoefficient = mfStopwatchCyclesToMicrosecondsCoefficient;
  373. break;
  374. case kUnitsMilliseconds:
  375. mfStopwatchCyclesToUnitsCoefficient = mfStopwatchCyclesToMillisecondsCoefficient;
  376. break;
  377. case kUnitsSeconds:
  378. mfStopwatchCyclesToUnitsCoefficient = mfStopwatchCyclesToSecondsCoefficient;
  379. break;
  380. case kUnitsMinutes:
  381. mfStopwatchCyclesToUnitsCoefficient = mfStopwatchCyclesToMinutesCoefficient;
  382. break;
  383. }
  384. }
  385. void EA::StdC::Stopwatch::Stop()
  386. {
  387. if(mnStartTime) // Check to make sure the stopwatch is actually running
  388. {
  389. // Note that below we compare the elapsed time (from the most recent start
  390. // to the this stop) to sStopwatchCycleReadingOverhead. For most timing situations,
  391. // the elapsed time will be *much* greater than the overhead. For some
  392. // cases (e.g. timing 10-20 lines of C code) the elapsed time will be only
  393. // 3-10 times the value of sStopwatchCycleReadingOverhead. In these cases, the
  394. // value of subtracting this overhead may be useful. For some cases the
  395. // code being timed is so small or brief that sStopwatchCycleReadingOverhead may
  396. // actually come out to be higher than the stretch of code. If this is the
  397. // case, you really don't want to be trying to time this code with a
  398. // softare-based stopwatch. What we do is simply set the elapsed time to a
  399. // small value such as 1, in order for code that is using this stopwatch to at
  400. // least believe that something is happening.
  401. const uint64_t nCurrentTime(mnUnits == kUnitsCPUCycles ? GetCPUCycle() : GetStopwatchCycle());
  402. // EA_ASSERT(nCurrentTime >= mnStartTime); We might want to enable this, at least for modern platforms.
  403. #if EASTDC_STOPWATCH_OVERHEAD_ENABLED
  404. const uint64_t nElapsedTime(nCurrentTime - mnStartTime);
  405. if(nElapsedTime > mnStopwatchCycleReadingOverhead)
  406. mnTotalElapsedTime += nElapsedTime - mnStopwatchCycleReadingOverhead;
  407. else
  408. mnTotalElapsedTime += 1; // We pretend that just one stopwatch cycle has elapsed.
  409. #else
  410. mnTotalElapsedTime += (nCurrentTime - mnStartTime);
  411. #endif
  412. mnStartTime = 0;
  413. }
  414. }
  415. uint64_t EA::StdC::Stopwatch::GetElapsedTime() const
  416. {
  417. uint64_t nFinalTotalElapsedTime64(mnTotalElapsedTime);
  418. if(mnStartTime) // We we are currently running, then take into account time passed since last start.
  419. {
  420. uint64_t nCurrentTime;
  421. // See the 'Stop' function for an explanation of the code below.
  422. if(mnUnits == kUnitsCPUCycles)
  423. nCurrentTime = GetCPUCycle();
  424. else
  425. nCurrentTime = GetStopwatchCycle();
  426. uint64_t nElapsed = nCurrentTime - mnStartTime;
  427. // EA_ASSERT(nCurrentTime >= mnStartTime); We might want to enable this, at least for modern platforms.
  428. #if EASTDC_STOPWATCH_OVERHEAD_ENABLED
  429. const uint64_t nElapsedTime = nElapsed;
  430. if(nElapsedTime > mnStopwatchCycleReadingOverhead)
  431. nFinalTotalElapsedTime64 += nElapsedTime - mnStopwatchCycleReadingOverhead;
  432. else
  433. nFinalTotalElapsedTime64 += 1; // We pretend that just one stopwatch cycle has elapsed.
  434. #else
  435. nFinalTotalElapsedTime64 += nElapsed;
  436. #endif
  437. } // Now nFinalTotalElapsedTime64 holds the elapsed time in stopwatch cycles.
  438. if(mfStopwatchCyclesToUnitsCoefficient == 0) // If the stopwatch was started before the global EAStdCStopwatchSetup function was executed...
  439. {
  440. // We can recover from this by calling SetUnits here, which will re-read the global setup results.
  441. const_cast<Stopwatch*>(this)->SetUnits(mnUnits);
  442. // EA_ASSERT(mfStopwatchCyclesToUnitsCoefficient != 0);
  443. }
  444. // We are doing a float to int cast here, which is a relatively slow operation on some CPUs.
  445. return (uint64_t)((nFinalTotalElapsedTime64 * mfStopwatchCyclesToUnitsCoefficient) + 0.49999f);
  446. }
  447. void EA::StdC::Stopwatch::SetElapsedTime(uint64_t nElapsedTime)
  448. {
  449. if(IsRunning())
  450. Restart();
  451. // Concern: The division here is not lightning fast and also is subject to precision error.
  452. mnTotalElapsedTime = (uint64_t)((nElapsedTime / mfStopwatchCyclesToUnitsCoefficient) + 0.49999f);
  453. }
  454. float EA::StdC::Stopwatch::GetElapsedTimeFloat() const
  455. {
  456. uint64_t nFinalTotalElapsedTime64(mnTotalElapsedTime);
  457. if(mnStartTime){ //We we are currently running, then take into account time passed since last start.
  458. uint64_t nCurrentTime;
  459. // See the 'Stop' function for an explanation of the code below.
  460. if(mnUnits == kUnitsCPUCycles)
  461. nCurrentTime = GetCPUCycle();
  462. else
  463. nCurrentTime = GetStopwatchCycle();
  464. uint64_t nElapsed = nCurrentTime - mnStartTime;
  465. // EA_ASSERT(nCurrentTime >= mnStartTime); We might want to enable this, at least for modern platforms.
  466. #if EASTDC_STOPWATCH_OVERHEAD_ENABLED
  467. const uint64_t nElapsedTime = nElapsed;
  468. if(nElapsedTime > mnStopwatchCycleReadingOverhead)
  469. nFinalTotalElapsedTime64 += nElapsedTime - mnStopwatchCycleReadingOverhead;
  470. else
  471. nFinalTotalElapsedTime64 += 1; // We pretend that just one stopwatch cycle has elapsed.
  472. #else
  473. nFinalTotalElapsedTime64 += nElapsed;
  474. #endif
  475. } // Now nFinalTotalElapsedTime64 holds the elapsed time in stopwatch cycles.
  476. // We are doing a float to int cast here, which is a relatively slow operation on some CPUs.
  477. return (float)(nFinalTotalElapsedTime64 * mfStopwatchCyclesToUnitsCoefficient);
  478. }
  479. void EA::StdC::Stopwatch::SetElapsedTimeFloat(float fElapsedTime)
  480. {
  481. if(IsRunning())
  482. Restart();
  483. // Concern: The division here is not lightning fast and also is subject to precision error.
  484. mnTotalElapsedTime = (uint64_t)(fElapsedTime / mfStopwatchCyclesToUnitsCoefficient);
  485. }
  486. float EA::StdC::Stopwatch::GetUnitsPerStopwatchCycle(Units units)
  487. {
  488. switch (units)
  489. {
  490. case kUnitsNanoseconds:
  491. return mfStopwatchCyclesToNanosecondsCoefficient;
  492. case kUnitsMicroseconds:
  493. return mfStopwatchCyclesToMicrosecondsCoefficient;
  494. case kUnitsMilliseconds:
  495. return mfStopwatchCyclesToMillisecondsCoefficient;
  496. case kUnitsSeconds:
  497. return mfStopwatchCyclesToSecondsCoefficient;
  498. case kUnitsMinutes:
  499. return mfStopwatchCyclesToMinutesCoefficient;
  500. case kUnitsCycles:
  501. case kUnitsCPUCycles:
  502. case kUnitsUserDefined:
  503. default:
  504. break;
  505. }
  506. return 1;
  507. }
  508. float EA::StdC::Stopwatch::GetUnitsPerCPUCycle(Units units)
  509. {
  510. switch (units)
  511. {
  512. case kUnitsNanoseconds:
  513. return mfCPUCyclesToNanosecondsCoefficient;
  514. case kUnitsMicroseconds:
  515. return mfCPUCyclesToMicrosecondsCoefficient;
  516. case kUnitsMilliseconds:
  517. return mfCPUCyclesToMillisecondsCoefficient;
  518. case kUnitsSeconds:
  519. return mfCPUCyclesToSecondsCoefficient;
  520. case kUnitsMinutes:
  521. return mfCPUCyclesToMinutesCoefficient;
  522. case kUnitsCycles:
  523. case kUnitsCPUCycles:
  524. case kUnitsUserDefined:
  525. default:
  526. break;
  527. }
  528. return 1;
  529. }
  530. // This function is here instead of inlined in the associated header file
  531. // because it uses mnStopwatchFrequency, which is a variable local to this
  532. // source file.
  533. uint64_t EA::StdC::Stopwatch::GetStopwatchFrequency()
  534. {
  535. return mnStopwatchFrequency;
  536. }
  537. // This function is here instead of inlined in the associated header file
  538. // because it uses mnStopwatchFrequency, which is a variable local to this
  539. // source file.
  540. uint64_t EA::StdC::Stopwatch::GetCPUFrequency()
  541. {
  542. return mnCPUFrequency;
  543. }
  544. ///////////////////////////////////////////////////////////////////////////////
  545. // LimitStopwatch
  546. ///////////////////////////////////////////////////////////////////////////////
  547. void EA::StdC::LimitStopwatch::SetTimeLimit(uint64_t nLimit, bool bStartImmediately)
  548. {
  549. const uint64_t nCurrentTime = GetStopwatchCycle();
  550. mnEndTime = nCurrentTime + (uint64_t)(nLimit / mfStopwatchCyclesToUnitsCoefficient);
  551. if(bStartImmediately)
  552. Start();
  553. }
  554. float EA::StdC::LimitStopwatch::GetTimeRemainingFloat() const
  555. {
  556. const uint64_t nCurrentTime = GetStopwatchCycle();
  557. const float fTimeRemaining = (float)((int64_t)(mnEndTime - nCurrentTime)) * mfStopwatchCyclesToUnitsCoefficient;
  558. return fTimeRemaining;
  559. }