Merge pull request #429 from JeffProgrammer/high_resolution_timer

High resolution timer fixes
This commit is contained in:
Brian Roberts 2020-12-30 19:37:52 -06:00 committed by GitHub
commit a0581dce2a
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 38 additions and 110 deletions

View file

@ -23,11 +23,9 @@
#include "platform/platform.h" #include "platform/platform.h"
#if defined(TORQUE_OS_WIN) #if defined(TORQUE_OS_WIN)
#include<Windows.h> // for SetThreadAffinityMask #include<Windows.h> // for SetThreadAffinityMask, QueryPerformanceCounter, QueryPerformanceFrequency
#endif #elif defined(TORQUE_OS_MAC)
#include <mach/mach_time.h> // for mach_absolute_time, mach_timebase_info
#if defined(TORQUE_OS_MAC)
#include <mach/mach_time.h>
#endif #endif
#include "core/stream/fileStream.h" #include "core/stream/fileStream.h"
@ -63,111 +61,61 @@ Vector<StringTableEntry> gProfilerNodeStack;
#define PROFILER_DEBUG_POP_NODE() ; #define PROFILER_DEBUG_POP_NODE() ;
#endif #endif
#if defined(TORQUE_SUPPORTS_VC_INLINE_X86_ASM) #if defined(TORQUE_OS_WIN)
// platform specific get hires times...
void startHighResolutionTimer(U32 time[2])
{
//time[0] = Platform::getRealMilliseconds();
__asm static bool sQueryPerformanceInit = false;
{ static U64 sQueryPerformanceFrequency = 0;
push eax
push edx
push ecx
rdtsc
mov ecx, time
mov DWORD PTR [ecx], eax
mov DWORD PTR [ecx + 4], edx
pop ecx
pop edx
pop eax
}
}
U32 endHighResolutionTimer(U32 time[2])
{
U32 ticks;
//ticks = Platform::getRealMilliseconds() - time[0];
//return ticks;
__asm
{
push eax
push edx
push ecx
//db 0fh, 31h
rdtsc
mov ecx, time
sub edx, DWORD PTR [ecx+4]
sbb eax, DWORD PTR [ecx]
mov DWORD PTR ticks, eax
pop ecx
pop edx
pop eax
}
return ticks;
}
#elif defined(TORQUE_SUPPORTS_GCC_INLINE_X86_ASM)
// platform specific get hires times... // platform specific get hires times...
void startHighResolutionTimer(U32 time[2]) void startHighResolutionTimer(U64 &time)
{ {
__asm__ __volatile__( QueryPerformanceCounter((LARGE_INTEGER*)&time);
"rdtsc\n"
: "=a" (time[0]), "=d" (time[1])
);
} }
U32 endHighResolutionTimer(U32 time[2]) F64 endHighResolutionTimer(U64 time)
{ {
U32 ticks; if (!sQueryPerformanceInit)
__asm__ __volatile__( {
"rdtsc\n" sQueryPerformanceInit = true;
"sub 0x4(%%ecx), %%edx\n" QueryPerformanceFrequency((LARGE_INTEGER*)&sQueryPerformanceFrequency);
"sbb (%%ecx), %%eax\n" }
: "=a" (ticks) : "c" (time)
); U64 current;
return ticks; QueryPerformanceCounter((LARGE_INTEGER*)&current);
return ((1000.0 * static_cast<F64>(current-time)) / static_cast<F64>(sQueryPerformanceFrequency));
} }
#elif defined(TORQUE_OS_MAC) #elif defined(TORQUE_OS_MAC)
void startHighResolutionTimer(U64 &time) {
void startHighResolutionTimer(U32 time[2]) { time = mach_absolute_time();
U64 now = mach_absolute_time();
AssertFatal(sizeof(U32[2]) == sizeof(U64), "Can't pack mach_absolute_time into U32[2]");
memcpy(time, &now, sizeof(U64));
} }
U32 endHighResolutionTimer(U32 time[2]) { F64 endHighResolutionTimer(U64 time) {
static mach_timebase_info_data_t sTimebaseInfo = {0, 0}; static mach_timebase_info_data_t sTimebaseInfo = {0, 0};
U64 now = mach_absolute_time(); U64 now = mach_absolute_time();
AssertFatal(sizeof(U32[2]) == sizeof(U64), "Can't pack mach_absolute_time into U32[2]");
U64 then;
memcpy(&then, time, sizeof(U64));
if(sTimebaseInfo.denom == 0){ if(sTimebaseInfo.denom == 0){
mach_timebase_info(&sTimebaseInfo); mach_timebase_info(&sTimebaseInfo);
} }
// Handle the micros/nanos conversion first, because shedding a few bits is better than overflowing. // Handle the micros/nanos conversion first, because shedding a few bits is better than overflowing.
U64 elapsedMicros = ((now - then) / 1000) * sTimebaseInfo.numer / sTimebaseInfo.denom; F64 elapsedMicros = (static_cast<F64>(now - time) / 1000.0) * static_cast<F64>(sTimebaseInfo.numer) / static_cast<F64>(sTimebaseInfo.denom);
return (U32)elapsedMicros; // Just truncate, and hope we didn't overflow return elapsedMicros; // Just truncate, and hope we didn't overflow
} }
#else #else
void startHighResolutionTimer(U32 time[2]) void startHighResolutionTimer(U64 &time)
{ {
time[0] = Platform::getRealMilliseconds(); time = (U64)Platform::getRealMilliseconds();
} }
U32 endHighResolutionTimer(U32 time[2]) F64 endHighResolutionTimer(U64 time)
{ {
U32 ticks = Platform::getRealMilliseconds() - time[0]; return (F64)Platform::getRealMilliseconds() - time;
return ticks;
} }
#endif #endif

