Index: ps/trunk/source/lib/timer.cpp =================================================================== --- ps/trunk/source/lib/timer.cpp (revision 24506) +++ ps/trunk/source/lib/timer.cpp (revision 24507) @@ -1,240 +1,240 @@ -/* Copyright (C) 2020 Wildfire Games. +/* Copyright (C) 2021 Wildfire Games. * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to * the following conditions: * * The above copyright notice and this permission notice shall be included * in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /* * platform-independent high resolution timer */ #include "precompiled.h" #include "lib/timer.h" #include #include #include #include #include #include // std::stringstream #include "lib/module_init.h" #include "lib/posix/posix_time.h" #include "lib/sysdep/cpu.h" #if OS_WIN #include "lib/sysdep/os/win/win.h" #endif #if OS_UNIX # include #endif #if OS_UNIX || OS_WIN # define HAVE_GETTIMEOFDAY 1 #else # define HAVE_GETTIMEOFDAY 0 #endif #if (defined(_POSIX_TIMERS) && _POSIX_TIMERS > 0) # define HAVE_CLOCK_GETTIME 1 #else # define HAVE_CLOCK_GETTIME 0 #endif // rationale for wrapping gettimeofday and clock_gettime, instead of just // emulating them where not available: allows returning higher-resolution // timer values than their us / ns interface, via double [seconds]. // they're also not guaranteed to be monotonic. #if OS_WIN static LARGE_INTEGER start; #elif HAVE_CLOCK_GETTIME static struct timespec start; #elif HAVE_GETTIMEOFDAY static struct timeval start; #endif //----------------------------------------------------------------------------- // timer API // Cached because the default implementation may take several milliseconds. static double resolution; static Status InitResolution() { #if OS_WIN LARGE_INTEGER frequency; ENSURE(QueryPerformanceFrequency(&frequency)); resolution = 1.0 / static_cast(frequency.QuadPart); #elif HAVE_CLOCK_GETTIME struct timespec ts; - if (clock_getres(CLOCK_REALTIME, &ts) == 0) + if (clock_getres(CLOCK_MONOTONIC, &ts) == 0) resolution = ts.tv_nsec * 1e-9; else resolution = 1e-9; #elif HAVE_GETTIMEOFDAY resolution = 1e-6; #else const double t0 = timer_Time(); double t1, t2; do t1 = timer_Time(); while (t1 == t0); do t2 = timer_Time(); while (t2 == t1); resolution = t2 - t1; #endif return INFO::OK; } void timer_Init() { #if OS_WIN ENSURE(QueryPerformanceCounter(&start)); #elif HAVE_CLOCK_GETTIME - (void)clock_gettime(CLOCK_REALTIME, &start); + ENSURE(clock_gettime(CLOCK_MONOTONIC, &start) == 0); #elif HAVE_GETTIMEOFDAY - gettimeofday(&start, 0); + ENSURE(gettimeofday(&start, 0) == 0); #endif static ModuleInitState initState; ModuleInit(&initState, InitResolution); ENSURE(resolution != 0.0); } static std::mutex ensure_monotonic_mutex; // NB: does not guarantee strict monotonicity - callers must avoid // dividing by the difference of two equal times. static void EnsureMonotonic(double& newTime) { std::lock_guard lock(ensure_monotonic_mutex); static double maxTime; maxTime = std::max(maxTime, newTime); newTime = maxTime; } double timer_Time() { double t; #if OS_WIN ENSURE(start.QuadPart); // must have called timer_LatchStartTime first LARGE_INTEGER now; ENSURE(QueryPerformanceCounter(&now)); t = static_cast(now.QuadPart - start.QuadPart) * resolution; #elif HAVE_CLOCK_GETTIME ENSURE(start.tv_sec || start.tv_nsec); // must have called timer_LatchStartTime first struct timespec cur; - (void)clock_gettime(CLOCK_REALTIME, &cur); + ENSURE(clock_gettime(CLOCK_MONOTONIC, &cur) == 0); t = (cur.tv_sec - start.tv_sec) + (cur.tv_nsec - start.tv_nsec) * resolution; #elif HAVE_GETTIMEOFDAY ENSURE(start.tv_sec || start.tv_usec); // must have called timer_LatchStartTime first struct timeval cur; - gettimeofday(&cur, 0); + ENSURE(gettimeofday(&cur, 0) == 0); t = (cur.tv_sec - start.tv_sec) + (cur.tv_usec - start.tv_usec) * resolution; #else # error "timer_Time: add timer implementation for this platform!" #endif EnsureMonotonic(t); return t; } double timer_Resolution() { return resolution; } //----------------------------------------------------------------------------- // client API // intrusive linked-list of all clients. a fixed-size limit would be // acceptable (since timers are added manually), but the list is easy // to implement and only has the drawback of exposing TimerClient to users. // // do not use std::list et al. for this! we must be callable at any time, // especially before NLSO ctors run or before heap init. static size_t numClients; static TimerClient* clients; TimerClient* timer_AddClient(TimerClient* tc, const wchar_t* description) { tc->sum.SetToZero(); tc->description = description; // insert at front of list tc->next = clients; clients = tc; numClients++; return tc; } void timer_DisplayClientTotals() { debug_printf("TIMER TOTALS (%lu clients)\n", (unsigned long)numClients); debug_printf("-----------------------------------------------------\n"); for(TimerClient* tc = clients; tc; tc = tc->next) { const std::string duration = tc->sum.ToString(); debug_printf(" %s: %s (%lux)\n", utf8_from_wstring(tc->description).c_str(), duration.c_str(), (unsigned long)tc->num_calls); } debug_printf("-----------------------------------------------------\n"); } //----------------------------------------------------------------------------- std::string StringForSeconds(double seconds) { double scale = 1e6; const char* unit = " us"; if(seconds > 1.0) scale = 1, unit = " s"; else if(seconds > 1e-3) scale = 1e3, unit = " ms"; std::stringstream ss; ss << seconds*scale; ss << unit; return ss.str(); } std::string StringForCycles(Cycles cycles) { double scale = 1.0; const char* unit = " c"; if(cycles > 10000000000LL) // 10 Gc scale = 1e-9, unit = " Gc"; else if(cycles > 10000000) // 10 Mc scale = 1e-6, unit = " Mc"; else if(cycles > 10000) // 10 kc scale = 1e-3, unit = " kc"; std::stringstream ss; ss << cycles*scale; ss << unit; return ss.str(); }