CppCommon 1.0.5.0
C++ Common Library
Loading...
Searching...
No Matches
timestamp.cpp
Go to the documentation of this file.
1
9#include "time/timestamp.h"
10
11#include "math/math.h"
12
13#if defined(__APPLE__)
14#include <mach/mach.h>
15#include <mach/mach_time.h>
16#include <sys/time.h>
17#include <math.h>
18#include <time.h>
19#elif defined(unix) || defined(__unix) || defined(__unix__)
20#include <time.h>
21#elif defined(_WIN32) || defined(_WIN64)
22#include <windows.h>
23#endif
24
25namespace CppCommon {
26
28namespace Internals {
29
30#if defined(__APPLE__)
31
32uint32_t CeilLog2(uint32_t x)
33{
34 uint32_t result = 0;
35
36 --x;
37 while (x > 0)
38 {
39 ++result;
40 x >>= 1;
41 }
42
43 return result;
44}
45
46// This function returns the rational number inside the given interval with
47// the smallest denominator (and smallest numerator breaks ties; correctness
48// proof neglects floating-point errors).
49mach_timebase_info_data_t BestFrac(double a, double b)
50{
51 if (floor(a) < floor(b))
52 {
53 mach_timebase_info_data_t rv = { (uint32_t)ceil(a), 1 };
54 return rv;
55 }
56
57 double m = floor(a);
58 mach_timebase_info_data_t next = BestFrac(1 / (b - m), 1 / (a - m));
59 mach_timebase_info_data_t rv = { (int)m * next.numer + next.denom, next.numer };
60 return rv;
61}
62
63// This is just less than the smallest thing we can multiply numer by without
64// overflowing. CeilLog2(numer) = 64 - number of leading zeros of numer
65uint64_t GetExpressibleSpan(uint32_t numer, uint32_t denom)
66{
67 uint64_t maxDiffWithoutOverflow = ((uint64_t)1 << (64 - CeilLog2(numer))) - 1;
68 return maxDiffWithoutOverflow * numer / denom;
69}
70
71// The clock may run up to 0.1% faster or slower than the "exact" tick count.
72// However, although the bound on the error is the same as for the pragmatic
73// answer, the error is actually minimized over the given accuracy bound.
74uint64_t PrepareTimebaseInfo(mach_timebase_info_data_t& tb)
75{
76 tb.numer = 0;
77 tb.denom = 1;
78
79 kern_return_t mtiStatus = mach_timebase_info(&tb);
80 if (mtiStatus != KERN_SUCCESS)
81 return 0;
82
83 double frac = (double)tb.numer / tb.denom;
84 uint64_t spanTarget = 315360000000000000llu;
85 if (GetExpressibleSpan(tb.numer, tb.denom) >= spanTarget)
86 return 0;
87
88 for (double errorTarget = 1 / 1024.0; errorTarget > 0.000001;)
89 {
90 mach_timebase_info_data_t newFrac = BestFrac((1 - errorTarget) * frac, (1 + errorTarget) * frac);
91 if (GetExpressibleSpan(newFrac.numer, newFrac.denom) < spanTarget)
92 break;
93 tb = newFrac;
94 errorTarget = fabs((double)tb.numer / tb.denom - frac) / frac / 8;
95 }
96
97 return 0;
98}
99
100#endif
101
102} // namespace Internals
104
106{
107#if defined(unix) || defined(__unix) || defined(__unix__) || defined(__APPLE__)
108 struct timespec timestamp;
109 if (clock_gettime(CLOCK_REALTIME, &timestamp) != 0)
110 throwex SystemException("Cannot get value of CLOCK_REALTIME timer!");
111 return (timestamp.tv_sec * 1000000000) + timestamp.tv_nsec;
112#elif defined(_WIN32) || defined(_WIN64)
113 FILETIME ft;
114 GetSystemTimePreciseAsFileTime(&ft);
115
116 ULARGE_INTEGER result;
117 result.LowPart = ft.dwLowDateTime;
118 result.HighPart = ft.dwHighDateTime;
119 return (result.QuadPart - 116444736000000000ull) * 100;
120#endif
121}
122
124{
125#if defined(unix) || defined(__unix) || defined(__unix__) || defined(__APPLE__)
126 uint64_t timestamp = utc();
127
128 // Adjust UTC time with local timezone offset
129 struct tm local;
130 time_t seconds = timestamp / (1000000000);
131 if (localtime_r(&seconds, &local) != &local)
132 throwex SystemException("Cannot convert CLOCK_REALTIME time to local date & time structure!");
133 return timestamp + (local.tm_gmtoff * 1000000000);
134#elif defined(_WIN32) || defined(_WIN64)
135 FILETIME ft;
136#if (_WIN32_WINNT >= _WIN32_WINNT_WIN8)
137 GetSystemTimePreciseAsFileTime(&ft);
138#else
139 GetSystemTimeAsFileTime(&ft);
140#endif
141
142 FILETIME ft_local;
143 if (!FileTimeToLocalFileTime(&ft, &ft_local))
144 throwex SystemException("Cannot convert UTC file time to local file time structure!");
145
146 ULARGE_INTEGER result;
147 result.LowPart = ft_local.dwLowDateTime;
148 result.HighPart = ft_local.dwHighDateTime;
149 return (result.QuadPart - 116444736000000000ull) * 100;
150#endif
151}
152
154{
155#if defined(__APPLE__)
156 static mach_timebase_info_data_t info;
157 static uint64_t bias = Internals::PrepareTimebaseInfo(info);
158 return ((mach_absolute_time() - bias) * info.numer) / info.denom;
159#elif defined(unix) || defined(__unix) || defined(__unix__)
160 struct timespec timestamp = { 0 };
161 if (clock_gettime(CLOCK_MONOTONIC, &timestamp) != 0)
162 throwex SystemException("Cannot get value of CLOCK_MONOTONIC timer!");
163 return (timestamp.tv_sec * 1000000000) + timestamp.tv_nsec;
164#elif defined(_WIN32) || defined(_WIN64)
165 static uint64_t offset = 0;
166 static LARGE_INTEGER first = { 0 };
167 static LARGE_INTEGER frequency = { 0 };
168 static bool initialized = false;
169 static bool qpc = true;
170
171 if (!initialized)
172 {
173 // Calculate timestamp offset
174 FILETIME timestamp;
175 GetSystemTimePreciseAsFileTime(&timestamp);
176
177 ULARGE_INTEGER result;
178 result.LowPart = timestamp.dwLowDateTime;
179 result.HighPart = timestamp.dwHighDateTime;
180
181 // Convert 01.01.1601 to 01.01.1970
182 result.QuadPart -= 116444736000000000ll;
183 offset = result.QuadPart * 100;
184
185 // Setup performance counter
186 qpc = QueryPerformanceFrequency(&frequency) && QueryPerformanceCounter(&first);
187
188 initialized = true;
189 }
190
191 if (qpc)
192 {
193 LARGE_INTEGER timestamp = { 0 };
194 QueryPerformanceCounter(&timestamp);
195 timestamp.QuadPart -= first.QuadPart;
196 return offset + Math::MulDiv64(timestamp.QuadPart, 1000000000, frequency.QuadPart);
197 }
198 else
199 return offset;
200#else
201 #error Unsupported platform
202#endif
203}
204
206{
207#if defined(__APPLE__)
208 // This goes at the top because we need ALL Macs, regardless of
209 // architecture, to return the number of "mach time units" that
210 // have passed since startup. See sysinfo.cc where
211 // InitializeSystemInfo() sets the supposed cpu clock frequency of
212 // macs to the number of mach time units per second, not actual
213 // CPU clock frequency (which can change in the face of CPU
214 // frequency scaling). Also note that when the Mac sleeps, this
215 // counter pauses; it does not continue counting, nor does it
216 // reset to zero.
217 return mach_absolute_time();
218#elif defined(_MSC_VER)
219 return __rdtsc();
220#elif defined(__i386__)
221 int64_t ret;
222 __asm__ volatile("rdtsc" : "=A"(ret));
223 return ret;
224#elif defined(__x86_64__) || defined(__amd64__)
225 uint64_t low, high;
226 __asm__ volatile("rdtsc" : "=a"(low), "=d"(high));
227 return (high << 32) | low;
228#elif defined(__powerpc__) || defined(__ppc__)
229 // This returns a time-base, which is not always precisely a cycle-count.
230 int64_t tbl, tbu0, tbu1;
231 asm("mftbu %0" : "=r"(tbu0));
232 asm("mftb %0" : "=r"(tbl));
233 asm("mftbu %0" : "=r"(tbu1));
234 tbl &= -static_cast<int64>(tbu0 == tbu1);
235 // High 32 bits in tbu1;
236 // How 32 bits in tbl (tbu0 is garbage)
237 return (tbu1 << 32) | tbl;
238#elif defined(__sparc__)
239 int64_t tick;
240 asm(".byte 0x83, 0x41, 0x00, 0x00");
241 asm("mov %%g1, %0" : "=r"(tick));
242 return tick;
243#elif defined(__ia64__)
244 int64_t itc;
245 asm("mov %0 = ar.itc" : "=r"(itc));
246 return itc;
247#elif defined(COMPILER_MSVC) && defined(_M_IX86)
248 // Older MSVC compilers (like 7.x) don't seem to support the
249 // __rdtsc intrinsic properly, so I prefer to use _asm instead
250 // when I know it will work. Otherwise, I'll use __rdtsc and hope
251 // the code is being compiled with a non-ancient compiler.
252 _asm rdtsc
253#elif defined(__aarch64__)
254 // System timer of ARMv8 runs at a different frequency than the CPU's.
255 // The frequency is fixed, typically in the range 1-50MHz. It can be
256 // read at CNTFRQ special register. We assume the OS has set up
257 // the virtual timer properly.
258 int64_t virtual_timer_value;
259 asm volatile("mrs %0, cntvct_el0" : "=r"(virtual_timer_value));
260 return virtual_timer_value;
261#elif defined(__ARM_ARCH)
262
263 // V6 is the earliest arch that has a standard cyclecount
264 #if (__ARM_ARCH >= 6)
265 uint32_t pmccntr;
266 uint32_t pmuseren;
267 uint32_t pmcntenset;
268 // Read the user mode perf monitor counter access permissions.
269 asm volatile("mrc p15, 0, %0, c9, c14, 0" : "=r"(pmuseren));
270 if (pmuseren & 1)
271 {
272 // Allows reading perfmon counters for user mode code.
273 asm volatile("mrc p15, 0, %0, c9, c12, 1" : "=r"(pmcntenset));
274 if (pmcntenset & 0x80000000ul)
275 {
276 // Is it counting?
277 asm volatile("mrc p15, 0, %0, c9, c13, 0" : "=r"(pmccntr));
278 // The counter is set up to count every 64th cycle
279 return static_cast<int64_t>(pmccntr) * 64;
280 }
281 }
282 #endif
283
284 struct timeval tv;
285 gettimeofday(&tv, nullptr);
286 return static_cast<int64_t>(tv.tv_sec) * 1000000 + tv.tv_usec;
287#elif defined(__mips__) || defined(__loongarch__) || defined(__riscv)
288 // mips apparently only allows rdtsc for superusers, so we fall back to gettimeofday.
289 // It's possible clock_gettime would be better.
290 struct timeval tv;
291 gettimeofday(&tv, nullptr);
292 return static_cast<int64_t>(tv.tv_sec) * 1000000 + tv.tv_usec;
293#else
294 #error Unsupported platform
295#endif
296}
297
298} // namespace CppCommon
static uint64_t MulDiv64(uint64_t operant, uint64_t multiplier, uint64_t divider)
Calculate (operant * multiplier / divider) with 64-bit unsigned integer values.
Definition math.cpp:17
System exception.
Definition exceptions.h:107
static uint64_t local()
Get the local timestamp.
static uint64_t utc()
Get the UTC timestamp.
static uint64_t nano()
Get the high resolution timestamp.
uint64_t seconds() const noexcept
Get total seconds of the current timestamp.
Definition timestamp.h:143
static uint64_t rdts()
Get the current value of RDTS (Read Time Stamp Counter)
#define throwex
Throw extended exception macro.
Definition exceptions.h:23
Math definition.
C++ Common project definitions.
Time definition.
Timestamp definition.