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"
35 #include <sstream> // std::stringstream
37 #include "lib/module_init.h"
38 #include "lib/posix/posix_time.h"
39 #include "lib/sysdep/cpu.h"
42 #include "lib/sysdep/os/win/win.h"
49 # define HAVE_GETTIMEOFDAY 1
51 # define HAVE_GETTIMEOFDAY 0
54 #if (defined(_POSIX_TIMERS) && _POSIX_TIMERS > 0)
55 # define HAVE_CLOCK_GETTIME 1
57 # define HAVE_CLOCK_GETTIME 0
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.
66 static LARGE_INTEGER start
;
67 #elif HAVE_CLOCK_GETTIME
68 static struct timespec start
;
69 #elif HAVE_GETTIMEOFDAY
70 static struct timeval start
;
74 //-----------------------------------------------------------------------------
78 // Cached because the default implementation may take several milliseconds.
79 static double resolution
;
80 static Status
InitResolution()
83 LARGE_INTEGER frequency
;
84 ENSURE(QueryPerformanceFrequency(&frequency
));
85 resolution
= 1.0 / static_cast<double>(frequency
.QuadPart
);
86 #elif HAVE_CLOCK_GETTIME
88 if (clock_getres(CLOCK_MONOTONIC
, &ts
) == 0)
89 resolution
= ts
.tv_nsec
* 1e-9;
92 #elif HAVE_GETTIMEOFDAY
95 const double t0
= timer_Time();
97 do t1
= timer_Time(); while (t1
== t0
);
98 do t2
= timer_Time(); while (t2
== t1
);
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);
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
);
135 ENSURE(start
.QuadPart
); // must have called timer_LatchStartTime first
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
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
147 ENSURE(gettimeofday(&cur
, 0) == 0);
148 t
= (cur
.tv_sec
- start
.tv_sec
) + (cur
.tv_usec
- start
.tv_usec
) * resolution
;
150 # error "timer_Time: add timer implementation for this platform!"
158 double timer_Resolution()
164 //-----------------------------------------------------------------------------
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
)
181 tc
->description
= description
;
183 // insert at front of list
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
)
212 const char* unit
= " us";
214 scale
= 1, unit
= " s";
215 else if(seconds
> 1e-3)
216 scale
= 1e3
, unit
= " ms";
218 std::stringstream ss
;
225 std::string
StringForCycles(Cycles cycles
)
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
;