Skip to content

Commit fa2074e

Browse files
committed
Use CLOCK_MONOTONIC_RAW over CLOCK_MONOTONIC on Linux (fixes non monotonic clock)
There is no bug in Linux, the issue that CLOCK_MONOTONIC returns values less then previous calls likely happens due to adjtime(3) (NTP), since CLOCK_MONOTONIC is affected by it, and I've seen lots of slight time modifications due to NTP on the servers. And even on my desktop (I also have NTP enabled): CLOCK_MONOTONIC: 189292.803 (2 days + 4h 34m 52s) CLOCK_MONOTONIC_RAW: 189290.016 (2 days + 4h 34m 50s) However on Linux there is CLOCK_MONOTONIC_RAW, it is similar to CLOCK_MONOTONIC, but does not affected by the adjtime(3). About performance, it is the same: CLOCK_MONOTONIC 10e6: real=0m0.191s user=0m0.190s sys=0m0.000s CLOCK_MONOTONIC_RAW 10e6: real=0m0.191s user=0m0.191s sys=0m0.000s Ops/s: - AMD Threadripper: 52.3e6 - Xeon Silver 4216 2.10: 46.5e6 Fixes: c5d631c Fixes: #29811 (cc @tavplubix) Signed-off-by: Azat Khuzhin <a.khuzhin@semrush.com>
1 parent fc993f4 commit fa2074e

1 file changed

Lines changed: 23 additions & 8 deletions

File tree

src/Common/Stopwatch.h

Lines changed: 23 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -8,19 +8,34 @@
88
#include <atomic>
99
#include <memory>
1010

11-
12-
inline UInt64 clock_gettime_ns(clockid_t clock_type = CLOCK_MONOTONIC)
11+
/// From clock_getres(2):
12+
///
13+
/// Similar to CLOCK_MONOTONIC, but provides access to a raw hardware-based
14+
/// time that is not subject to NTP adjustments or the incremental
15+
/// adjustments performed by adjtime(3).
16+
#ifdef CLOCK_MONOTONIC_RAW
17+
static constexpr clockid_t STOPWATCH_DEFAULT_CLOCK = CLOCK_MONOTONIC_RAW;
18+
#else
19+
static constexpr clockid_t STOPWATCH_DEFAULT_CLOCK = CLOCK_MONOTONIC;
20+
#endif
21+
22+
inline UInt64 clock_gettime_ns(clockid_t clock_type = STOPWATCH_DEFAULT_CLOCK)
1323
{
1424
struct timespec ts;
1525
clock_gettime(clock_type, &ts);
1626
return UInt64(ts.tv_sec * 1000000000LL + ts.tv_nsec);
1727
}
1828

19-
/// Sometimes monotonic clock may not be monotonic (due to bug in kernel?).
20-
/// It may cause some operations to fail with "Timeout exceeded: elapsed 18446744073.709553 seconds".
2129
/// Takes previously returned value and returns it again if time stepped back for some reason.
22-
inline UInt64 clock_gettime_ns_adjusted(UInt64 prev_time, clockid_t clock_type = CLOCK_MONOTONIC)
30+
///
31+
/// You should use this if OS does not support CLOCK_MONOTONIC_RAW
32+
inline UInt64 clock_gettime_ns_adjusted(UInt64 prev_time, clockid_t clock_type = STOPWATCH_DEFAULT_CLOCK)
2333
{
34+
#ifdef CLOCK_MONOTONIC_RAW
35+
if (likely(clock_type == CLOCK_MONOTONIC_RAW))
36+
return clock_gettime_ns(clock_type);
37+
#endif
38+
2439
UInt64 current_time = clock_gettime_ns(clock_type);
2540
if (likely(prev_time <= current_time))
2641
return current_time;
@@ -36,10 +51,10 @@ inline UInt64 clock_gettime_ns_adjusted(UInt64 prev_time, clockid_t clock_type =
3651
class Stopwatch
3752
{
3853
public:
39-
/** CLOCK_MONOTONIC works relatively efficient (~15 million calls/sec) and doesn't lead to syscall.
54+
/** CLOCK_MONOTONIC/CLOCK_MONOTONIC_RAW works relatively efficient (~40-50 million calls/sec) and doesn't lead to syscall.
4055
* Pass CLOCK_MONOTONIC_COARSE, if you need better performance with acceptable cost of several milliseconds of inaccuracy.
4156
*/
42-
explicit Stopwatch(clockid_t clock_type_ = CLOCK_MONOTONIC) : clock_type(clock_type_) { start(); }
57+
explicit Stopwatch(clockid_t clock_type_ = STOPWATCH_DEFAULT_CLOCK) : clock_type(clock_type_) { start(); }
4358
explicit Stopwatch(clockid_t clock_type_, UInt64 start_nanoseconds, bool is_running_)
4459
: start_ns(start_nanoseconds), clock_type(clock_type_), is_running(is_running_)
4560
{
@@ -75,7 +90,7 @@ using StopwatchUniquePtr = std::unique_ptr<Stopwatch>;
7590
class AtomicStopwatch
7691
{
7792
public:
78-
explicit AtomicStopwatch(clockid_t clock_type_ = CLOCK_MONOTONIC) : clock_type(clock_type_) { restart(); }
93+
explicit AtomicStopwatch(clockid_t clock_type_ = STOPWATCH_DEFAULT_CLOCK) : clock_type(clock_type_) { restart(); }
7994

8095
void restart() { start_ns = nanoseconds(0); }
8196
UInt64 elapsed() const

0 commit comments

Comments
 (0)