Merge 'remotes/trunk'
[0ad.git] / source / lib / timer.cpp
blob8cc9fd6e95dfd2a931005d1703e9b0438013b603
1 /* Copyright (C) 2021 Wildfire Games.
3 * Permission is hereby granted, free of charge, to any person obtaining
4 * a copy of this software and associated documentation files (the
5 * "Software"), to deal in the Software without restriction, including
6 * without limitation the rights to use, copy, modify, merge, publish,
7 * distribute, sublicense, and/or sell copies of the Software, and to
8 * permit persons to whom the Software is furnished to do so, subject to
9 * the following conditions:
11 * The above copyright notice and this permission notice shall be included
12 * in all copies or substantial portions of the Software.
14 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
17 * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
18 * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
19 * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
20 * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
24 * platform-independent high resolution timer
27 #include "precompiled.h"
28 #include "lib/timer.h"
30 #include <cfloat>
31 #include <cmath>
32 #include <cstdarg>
33 #include <mutex>
34 #include <numeric>
35 #include <sstream> // std::stringstream
37 #include "lib/module_init.h"
38 #include "lib/posix/posix_time.h"
39 #include "lib/sysdep/cpu.h"
41 #if OS_WIN
42 #include "lib/sysdep/os/win/win.h"
43 #endif
44 #if OS_UNIX
45 # include <unistd.h>
46 #endif
48 #if OS_UNIX || OS_WIN
49 # define HAVE_GETTIMEOFDAY 1
50 #else
51 # define HAVE_GETTIMEOFDAY 0
52 #endif
54 #if (defined(_POSIX_TIMERS) && _POSIX_TIMERS > 0)
55 # define HAVE_CLOCK_GETTIME 1
56 #else
57 # define HAVE_CLOCK_GETTIME 0
58 #endif
60 // rationale for wrapping gettimeofday and clock_gettime, instead of just
61 // emulating them where not available: allows returning higher-resolution
62 // timer values than their us / ns interface, via double [seconds].
63 // they're also not guaranteed to be monotonic.
65 #if OS_WIN
66 static LARGE_INTEGER start;
67 #elif HAVE_CLOCK_GETTIME
68 static struct timespec start;
69 #elif HAVE_GETTIMEOFDAY
70 static struct timeval start;
71 #endif
74 //-----------------------------------------------------------------------------
75 // timer API
78 // Cached because the default implementation may take several milliseconds.
79 static double resolution;
80 static Status InitResolution()
82 #if OS_WIN
83 LARGE_INTEGER frequency;
84 ENSURE(QueryPerformanceFrequency(&frequency));
85 resolution = 1.0 / static_cast<double>(frequency.QuadPart);
86 #elif HAVE_CLOCK_GETTIME
87 struct timespec ts;
88 if (clock_getres(CLOCK_MONOTONIC, &ts) == 0)
89 resolution = ts.tv_nsec * 1e-9;
90 else
91 resolution = 1e-9;
92 #elif HAVE_GETTIMEOFDAY
93 resolution = 1e-6;
94 #else
95 const double t0 = timer_Time();
96 double t1, t2;
97 do t1 = timer_Time(); while (t1 == t0);
98 do t2 = timer_Time(); while (t2 == t1);
99 resolution = t2 - t1;
100 #endif
101 return INFO::OK;
104 void timer_Init()
106 #if OS_WIN
107 ENSURE(QueryPerformanceCounter(&start));
108 #elif HAVE_CLOCK_GETTIME
109 ENSURE(clock_gettime(CLOCK_MONOTONIC, &start) == 0);
110 #elif HAVE_GETTIMEOFDAY
111 ENSURE(gettimeofday(&start, 0) == 0);
112 #endif
114 static ModuleInitState initState;
115 ModuleInit(&initState, InitResolution);
116 ENSURE(resolution != 0.0);
119 static std::mutex ensure_monotonic_mutex;
120 // NB: does not guarantee strict monotonicity - callers must avoid
121 // dividing by the difference of two equal times.
122 static void EnsureMonotonic(double& newTime)
124 std::lock_guard<std::mutex> lock(ensure_monotonic_mutex);
125 static double maxTime;
126 maxTime = std::max(maxTime, newTime);
127 newTime = maxTime;
130 double timer_Time()
132 double t;
134 #if OS_WIN
135 ENSURE(start.QuadPart); // must have called timer_LatchStartTime first
136 LARGE_INTEGER now;
137 ENSURE(QueryPerformanceCounter(&now));
138 t = static_cast<double>(now.QuadPart - start.QuadPart) * resolution;
139 #elif HAVE_CLOCK_GETTIME
140 ENSURE(start.tv_sec || start.tv_nsec); // must have called timer_LatchStartTime first
141 struct timespec cur;
142 ENSURE(clock_gettime(CLOCK_MONOTONIC, &cur) == 0);
143 t = (cur.tv_sec - start.tv_sec) + (cur.tv_nsec - start.tv_nsec) * resolution;
144 #elif HAVE_GETTIMEOFDAY
145 ENSURE(start.tv_sec || start.tv_usec); // must have called timer_LatchStartTime first
146 struct timeval cur;
147 ENSURE(gettimeofday(&cur, 0) == 0);
148 t = (cur.tv_sec - start.tv_sec) + (cur.tv_usec - start.tv_usec) * resolution;
149 #else
150 # error "timer_Time: add timer implementation for this platform!"
151 #endif
153 EnsureMonotonic(t);
154 return t;
158 double timer_Resolution()
160 return resolution;
164 //-----------------------------------------------------------------------------
165 // client API
167 // intrusive linked-list of all clients. a fixed-size limit would be
168 // acceptable (since timers are added manually), but the list is easy
169 // to implement and only has the drawback of exposing TimerClient to users.
171 // do not use std::list et al. for this! we must be callable at any time,
172 // especially before NLSO ctors run or before heap init.
173 static size_t numClients;
174 static TimerClient* clients;
177 TimerClient* timer_AddClient(TimerClient* tc, const wchar_t* description)
179 tc->sum.SetToZero();
181 tc->description = description;
183 // insert at front of list
184 tc->next = clients;
185 clients = tc;
186 numClients++;
188 return tc;
192 void timer_DisplayClientTotals()
194 debug_printf("TIMER TOTALS (%lu clients)\n", (unsigned long)numClients);
195 debug_printf("-----------------------------------------------------\n");
197 for(TimerClient* tc = clients; tc; tc = tc->next)
199 const std::string duration = tc->sum.ToString();
200 debug_printf(" %s: %s (%lux)\n", utf8_from_wstring(tc->description).c_str(), duration.c_str(), (unsigned long)tc->num_calls);
203 debug_printf("-----------------------------------------------------\n");
207 //-----------------------------------------------------------------------------
209 std::string StringForSeconds(double seconds)
211 double scale = 1e6;
212 const char* unit = " us";
213 if(seconds > 1.0)
214 scale = 1, unit = " s";
215 else if(seconds > 1e-3)
216 scale = 1e3, unit = " ms";
218 std::stringstream ss;
219 ss << seconds*scale;
220 ss << unit;
221 return ss.str();
225 std::string StringForCycles(Cycles cycles)
227 double scale = 1.0;
228 const char* unit = " c";
229 if(cycles > 10000000000LL) // 10 Gc
230 scale = 1e-9, unit = " Gc";
231 else if(cycles > 10000000) // 10 Mc
232 scale = 1e-6, unit = " Mc";
233 else if(cycles > 10000) // 10 kc
234 scale = 1e-3, unit = " kc";
236 std::stringstream ss;
237 ss << cycles*scale;
238 ss << unit;
239 return ss.str();