/////////////////////////////////////////////////////////////////////////////// // Copyright (c) Electronic Arts Inc. All rights reserved. /////////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////// // Implements a stopwatch-style stopwatch. This is useful for both benchmarking // and for implementing runtime timing. ///////////////////////////////////////////////////////////////////////////// #include #include #if defined(EA_PLATFORM_MICROSOFT) #pragma warning(push, 0) #include #pragma warning(pop) #if (defined(EA_PLATFORM_WINDOWS) && !EA_WINAPI_FAMILY_PARTITION(EA_WINAPI_PARTITION_DESKTOP)) || defined(EA_PLATFORM_WINDOWS_PHONE) EA_DISABLE_ALL_VC_WARNINGS() #include #include EA_RESTORE_ALL_VC_WARNINGS() #endif #elif defined(EA_PLATFORM_SONY) #include #elif defined(EA_PLATFORM_POSIX) #include #endif /////////////////////////////////////////////////////////////////////////////// // Auto-setup code // // The code below is for setting up the stopwatch system, and it's needed // because it allows us to know at runtime what the stopwatch frequency is // without having to spend CPU cycles repeatedly calculating it at runtime. // The code below is set up to auto-execute on startup when possible, but // it's OK if it doesn't because the Stopwatch constructor auto-checks for // initialization and calls if it not already. However, the auto code below // is still useful because it's better to get this taken care of on startup // rather than unexpectedly during runtime (as it takes a few milliseconds // on some platforms). /////////////////////////////////////////////////////////////////////////////// #if defined(_MSC_VER) #ifndef EASTDC_INIT_SEG_DEFINED #define EASTDC_INIT_SEG_DEFINED // Set initialization order between init_seg(compiler) (.CRT$XCC) and // init_seg(lib) (.CRT$XCL). The MSVC linker sorts the .CRT sections // alphabetically so we simply need to pick a name that is between // XCC and XCL. This works on both Windows and XBox. #pragma warning(disable: 4075) // "initializers put in unrecognized initialization area" #pragma init_seg(".CRT$XCF") #endif #elif defined(__GNUC__) // By adding the 'constructor' attribute to this function, we tell GCC // to have this function be called on startup before main(). We have the // AutoStopwatchSetup mechanism below, but GCC ignores it because the // object isn't externally reference and GCC thinks it can thus be eliminated. void EAStdCStopwatchSetupCoefficients(); void EAStdCStopwatchSetup() __attribute__ ((constructor)); void EAStdCStopwatchDisableCPUCalibration(uint64_t cpuFrequency); #else // Some compilers require these functions to be pre-declared. void EAStdCStopwatchSetupCoefficients(); void EAStdCStopwatchSetup(); void EAStdCStopwatchDisableCPUCalibration(uint64_t cpuFrequency); #endif // Anonymous namespace // Has the effect of making things declared within it be static, but with some improvements. namespace { // Stopwatch cycle metrics uint64_t mnStopwatchFrequency(1); // Set to one to prevent possible div/0 errors. float mfStopwatchCyclesToNanosecondsCoefficient; float mfStopwatchCyclesToMicrosecondsCoefficient; float mfStopwatchCyclesToMillisecondsCoefficient; float mfStopwatchCyclesToSecondsCoefficient; float mfStopwatchCyclesToMinutesCoefficient; // CPU cycle metrics uint64_t mnCPUFrequency(1); // Set to one to prevent possible div/0 errors. float mfCPUCyclesToNanosecondsCoefficient; float mfCPUCyclesToMicrosecondsCoefficient; float mfCPUCyclesToMillisecondsCoefficient; float mfCPUCyclesToSecondsCoefficient; float mfCPUCyclesToMinutesCoefficient; #if EASTDC_STOPWATCH_OVERHEAD_ENABLED uint64_t mnCPUCycleReadingOverhead(0); uint64_t mnStopwatchCycleReadingOverhead(0); #endif #if defined(EA_PLATFORM_MICROSOFT) void EAStdCThreadSleep(int ms) { #if (defined(EA_PLATFORM_WINDOWS) && !EA_WINAPI_FAMILY_PARTITION(EA_WINAPI_PARTITION_DESKTOP)) || defined(EA_PLATFORM_WINDOWS_PHONE) std::chrono::milliseconds duration(ms); std::this_thread::sleep_for(duration); #else ::SleepEx((DWORD)ms, TRUE); #endif } #endif } void EAStdCStopwatchSetupCoefficients() { // Calculate coefficients. mfStopwatchCyclesToMinutesCoefficient = 1.f / 60.f / (int64_t)mnStopwatchFrequency; // Some compilers require the mfStopwatchCyclesToSecondsCoefficient = 1.f / (int64_t)mnStopwatchFrequency; // conversion to int64_t for the math. mfStopwatchCyclesToMillisecondsCoefficient = 1000.f / (int64_t)mnStopwatchFrequency; mfStopwatchCyclesToMicrosecondsCoefficient = 1000000.f / (int64_t)mnStopwatchFrequency; mfStopwatchCyclesToNanosecondsCoefficient = 1000000000.f / (int64_t)mnStopwatchFrequency; #if EASTDC_STOPWATCH_OVERHEAD_ENABLED // Here we make a rough measurement of the start and stop overhead of // the stopwatch code. It is hard to say what a good way to determine this is, // as the runtime use of the stopwatch will actually cause the overhead to // vary somewhat between uses. It is perhaps even debateable whether we // should even be attempting to do such overhead calculations. uint64_t nCurrentStopwatchCycleValue1; uint64_t nCurrentStopwatchCycleValue2; uint64_t nLowestStopwatchCycleReadingOverhead(UINT64_MAX); uint64_t nCurrentStopwatchCycleReadingOverhead; for(int t(0); t < 8; t++) { nCurrentStopwatchCycleValue1 = EA::StdC::Stopwatch::GetStopwatchCycle(); nCurrentStopwatchCycleValue2 = EA::StdC::Stopwatch::GetStopwatchCycle(); nCurrentStopwatchCycleReadingOverhead = nCurrentStopwatchCycleValue2 - nCurrentStopwatchCycleValue1; if(nLowestStopwatchCycleReadingOverhead > nCurrentStopwatchCycleReadingOverhead) nLowestStopwatchCycleReadingOverhead = nCurrentStopwatchCycleReadingOverhead; } mnStopwatchCycleReadingOverhead = nLowestStopwatchCycleReadingOverhead; #endif // CPU Frequencies mfCPUCyclesToMinutesCoefficient = 1.f / 60.f / (int64_t)mnCPUFrequency; mfCPUCyclesToSecondsCoefficient = 1.f / (int64_t)mnCPUFrequency; mfCPUCyclesToMillisecondsCoefficient = 1000.f / (int64_t)mnCPUFrequency; mfCPUCyclesToMicrosecondsCoefficient = 1000000.f / (int64_t)mnCPUFrequency; mfCPUCyclesToNanosecondsCoefficient = 1000000000.f / (int64_t)mnCPUFrequency; #if EASTDC_STOPWATCH_OVERHEAD_ENABLED uint64_t nCurrentCPUCycleValue1, nCurrentCPUCycleValue2; uint64_t nLowestCPUCycleReadingOverhead(UINT64_MAX); uint64_t nCurrentCPUCycleReadingOverhead; for(int c = 0; c < 8; c++) { nCurrentCPUCycleValue1 = EA::StdC::Stopwatch::GetCPUCycle(); nCurrentCPUCycleValue2 = EA::StdC::Stopwatch::GetCPUCycle(); nCurrentCPUCycleReadingOverhead = nCurrentCPUCycleValue2 - nCurrentCPUCycleValue1; if(nLowestCPUCycleReadingOverhead > nCurrentCPUCycleReadingOverhead) nLowestCPUCycleReadingOverhead = nCurrentCPUCycleReadingOverhead; } mnCPUCycleReadingOverhead = nLowestCPUCycleReadingOverhead; #endif } void EAStdCStopwatchSetup() { if(mnStopwatchFrequency <= 1) // If we haven't already calculated this... { #if defined(EA_PLATFORM_SONY) // According to Sony: A time stamp counter exists for each CPU core, but the frequency is the same // value for all CPU cores. This frequency will not change during the lifetime of a process. mnCPUFrequency = sceKernelGetProcessTimeCounterFrequency(); mnStopwatchFrequency = mnCPUFrequency; #elif defined(__APPLE__) mach_timebase_info_data_t timebaseInfo; mach_timebase_info(&timebaseInfo); mnCPUFrequency = (UINT64_C(1000000000) * (uint64_t)timebaseInfo.denom) / (uint64_t)timebaseInfo.numer; //////////////////////////////////////////////////// // Stopwatch Frequency mnStopwatchFrequency = mnCPUFrequency; #elif defined(EA_PLATFORM_XBOXONE) || defined(CS_UNDEFINED_STRING) // Microsoft has stated (https://forums.xboxlive.com/AnswerPage.aspx?qid=c3fcdad5-f3e4-46d9-85f9-d337506f0d6b&tgt=1) that // QueryPerformanceCounter / QueryPerformanceFrequency map to rdtsc and they are stable throughout the life of the process. // Thus we can use QueryPerformanceFrequency to portable tell the CPU frequency for our usage of rdtsc. QueryPerformanceFrequency(reinterpret_cast(&mnStopwatchFrequency)); mnCPUFrequency = mnStopwatchFrequency; #elif defined(EA_PLATFORM_MICROSOFT) // On Windows, the only way to tell the CPU-based timer frequency is to manually // time it. There is no function in Windows which tells you this information. // Indeed such a function might not be useful for CPU frequency-switching systems. // SetPriorityClass / SetThreadPriority APIs not available for Windows 8 Metro apps #if EA_WINAPI_FAMILY_PARTITION(EA_WINAPI_PARTITION_DESKTOP) HANDLE process = ::GetCurrentProcess(); DWORD oldPriorityClass = ::GetPriorityClass(process); HANDLE thread = ::GetCurrentThread(); int oldThreadPriority = ::GetThreadPriority(thread); // Set process/thread priorities. ::SetPriorityClass(process, REALTIME_PRIORITY_CLASS); ::SetThreadPriority(thread, THREAD_PRIORITY_TIME_CRITICAL); #endif //////////////////////////////////////////////////// // Stopwatch Frequency //////////////////////////////////////////////////// // CPU Frequency // Unfortunately, you can't call Win32 QueryPerformanceFrequency and // expect that it will give you the processor frequency in megahertz. // Doing such a thing would work for some CPUs and not others. In fact, // under Windows 2000, it works for my PIII-733 but not for my PII-300. // So what we do instead is simply find the ratio of the QueryPerformanceCounter // and our Stopwatch::GetCPUCycle functions. mnCPUFrequency = 0; for(int i(0); i < 5; i++) // Give N chances at this, in case OS pre-emption occurs. { uint64_t nQPCCounter; ::QueryPerformanceCounter((LARGE_INTEGER*)&nQPCCounter); uint64_t nCPUCounter = EA::StdC::Stopwatch::GetCPUCycle(); double dRatio = (double)nCPUCounter / (double)nQPCCounter; if((dRatio > 0.98 && dRatio < 1.02)) { ::QueryPerformanceFrequency((LARGE_INTEGER*)&mnCPUFrequency); break; } } if(!mnCPUFrequency) { // Do our own manual timing of clock ticks. // We use Win32 QueryPerformanceCounter and QueryPerformanceFrequency to // calibrate our CPU cycle counter. uint64_t nQPFrequency, nQPCCounter1, nQPCCounter2; uint64_t nCPUCounter1, nCPUCounter2=0; double dQPCSeconds(0); ::QueryPerformanceFrequency((LARGE_INTEGER*)&nQPFrequency); ::QueryPerformanceCounter((LARGE_INTEGER*)&nQPCCounter1); nCPUCounter1 = EA::StdC::Stopwatch::GetCPUCycle(); #if EASTDC_STOPWATCH_FORCE_CPU_CYCLE_USAGE static const double TIME_IN_SECONDS_TO_MEASURE_TICKS = 0.300; #else // Given that ticks are considered to be unreliable we can tolerate lower accuracy measurement // of number of ticks per second. https://en.wikipedia.org/wiki/Time_Stamp_Counter // Here the largest limitation will be the MS Perf counter accuracy which is less than 1us. static const double TIME_IN_SECONDS_TO_MEASURE_TICKS = 0.005; #endif while(dQPCSeconds < TIME_IN_SECONDS_TO_MEASURE_TICKS) { ::QueryPerformanceCounter((LARGE_INTEGER*)&nQPCCounter2); nCPUCounter2 = EA::StdC::Stopwatch::GetCPUCycle(); dQPCSeconds = (nQPCCounter2 - nQPCCounter1) / (double)nQPFrequency; } mnCPUFrequency = (uint64_t)((nCPUCounter2 - nCPUCounter1) / dQPCSeconds); } #if EASTDC_STOPWATCH_FORCE_CPU_CYCLE_USAGE mnStopwatchFrequency = mnCPUFrequency; #else ::QueryPerformanceFrequency((LARGE_INTEGER*)&mnStopwatchFrequency); #endif // Reset process/thread priorities. #if EA_WINAPI_FAMILY_PARTITION(EA_WINAPI_PARTITION_DESKTOP) ::SetPriorityClass(process, oldPriorityClass); ::SetThreadPriority(thread, oldThreadPriority); #endif #else //////////////////////////////////////////////////// // CPU Frequency #ifdef EASTDC_CPU_FREQ_CALCULATED // On Unix, the only way to tell the CPU-based timer frequency is to manually // time it. There is no function in Unix which tells you this information. // Indeed such a function might not be useful for CPU frequency-switching systems. uint64_t nTimeCounter1 = EA::StdC::Stopwatch::GetStopwatchCycle(); uint64_t nCPUCounter1 = EA::StdC::Stopwatch::GetCPUCycle(); usleep(250000); // Sleep for a ~quarter second. uint64_t nCPUCounter2 = EA::StdC::Stopwatch::GetCPUCycle(); uint64_t nTimeCounter2 = EA::StdC::Stopwatch::GetStopwatchCycle(); uint64_t nTimeDeltaUs = nTimeCounter2 - nTimeCounter1; uint64_t nCPUDeltaTicks = nCPUCounter2 - nCPUCounter1; // GetStopwatchCycle will have varying resolution so we need to account for that accordingly #if EASTDC_STOPWATCH_USE_CLOCK_GETTIME mnCPUFrequency = (nCPUDeltaTicks * 1000000000 / nTimeDeltaUs); // We are using clock_gettime, which works in nanoseconds. #elif EASTDC_STOPWATCH_USE_GETTIMEOFDAY mnCPUFrequency = (nCPUDeltaTicks * 1000000 / nTimeDeltaUs); // We are using gettimeofday, which works in microseconds. #else mnCPUFrequency = (nCPUDeltaTicks * 1000000 / nTimeDeltaUs); #endif #if EASTDC_STOPWATCH_OVERHEAD_ENABLED // To do. Slightly less accuracy without this implemented. #endif #elif EASTDC_STOPWATCH_USE_CLOCK_GETTIME mnCPUFrequency = UINT64_C(1000000000); // We are using clock_gettime, which works in nanoseconds. #elif EASTDC_STOPWATCH_USE_GETTIMEOFDAY mnCPUFrequency = 1000000; // We are using gettimeofday, which works in microseconds. #else #error mnCPUFrequency = 1; #endif //////////////////////////////////////////////////// // Stopwatch Frequency #if EASTDC_STOPWATCH_USE_CLOCK_GETTIME mnStopwatchFrequency = UINT64_C(1000000000); // We are using clock_gettime, which works in nanoseconds. #elif EASTDC_STOPWATCH_USE_GETTIMEOFDAY mnStopwatchFrequency = 1000000; // We are using gettimeofday, which works in microseconds. #else mnStopwatchFrequency = mnCPUFrequency; #endif #endif EAStdCStopwatchSetupCoefficients(); } } /////////////////////////////////////////////////////////////////////////////// // EAStdCStopwatchDisableCPUCalibration // // This function circumvents the EAStdCStopwatchSetup function for the purpose // of making it so that EAStdCStopwatchSetup doesn't take a long time to execute // on startup. If this function is executed before EAStdCStopwatchSetup then // if EAStdCStopwatchSetup is later executed then it will just immediately exit. // This function can be considered an alternative to the EAStdCStopwatchSetup // function that executes much faster. The downside is that the CPU frequency // will not be calculated or known and thus the CPU-based timing functions won't // be available (though the system time-based timing functions will be). If you // have a utility app that you need to run which needs to execute quickly and // doesn't need CPU-based clock tick timing precision, you might want to use // this function. See the EASTDC_STOPWATCH_DISABLE_CPU_CALIBRATION function for // how to do this. The most important thing is that this function needs to be // called before the EAStdCStopwatchSetup function, which may require some // compiler/platform-specific trickery as documented with EASTDC_STOPWATCH_DISABLE_CPU_CALIBRATION. // EASTDC_API void EAStdCStopwatchDisableCPUCalibration(uint64_t cpuFrequency) { if(cpuFrequency) mnCPUFrequency = cpuFrequency; else 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. //////////////////////////////////////////////////// // Stopwatch Frequency #if EASTDC_STOPWATCH_FORCE_CPU_CYCLE_USAGE mnStopwatchFrequency = mnCPUFrequency; #else #if defined(EA_PLATFORM_MICROSOFT) ::QueryPerformanceFrequency((LARGE_INTEGER*)&mnStopwatchFrequency); #else // To do. #endif #endif EAStdCStopwatchSetupCoefficients(); } namespace EA { namespace StdC { //We have this #if !defined() commented out because we aren't sure that all GCC versions act the same across all platforms. //#if !defined(__GNUC__) // GCC uses __attribute__((constructor)) instead of this to call EAStdCStopwatchSetup. // AutoStopwatchSetup // We create this static class in order to trigger // automatic calling of the code below. struct AutoStopwatchSetup { AutoStopwatchSetup() { EAStdCStopwatchSetup(); } }; AutoStopwatchSetup gAutoStopwatchSetup EA_INIT_PRIORITY(1000); //#endif } } /////////////////////////////////////////////////////////////////////////////// // Stopwatch /////////////////////////////////////////////////////////////////////////////// EA::StdC::Stopwatch::Stopwatch(int nUnits, bool bStartImmediately) : mnStartTime(0), mnTotalElapsedTime(0), mnUnits(0), mfStopwatchCyclesToUnitsCoefficient(1.f) { SetUnits(nUnits); if(mnStopwatchFrequency <= 1) // If not already initialized... EAStdCStopwatchSetup(); if(bStartImmediately) Start(); } void EA::StdC::Stopwatch::SetUnits(int nUnits) { mnUnits = nUnits; mfStopwatchCyclesToUnitsCoefficient = 1; switch (mnUnits) { case kUnitsCPUCycles: mfStopwatchCyclesToUnitsCoefficient = 1; // Timing is reported directly with GetCPUCycle(). break; case kUnitsCycles: mfStopwatchCyclesToUnitsCoefficient = 1; // Timing is reported directly with GetStopwatchCycle(). break; case kUnitsNanoseconds: mfStopwatchCyclesToUnitsCoefficient = mfStopwatchCyclesToNanosecondsCoefficient; break; case kUnitsMicroseconds: mfStopwatchCyclesToUnitsCoefficient = mfStopwatchCyclesToMicrosecondsCoefficient; break; case kUnitsMilliseconds: mfStopwatchCyclesToUnitsCoefficient = mfStopwatchCyclesToMillisecondsCoefficient; break; case kUnitsSeconds: mfStopwatchCyclesToUnitsCoefficient = mfStopwatchCyclesToSecondsCoefficient; break; case kUnitsMinutes: mfStopwatchCyclesToUnitsCoefficient = mfStopwatchCyclesToMinutesCoefficient; break; } } void EA::StdC::Stopwatch::Stop() { if(mnStartTime) // Check to make sure the stopwatch is actually running { // Note that below we compare the elapsed time (from the most recent start // to the this stop) to sStopwatchCycleReadingOverhead. For most timing situations, // the elapsed time will be *much* greater than the overhead. For some // cases (e.g. timing 10-20 lines of C code) the elapsed time will be only // 3-10 times the value of sStopwatchCycleReadingOverhead. In these cases, the // value of subtracting this overhead may be useful. For some cases the // code being timed is so small or brief that sStopwatchCycleReadingOverhead may // actually come out to be higher than the stretch of code. If this is the // case, you really don't want to be trying to time this code with a // softare-based stopwatch. What we do is simply set the elapsed time to a // small value such as 1, in order for code that is using this stopwatch to at // least believe that something is happening. const uint64_t nCurrentTime(mnUnits == kUnitsCPUCycles ? GetCPUCycle() : GetStopwatchCycle()); // EA_ASSERT(nCurrentTime >= mnStartTime); We might want to enable this, at least for modern platforms. #if EASTDC_STOPWATCH_OVERHEAD_ENABLED const uint64_t nElapsedTime(nCurrentTime - mnStartTime); if(nElapsedTime > mnStopwatchCycleReadingOverhead) mnTotalElapsedTime += nElapsedTime - mnStopwatchCycleReadingOverhead; else mnTotalElapsedTime += 1; // We pretend that just one stopwatch cycle has elapsed. #else mnTotalElapsedTime += (nCurrentTime - mnStartTime); #endif mnStartTime = 0; } } uint64_t EA::StdC::Stopwatch::GetElapsedTime() const { uint64_t nFinalTotalElapsedTime64(mnTotalElapsedTime); if(mnStartTime) // We we are currently running, then take into account time passed since last start. { uint64_t nCurrentTime; // See the 'Stop' function for an explanation of the code below. if(mnUnits == kUnitsCPUCycles) nCurrentTime = GetCPUCycle(); else nCurrentTime = GetStopwatchCycle(); uint64_t nElapsed = nCurrentTime - mnStartTime; // EA_ASSERT(nCurrentTime >= mnStartTime); We might want to enable this, at least for modern platforms. #if EASTDC_STOPWATCH_OVERHEAD_ENABLED const uint64_t nElapsedTime = nElapsed; if(nElapsedTime > mnStopwatchCycleReadingOverhead) nFinalTotalElapsedTime64 += nElapsedTime - mnStopwatchCycleReadingOverhead; else nFinalTotalElapsedTime64 += 1; // We pretend that just one stopwatch cycle has elapsed. #else nFinalTotalElapsedTime64 += nElapsed; #endif } // Now nFinalTotalElapsedTime64 holds the elapsed time in stopwatch cycles. if(mfStopwatchCyclesToUnitsCoefficient == 0) // If the stopwatch was started before the global EAStdCStopwatchSetup function was executed... { // We can recover from this by calling SetUnits here, which will re-read the global setup results. const_cast(this)->SetUnits(mnUnits); // EA_ASSERT(mfStopwatchCyclesToUnitsCoefficient != 0); } // We are doing a float to int cast here, which is a relatively slow operation on some CPUs. return (uint64_t)((nFinalTotalElapsedTime64 * mfStopwatchCyclesToUnitsCoefficient) + 0.49999f); } void EA::StdC::Stopwatch::SetElapsedTime(uint64_t nElapsedTime) { if(IsRunning()) Restart(); // Concern: The division here is not lightning fast and also is subject to precision error. mnTotalElapsedTime = (uint64_t)((nElapsedTime / mfStopwatchCyclesToUnitsCoefficient) + 0.49999f); } float EA::StdC::Stopwatch::GetElapsedTimeFloat() const { uint64_t nFinalTotalElapsedTime64(mnTotalElapsedTime); if(mnStartTime){ //We we are currently running, then take into account time passed since last start. uint64_t nCurrentTime; // See the 'Stop' function for an explanation of the code below. if(mnUnits == kUnitsCPUCycles) nCurrentTime = GetCPUCycle(); else nCurrentTime = GetStopwatchCycle(); uint64_t nElapsed = nCurrentTime - mnStartTime; // EA_ASSERT(nCurrentTime >= mnStartTime); We might want to enable this, at least for modern platforms. #if EASTDC_STOPWATCH_OVERHEAD_ENABLED const uint64_t nElapsedTime = nElapsed; if(nElapsedTime > mnStopwatchCycleReadingOverhead) nFinalTotalElapsedTime64 += nElapsedTime - mnStopwatchCycleReadingOverhead; else nFinalTotalElapsedTime64 += 1; // We pretend that just one stopwatch cycle has elapsed. #else nFinalTotalElapsedTime64 += nElapsed; #endif } // Now nFinalTotalElapsedTime64 holds the elapsed time in stopwatch cycles. // We are doing a float to int cast here, which is a relatively slow operation on some CPUs. return (float)(nFinalTotalElapsedTime64 * mfStopwatchCyclesToUnitsCoefficient); } void EA::StdC::Stopwatch::SetElapsedTimeFloat(float fElapsedTime) { if(IsRunning()) Restart(); // Concern: The division here is not lightning fast and also is subject to precision error. mnTotalElapsedTime = (uint64_t)(fElapsedTime / mfStopwatchCyclesToUnitsCoefficient); } float EA::StdC::Stopwatch::GetUnitsPerStopwatchCycle(Units units) { switch (units) { case kUnitsNanoseconds: return mfStopwatchCyclesToNanosecondsCoefficient; case kUnitsMicroseconds: return mfStopwatchCyclesToMicrosecondsCoefficient; case kUnitsMilliseconds: return mfStopwatchCyclesToMillisecondsCoefficient; case kUnitsSeconds: return mfStopwatchCyclesToSecondsCoefficient; case kUnitsMinutes: return mfStopwatchCyclesToMinutesCoefficient; case kUnitsCycles: case kUnitsCPUCycles: case kUnitsUserDefined: default: break; } return 1; } float EA::StdC::Stopwatch::GetUnitsPerCPUCycle(Units units) { switch (units) { case kUnitsNanoseconds: return mfCPUCyclesToNanosecondsCoefficient; case kUnitsMicroseconds: return mfCPUCyclesToMicrosecondsCoefficient; case kUnitsMilliseconds: return mfCPUCyclesToMillisecondsCoefficient; case kUnitsSeconds: return mfCPUCyclesToSecondsCoefficient; case kUnitsMinutes: return mfCPUCyclesToMinutesCoefficient; case kUnitsCycles: case kUnitsCPUCycles: case kUnitsUserDefined: default: break; } return 1; } // This function is here instead of inlined in the associated header file // because it uses mnStopwatchFrequency, which is a variable local to this // source file. uint64_t EA::StdC::Stopwatch::GetStopwatchFrequency() { return mnStopwatchFrequency; } // This function is here instead of inlined in the associated header file // because it uses mnStopwatchFrequency, which is a variable local to this // source file. uint64_t EA::StdC::Stopwatch::GetCPUFrequency() { return mnCPUFrequency; } /////////////////////////////////////////////////////////////////////////////// // LimitStopwatch /////////////////////////////////////////////////////////////////////////////// void EA::StdC::LimitStopwatch::SetTimeLimit(uint64_t nLimit, bool bStartImmediately) { const uint64_t nCurrentTime = GetStopwatchCycle(); mnEndTime = nCurrentTime + (uint64_t)(nLimit / mfStopwatchCyclesToUnitsCoefficient); if(bStartImmediately) Start(); } float EA::StdC::LimitStopwatch::GetTimeRemainingFloat() const { const uint64_t nCurrentTime = GetStopwatchCycle(); const float fTimeRemaining = (float)((int64_t)(mnEndTime - nCurrentTime)) * mfStopwatchCyclesToUnitsCoefficient; return fTimeRemaining; }