-
Notifications
You must be signed in to change notification settings - Fork 4.1k
Expand file tree
/
Copy pathtime.h
More file actions
422 lines (359 loc) · 13.2 KB
/
Copy pathtime.h
File metadata and controls
422 lines (359 loc) · 13.2 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements. See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership. The ASF licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with the License. You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the License for the
// specific language governing permissions and limitations
// under the License.
// Date: Wed Aug 11 10:38:17 2010
// Measuring time
#ifndef BUTIL_BAIDU_TIME_H
#define BUTIL_BAIDU_TIME_H
#include <time.h> // timespec, clock_gettime
#include <sys/time.h> // timeval, gettimeofday
#include <stdint.h> // int64_t, uint64_t
#if defined(NO_CLOCK_GETTIME_IN_MAC)
#include <mach/mach.h>
# define CLOCK_REALTIME CALENDAR_CLOCK
# define CLOCK_MONOTONIC SYSTEM_CLOCK
typedef int clockid_t;
// clock_gettime is not available in MacOS < 10.12
int clock_gettime(clockid_t id, timespec* time);
#endif
namespace butil {
// Get SVN revision of this copy.
const char* last_changed_revision();
// ----------------------
// timespec manipulations
// ----------------------
// Let tm->tv_nsec be in [0, 1,000,000,000) if it's not.
inline void timespec_normalize(timespec* tm) {
if (tm->tv_nsec >= 1000000000L) {
const int64_t added_sec = tm->tv_nsec / 1000000000L;
tm->tv_sec += added_sec;
tm->tv_nsec -= added_sec * 1000000000L;
} else if (tm->tv_nsec < 0) {
const int64_t sub_sec = (tm->tv_nsec - 999999999L) / 1000000000L;
tm->tv_sec += sub_sec;
tm->tv_nsec -= sub_sec * 1000000000L;
}
}
// Add timespec |span| into timespec |*tm|.
inline void timespec_add(timespec *tm, const timespec& span) {
tm->tv_sec += span.tv_sec;
tm->tv_nsec += span.tv_nsec;
timespec_normalize(tm);
}
// Minus timespec |span| from timespec |*tm|.
// tm->tv_nsec will be inside [0, 1,000,000,000)
inline void timespec_minus(timespec *tm, const timespec& span) {
tm->tv_sec -= span.tv_sec;
tm->tv_nsec -= span.tv_nsec;
timespec_normalize(tm);
}
// ------------------------------------------------------------------
// Get the timespec after specified duration from |start_time|
// ------------------------------------------------------------------
inline timespec nanoseconds_from(timespec start_time, int64_t nanoseconds) {
start_time.tv_nsec += nanoseconds;
timespec_normalize(&start_time);
return start_time;
}
inline timespec microseconds_from(timespec start_time, int64_t microseconds) {
return nanoseconds_from(start_time, microseconds * 1000L);
}
inline timespec milliseconds_from(timespec start_time, int64_t milliseconds) {
return nanoseconds_from(start_time, milliseconds * 1000000L);
}
inline timespec seconds_from(timespec start_time, int64_t seconds) {
return nanoseconds_from(start_time, seconds * 1000000000L);
}
// --------------------------------------------------------------------
// Get the timespec after specified duration from now (CLOCK_REALTIME)
// --------------------------------------------------------------------
inline timespec nanoseconds_from_now(int64_t nanoseconds) {
timespec time;
clock_gettime(CLOCK_REALTIME, &time);
return nanoseconds_from(time, nanoseconds);
}
inline timespec microseconds_from_now(int64_t microseconds) {
return nanoseconds_from_now(microseconds * 1000L);
}
inline timespec milliseconds_from_now(int64_t milliseconds) {
return nanoseconds_from_now(milliseconds * 1000000L);
}
inline timespec seconds_from_now(int64_t seconds) {
return nanoseconds_from_now(seconds * 1000000000L);
}
inline timespec timespec_from_now(const timespec& span) {
timespec time;
clock_gettime(CLOCK_REALTIME, &time);
timespec_add(&time, span);
return time;
}
// ---------------------------------------------------------------------
// Convert timespec to and from a single integer.
// For conversions between timespec and timeval, use TIMEVAL_TO_TIMESPEC
// and TIMESPEC_TO_TIMEVAL defined in <sys/time.h>
// ---------------------------------------------------------------------1
inline int64_t timespec_to_nanoseconds(const timespec& ts) {
return ts.tv_sec * 1000000000L + ts.tv_nsec;
}
inline int64_t timespec_to_microseconds(const timespec& ts) {
return timespec_to_nanoseconds(ts) / 1000L;
}
inline int64_t timespec_to_milliseconds(const timespec& ts) {
return timespec_to_nanoseconds(ts) / 1000000L;
}
inline int64_t timespec_to_seconds(const timespec& ts) {
return timespec_to_nanoseconds(ts) / 1000000000L;
}
inline timespec nanoseconds_to_timespec(int64_t ns) {
timespec ts;
ts.tv_sec = ns / 1000000000L;
ts.tv_nsec = ns - ts.tv_sec * 1000000000L;
return ts;
}
inline timespec microseconds_to_timespec(int64_t us) {
return nanoseconds_to_timespec(us * 1000L);
}
inline timespec milliseconds_to_timespec(int64_t ms) {
return nanoseconds_to_timespec(ms * 1000000L);
}
inline timespec seconds_to_timespec(int64_t s) {
return nanoseconds_to_timespec(s * 1000000000L);
}
// ---------------------------------------------------------------------
// Convert timeval to and from a single integer.
// For conversions between timespec and timeval, use TIMEVAL_TO_TIMESPEC
// and TIMESPEC_TO_TIMEVAL defined in <sys/time.h>
// ---------------------------------------------------------------------
inline int64_t timeval_to_microseconds(const timeval& tv) {
return tv.tv_sec * 1000000L + tv.tv_usec;
}
inline int64_t timeval_to_milliseconds(const timeval& tv) {
return timeval_to_microseconds(tv) / 1000L;
}
inline int64_t timeval_to_seconds(const timeval& tv) {
return timeval_to_microseconds(tv) / 1000000L;
}
inline timeval microseconds_to_timeval(int64_t us) {
timeval tv;
tv.tv_sec = us / 1000000L;
tv.tv_usec = us - tv.tv_sec * 1000000L;
return tv;
}
inline timeval milliseconds_to_timeval(int64_t ms) {
return microseconds_to_timeval(ms * 1000L);
}
inline timeval seconds_to_timeval(int64_t s) {
return microseconds_to_timeval(s * 1000000L);
}
// ---------------------------------------------------------------
// Get system-wide monotonic time.
// ---------------------------------------------------------------
extern int64_t monotonic_time_ns();
inline int64_t monotonic_time_us() {
return monotonic_time_ns() / 1000L;
}
inline int64_t monotonic_time_ms() {
return monotonic_time_ns() / 1000000L;
}
inline int64_t monotonic_time_s() {
return monotonic_time_ns() / 1000000000L;
}
namespace detail {
inline uint64_t clock_cycles() {
#if defined(__x86_64__) || defined(__amd64__)
unsigned int lo = 0;
unsigned int hi = 0;
// We cannot use "=A", since this would use %rax on x86_64
__asm__ __volatile__ (
"rdtsc"
: "=a" (lo), "=d" (hi)
);
return ((uint64_t)hi << 32) | lo;
#elif defined(__aarch64__)
uint64_t virtual_timer_value;
asm volatile("mrs %0, cntvct_el0" : "=r"(virtual_timer_value));
return virtual_timer_value;
#elif defined(__ARM_ARCH)
#if (__ARM_ARCH >= 6)
unsigned int pmccntr;
unsigned int pmuseren;
unsigned int pmcntenset;
// Read the user mode perf monitor counter access permissions.
asm volatile ("mrc p15, 0, %0, c9, c14, 0" : "=r" (pmuseren));
if (pmuseren & 1) { // Allows reading perfmon counters for user mode code.
asm volatile ("mrc p15, 0, %0, c9, c12, 1" : "=r" (pmcntenset));
if (pmcntenset & 0x80000000ul) { // Is it counting?
asm volatile ("mrc p15, 0, %0, c9, c13, 0" : "=r" (pmccntr));
// The counter is set up to count every 64th cycle
return static_cast<uint64_t>(pmccntr) * 64; // Should optimize to << 6
}
}
#else
#error "unsupported arm_arch"
#endif
#elif defined(__loongarch64)
uint64_t stable_counter;
uint64_t counter_id;
__asm__ __volatile__ (
"rdtime.d %1, %0"
: "=r" (stable_counter), "=r" (counter_id)
);
return stable_counter;
#elif defined(__riscv)
uint64_t cycles;
__asm__ __volatile__ (
"rdcycle %0"
: "=r" (cycles)
);
return cycles;
#else
#error "unsupported arch"
#endif
}
extern int64_t read_invariant_cpu_frequency();
// Be positive iff:
// 1 Intel x86_64 CPU (multiple cores) supporting constant_tsc and
// nonstop_tsc(check flags in /proc/cpuinfo)
extern int64_t invariant_cpu_freq;
} // namespace detail
// ---------------------------------------------------------------
// Get cpu-wide (wall-) time.
// Cost ~9ns on Intel(R) Xeon(R) CPU E5620 @ 2.40GHz
// ---------------------------------------------------------------
// note: Inlining shortens time cost per-call for 15ns in a loop of many
// calls to this function.
inline int64_t cpuwide_time_ns() {
#if !defined(BAIDU_INTERNAL) && !defined(__aarch64__)
// nearly impossible to get the correct invariant cpu frequency on
// different CPU and machines. CPU-ID rarely works and frequencies
// in "model name" and "cpu Mhz" are both unreliable.
// Since clock_gettime() in newer glibc/kernel is much faster(~30ns)
// which is closer to the previous impl. of cpuwide_time(~10ns), we
// simply use the monotonic time to get rid of all related issues.
timespec now;
clock_gettime(CLOCK_MONOTONIC, &now);
return now.tv_sec * 1000000000L + now.tv_nsec;
#else
int64_t cpu_freq = detail::invariant_cpu_freq;
if (cpu_freq > 0) {
const uint64_t tsc = detail::clock_cycles();
//Try to avoid overflow
const uint64_t sec = tsc / cpu_freq;
const uint64_t remain = tsc % cpu_freq;
// TODO: should be OK until CPU's frequency exceeds 16GHz.
return remain * 1000000000L / cpu_freq + sec * 1000000000L;
} else if (!cpu_freq) {
// Lack of necessary features, return system-wide monotonic time instead.
return monotonic_time_ns();
}
#endif // defined(BAIDU_INTERNAL)
}
// Get cpu clock time of the current thread in nanoseconds without the time spent in blocking I/O operations.
// Cost ~200ns
inline int64_t cputhread_time_ns() {
timespec now;
clock_gettime(CLOCK_THREAD_CPUTIME_ID, &now);
return now.tv_sec * 1000000000L + now.tv_nsec;
}
inline int64_t cpuwide_time_us() {
return cpuwide_time_ns() / 1000L;
}
inline int64_t cpuwide_time_ms() {
return cpuwide_time_ns() / 1000000L;
}
inline int64_t cpuwide_time_s() {
return cpuwide_time_ns() / 1000000000L;
}
// --------------------------------------------------------------------
// Get elapse since the Epoch.
// No gettimeofday_ns() because resolution of timeval is microseconds.
// Cost ~40ns on 2.6.32_1-12-0-0, Intel(R) Xeon(R) CPU E5620 @ 2.40GHz
// --------------------------------------------------------------------
inline int64_t gettimeofday_us() {
timeval now;
gettimeofday(&now, NULL);
return now.tv_sec * 1000000L + now.tv_usec;
}
inline int64_t gettimeofday_ms() {
return gettimeofday_us() / 1000L;
}
inline int64_t gettimeofday_s() {
return gettimeofday_us() / 1000000L;
}
// ----------------------------------------
// Control frequency of operations.
// ----------------------------------------
// Example:
// EveryManyUS every_1s(1000000L);
// while (1) {
// ...
// if (every_1s) {
// // be here at most once per second
// }
// }
class EveryManyUS {
public:
explicit EveryManyUS(int64_t interval_us)
: _last_time_us(cpuwide_time_us())
, _interval_us(interval_us) {}
operator bool() {
const int64_t now_us = cpuwide_time_us();
if (now_us < _last_time_us + _interval_us) {
return false;
}
_last_time_us = now_us;
return true;
}
private:
int64_t _last_time_us;
const int64_t _interval_us;
};
// ---------------
// Count elapses
// ---------------
class Timer {
public:
enum TimerType {
STARTED,
};
Timer() : _stop(0), _start(0) {}
explicit Timer(const TimerType) : Timer() {
start();
}
// Start this timer
void start() {
_start = cpuwide_time_ns();
_stop = _start;
}
// Stop this timer
void stop() {
_stop = cpuwide_time_ns();
}
// Get the elapse from start() to stop(), in various units.
int64_t n_elapsed() const { return _stop - _start; }
int64_t u_elapsed() const { return n_elapsed() / 1000L; }
int64_t m_elapsed() const { return u_elapsed() / 1000L; }
int64_t s_elapsed() const { return m_elapsed() / 1000L; }
double n_elapsed(double) const { return (double)(_stop - _start); }
double u_elapsed(double) const { return (double)n_elapsed() / 1000.0; }
double m_elapsed(double) const { return (double)u_elapsed() / 1000.0; }
double s_elapsed(double) const { return (double)m_elapsed() / 1000.0; }
private:
int64_t _stop;
int64_t _start;
};
} // namespace butil
#endif // BUTIL_BAIDU_TIME_H