View file

@ -153,7 +153,7 @@ struct ProfilerData
U32 mHash; U32 mHash;
U32 mSubDepth; U32 mSubDepth;
U32 mInvokeCount; U32 mInvokeCount;
U32 mStartTime[2]; U64 mStartTime;
F64 mTotalTime; F64 mTotalTime;
F64 mSubTime; F64 mSubTime;
#ifdef TORQUE_ENABLE_PROFILE_PATH #ifdef TORQUE_ENABLE_PROFILE_PATH

View file

@ -30,14 +30,11 @@
class Win32Timer : public PlatformTimer class Win32Timer : public PlatformTimer
{ {
private: private:
U32 mTickCountCurrent;
U32 mTickCountNext;
S64 mPerfCountCurrent; S64 mPerfCountCurrent;
S64 mPerfCountNext; S64 mPerfCountNext;
S64 mFrequency; S64 mFrequency;
F64 mPerfCountRemainderCurrent; F64 mPerfCountRemainderCurrent;
F64 mPerfCountRemainderNext; F64 mPerfCountRemainderNext;
bool mUsingPerfCounter;
public: public:
Win32Timer() Win32Timer()
@ -45,43 +42,26 @@ public:
mPerfCountRemainderCurrent = 0.0f; mPerfCountRemainderCurrent = 0.0f;
mPerfCountRemainderNext = 0.0f; mPerfCountRemainderNext = 0.0f;
// Attempt to use QPC for high res timing, otherwise fallback to GTC. QueryPerformanceFrequency((LARGE_INTEGER *) &mFrequency);
mUsingPerfCounter = QueryPerformanceFrequency((LARGE_INTEGER *) &mFrequency); QueryPerformanceCounter((LARGE_INTEGER *) &mPerfCountCurrent);
if(mUsingPerfCounter)
mUsingPerfCounter = QueryPerformanceCounter((LARGE_INTEGER *) &mPerfCountCurrent);
mPerfCountNext = 0.0; mPerfCountNext = 0.0;
if (!mUsingPerfCounter)
mTickCountCurrent = GetTickCount();
else
mTickCountCurrent = 0;
mTickCountNext = 0;
} }
const S32 getElapsedMs() const S32 getElapsedMs()
{ {
if(mUsingPerfCounter) // Use QPC, update remainders so we don't leak time, and return the elapsed time.
{ QueryPerformanceCounter( (LARGE_INTEGER *) &mPerfCountNext);
// Use QPC, update remainders so we don't leak time, and return the elapsed time. F64 elapsedF64 = (1000.0 * F64(mPerfCountNext - mPerfCountCurrent) / F64(mFrequency));
QueryPerformanceCounter( (LARGE_INTEGER *) &mPerfCountNext); elapsedF64 += mPerfCountRemainderCurrent;
F64 elapsedF64 = (1000.0 * F64(mPerfCountNext - mPerfCountCurrent) / F64(mFrequency)); U32 elapsed = (U32)mFloor(elapsedF64);
elapsedF64 += mPerfCountRemainderCurrent; mPerfCountRemainderNext = elapsedF64 - F64(elapsed);
U32 elapsed = (U32)mFloor(elapsedF64);
mPerfCountRemainderNext = elapsedF64 - F64(elapsed);
return elapsed; return elapsed;
}
else
{
// Do something naive with GTC.
mTickCountNext = GetTickCount();
return mTickCountNext - mTickCountCurrent;
}
} }
void reset() void reset()
{ {
// Do some simple copying to reset the timer to 0. // Do some simple copying to reset the timer to 0.
mTickCountCurrent = mTickCountNext;
mPerfCountCurrent = mPerfCountNext; mPerfCountCurrent = mPerfCountNext;
mPerfCountRemainderCurrent = mPerfCountRemainderNext; mPerfCountRemainderCurrent = mPerfCountRemainderNext;
} }