FastMM documentation
FastMM C++ API 0.6.0
Public headers, tier 1 and tier 2
Loading...
Searching...
No Matches
time.hpp
Go to the documentation of this file.
1#pragma once
2// Time primitives. Timestamp is ns since the Unix epoch; Cycles is a raw TSC reading.
3//
4// TscClock converts rdtsc to wall-clock ns with a 32.32 fixed-point multiplier calibrated
5// against CLOCK_REALTIME, refreshed at run time through a Seqlocked<TscCalibration> without
6// letting time go backwards. Without an invariant TSC (constant_tsc and nonstop_tsc) wall time
7// comes from clock_gettime (detected once from /proc/cpuinfo, see src/core/time.cpp); with
8// constant_tsc alone, as on many cloud VMs, the TSC still stamps latency intervals. SimClock is the
9// deterministic replacement for backtests/replay: same interface, time only moves when told.
10#include "fastmm/core/config_macros.hpp"
11#include "fastmm/core/seqlock.hpp"
12
13#include <x86intrin.h>
14
15#include <compare>
16#include <cstdint>
17
18namespace fastmm {
19
20struct Duration {
21 std::int64_t ns = 0;
22 constexpr auto operator<=>(const Duration&) const noexcept = default;
23 friend constexpr Duration operator+(Duration a, Duration b) noexcept { return {a.ns + b.ns}; }
24 friend constexpr Duration operator-(Duration a, Duration b) noexcept { return {a.ns - b.ns}; }
25 friend constexpr Duration operator*(Duration a, std::int64_t k) noexcept { return {a.ns * k}; }
26 friend constexpr Duration operator/(Duration a, std::int64_t k) noexcept { return {a.ns / k}; }
27 [[nodiscard]] constexpr std::int64_t millis() const noexcept { return ns / 1'000'000; }
28 [[nodiscard]] constexpr std::int64_t micros() const noexcept { return ns / 1'000; }
29};
30[[nodiscard]] constexpr Duration nanoseconds(std::int64_t v) noexcept {
31 return {v};
33[[nodiscard]] constexpr Duration microseconds(std::int64_t v) noexcept {
34 return {v * 1'000};
36[[nodiscard]] constexpr Duration milliseconds(std::int64_t v) noexcept {
37 return {v * 1'000'000};
39[[nodiscard]] constexpr Duration seconds(std::int64_t v) noexcept {
40 return {v * 1'000'000'000};
42
43struct Timestamp {
44 std::int64_t ns = 0;
45 constexpr auto operator<=>(const Timestamp&) const noexcept = default;
46 friend constexpr Duration operator-(Timestamp a, Timestamp b) noexcept { return {a.ns - b.ns}; }
47 friend constexpr Timestamp operator+(Timestamp a, Duration d) noexcept { return {a.ns + d.ns}; }
48 friend constexpr Timestamp operator-(Timestamp a, Duration d) noexcept { return {a.ns - d.ns}; }
49 constexpr Timestamp& operator+=(Duration d) noexcept {
50 ns += d.ns;
51 return *this;
52 }
53 [[nodiscard]] constexpr bool valid() const noexcept { return ns != 0; }
54 [[nodiscard]] static constexpr Timestamp max() noexcept { return {INT64_MAX}; }
55};
57struct Cycles {
58 std::uint64_t v = 0;
59 constexpr auto operator<=>(const Cycles&) const noexcept = default;
60 friend constexpr std::uint64_t operator-(Cycles a, Cycles b) noexcept { return a.v - b.v; }
61};
63// Serialising read (rdtscp) is what latency hops want: it waits for prior loads to retire.
64FASTMM_FORCE_INLINE Cycles rdtscp() noexcept {
65 unsigned aux = 0;
66 return Cycles{__rdtscp(&aux)};
67}
68FASTMM_FORCE_INLINE Cycles rdtsc() noexcept {
69 return Cycles{__rdtsc()};
71
72// CLOCK_REALTIME via vDSO (~20 ns). Implemented in src/core/time.cpp.
73[[nodiscard]] Timestamp wall_now() noexcept;
74[[nodiscard]] Timestamp steady_now() noexcept;
75// What /proc/cpuinfo says about the TSC (cached after the first call):
76// Invariant constant_tsc + nonstop_tsc: wall time and intervals from the TSC.
77// Constant constant_tsc only (KVM guests such as Vultr's): fixed rate, but not promised
78// to count through deep C-states. Wall time from clock_gettime; the TSC still
79// times intervals, which are far shorter than any drift that could matter.
80// None neither: clock_gettime for everything.
81enum class TscKind : std::uint8_t { None, Constant, Invariant };
82[[nodiscard]] TscKind tsc_kind() noexcept;
83[[nodiscard]] inline bool has_invariant_tsc() noexcept {
86
87struct TscCalibration {
88 std::uint64_t tsc0 = 0; // TSC reading at the anchor
89 std::int64_t ns0 = 0; // wall time at the anchor
90 std::uint64_t ns_per_cycle_q32 = 0; // 32.32 fixed point; 0: no TSC rate
91 double ghz = 0.0; // diagnostics only
92 // Wall time from the TSC. False with a non-zero rate (TscKind::Constant): wall time from
93 // clock_gettime, TSC cycle intervals still convert with the rate.
94 bool use_tsc = false;
95 [[nodiscard]] constexpr bool has_rate() const noexcept { return ns_per_cycle_q32 != 0; }
96};
98// A TSC cycle interval in ns; 0 without a rate.
99[[nodiscard]] FASTMM_FORCE_INLINE std::uint64_t tsc_interval_ns(const TscCalibration& c,
100 std::uint64_t cycles) noexcept {
101 return static_cast<std::uint64_t>((static_cast<Uint128>(cycles) * c.ns_per_cycle_q32) >> 32);
102}
103
104// Clock readings used by TscCalibrator. Plain function pointers so tests can inject a
105// deterministic clock; system_clock_readings() reads rdtsc, CLOCK_REALTIME and CLOCK_MONOTONIC_RAW.
106struct ClockReadings {
107 std::uint64_t (*tsc)() noexcept = nullptr;
108 std::int64_t (*realtime_ns)() noexcept = nullptr;
109 std::int64_t (*monotonic_raw_ns)() noexcept = nullptr;
110 bool check_invariant_tsc = true; // false for injected clocks
111 TscKind assumed_kind = TscKind::Invariant; // injected clocks (check_invariant_tsc false)
113[[nodiscard]] ClockReadings system_clock_readings() noexcept;
114
115// Startup calibration (spins for `window`): TscCalibrator::start(), so the rate is measured against
116// CLOCK_MONOTONIC_RAW and a host wall-clock step inside the window cannot distort it; the anchor
117// follows CLOCK_REALTIME. src/core/time.cpp
118[[nodiscard]] TscCalibration calibrate_tsc(Duration window = milliseconds(50)) noexcept;
119[[nodiscard]] TscCalibration calibrate_tsc(Duration window, const ClockReadings& readings) noexcept;
121// One simultaneous reading of the three clocks (the TSC is the midpoint of a tight bracket).
122struct TscAnchor {
123 std::uint64_t tsc = 0;
124 std::int64_t realtime_ns = 0;
125 std::int64_t raw_ns = 0;
128struct TscRecalibration {
129 TscCalibration calibration; // to publish
130 std::int64_t drift_ns = 0; // CLOCK_REALTIME at the new anchor minus the old mapping's value
131 std::int64_t host_step_ns = 0; // realtime change minus monotonic-raw change over the baseline
132 double elapsed_s = 0.0; // baseline length
133 double rate_change_ppm = 0.0;
134 bool ok = false; // false: no TSC, or the baseline since the previous anchor is too short
137// Long-baseline TSC calibration for the calibrator (control) thread. start() measures an initial
138// rate over a short spin. update() takes only a fresh anchor and derives the rate from the whole
139// interval since the previous anchor, against CLOCK_MONOTONIC_RAW:
140// * anchor jitter (clock_gettime latency spikes of tens of microseconds on a busy or virtualised
141// host) costs a few ppm over a 10 s baseline instead of thousands of ppm over a 50 ms window,
142// which used to drift the mapping by milliseconds between recalibrations and force steps;
143// * NTP slews and host wall-clock steps (WSL2 resynchronising with Windows) cannot distort the
144// rate. A wall-clock step is reported separately in host_step_ns; the published anchor still
145// follows CLOCK_REALTIME, so TscClock steps with the host when it has to.
146class TscCalibrator {
147 public:
148 static constexpr Duration kMinBaseline = milliseconds(500);
149
150 explicit TscCalibrator(ClockReadings readings = system_clock_readings()) noexcept
151 : r_(readings) {}
152 [[nodiscard]] TscCalibration start(Duration window = milliseconds(50)) noexcept;
153 [[nodiscard]] TscRecalibration update() noexcept;
154 [[nodiscard]] const TscCalibration& current() const noexcept { return current_; }
156 private:
157 [[nodiscard]] TscAnchor sample() const noexcept;
158 ClockReadings r_;
159 TscAnchor last_{};
160 TscCalibration current_{};
161};
162
163// Outcome of TscClock::refresh() / reanchor().
164enum class TscRefresh : std::uint8_t {
165 None = 0, // no new calibration published (the common case)
166 Adopted, // the clock had no TSC mapping yet: took the published one as is
167 Reanchored, // continuous: same time at the refresh point, new rate from there on
168 Stepped, // old mapping disagreed with the measurement by more than the threshold
169 Rejected, // the published calibration is unusable (no TSC); kept the current mapping
170};
171[[nodiscard]] constexpr const char* to_string(TscRefresh r) noexcept {
172 switch (r) {
174 return "none";
176 return "adopted";
178 return "reanchored";
180 return "stepped";
182 return "rejected";
183 }
184 return "?";
185}
186
187// TSC reading -> wall ns under `c`. Signed, so readings taken before the anchor (e.g. a
188// network-thread receive stamp converted after a re-anchor) map to earlier times.
189[[nodiscard]] FASTMM_FORCE_INLINE std::int64_t tsc_to_ns(const TscCalibration& c,
190 std::uint64_t tsc) noexcept {
191 const auto dc = static_cast<std::int64_t>(tsc - c.tsc0);
192 return c.ns0 + static_cast<std::int64_t>(
193 (static_cast<Int128>(dc) * static_cast<Int128>(c.ns_per_cycle_q32)) >> 32);
194}
195
196// Periodic recalibration (see docs/explanation/architecture.md, "Clock calibration"): a calibrator
197// thread measures with calibrate_tsc() and publishes into a Seqlocked<TscCalibration>; every
198// TscClock user keeps its own copy and calls refresh() from its loop. A clock is owned by one
199// thread: refresh() and the time accessors must not race.
200class TscClock {
201 public:
203 static constexpr Duration kDefaultSlewHorizon = seconds(10);
204 // Upper bound on the rate adjustment used to absorb an offset (a clock servo, not a jump).
205 static constexpr std::int64_t kMaxSlewPpm = 500;
206
207 TscClock() noexcept = default;
208 explicit TscClock(const TscCalibration& c) noexcept : calib_(c) {}
210 // Blocking; call once at startup, before the clock is shared with another thread.
211 void calibrate(Duration window = milliseconds(50)) noexcept { calib_ = calibrate_tsc(window); }
212 void set_calibration(const TscCalibration& c) noexcept { calib_ = c; }
213 [[nodiscard]] const TscCalibration& calibration() const noexcept { return calib_; }
215 // Subscribes to published calibrations. The next refresh() takes whatever is published
216 // (even the version that was current at attach time); after that only new versions.
217 // `slew_horizon` should match the calibrator's period: each re-anchor absorbs the measured
218 // offset over that horizon (see reanchor()); zero disables slewing.
219 void attach_calibration_source(const Seqlocked<TscCalibration>* src,
222 src_ = src;
223 seen_version_ = kNoVersion;
224 step_threshold_ns_ = step_threshold.ns;
225 slew_horizon_ns_ = slew_horizon.ns > 0 ? slew_horizon.ns : 0;
226 }
227 [[nodiscard]] const Seqlocked<TscCalibration>* calibration_source() const noexcept {
228 return src_;
230
231 // Cheap when nothing changed (one atomic load); copies and re-anchors on a new version.
232 // A write in progress is skipped and picked up by a later call.
233 FASTMM_FORCE_INLINE TscRefresh refresh() noexcept {
234 if (FASTMM_LIKELY(src_ == nullptr || src_->version() == seen_version_)) return TscRefresh::None;
235 return refresh_slow();
236 }
237
238 // Switches to `fresh` at TSC reading `at` (refresh() passes rdtsc()). If both mappings use
239 // the TSC and agree at `at` within the step threshold, the clock re-anchors at the old
240 // mapping's time for `at`, so time never jumps, and runs at the fresh rate plus a bounded
241 // correction that absorbs the measured offset over the slew horizon: offset * rate / horizon,
242 // so after `horizon` ns the mapping agrees with `fresh` (clamped to kMaxSlewPpm). Without the
243 // correction the offset would accumulate across re-anchors until it forced a step. If the
244 // offset exceeds the threshold the clock steps to `fresh` instead.
245 TscRefresh reanchor(const TscCalibration& fresh, Cycles at) noexcept {
246 if (!fresh.use_tsc || fresh.ns_per_cycle_q32 == 0) {
247 if (calib_.use_tsc) return TscRefresh::Rejected;
248 calib_ = fresh;
249 return TscRefresh::Adopted;
250 }
251 if (!calib_.use_tsc) {
252 calib_ = fresh;
253 return TscRefresh::Adopted;
254 }
255 const std::int64_t old_ns = tsc_to_ns(calib_, at.v);
256 last_offset_ns_ = tsc_to_ns(fresh, at.v) - old_ns;
257 if (last_offset_ns_ > step_threshold_ns_ || last_offset_ns_ < -step_threshold_ns_) {
258 calib_ = fresh;
259 ++steps_;
260 return TscRefresh::Stepped;
261 }
262 std::uint64_t rate = fresh.ns_per_cycle_q32;
263 slew_ppb_ = 0;
264 if (slew_horizon_ns_ > 0 && last_offset_ns_ != 0) {
265 const Int128 max_adj = static_cast<Int128>(rate) * kMaxSlewPpm / 1'000'000;
266 Int128 adj = static_cast<Int128>(last_offset_ns_) * static_cast<Int128>(rate) /
267 static_cast<Int128>(slew_horizon_ns_);
268 if (adj > max_adj) adj = max_adj;
269 if (adj < -max_adj) adj = -max_adj;
270 rate = static_cast<std::uint64_t>(static_cast<Int128>(rate) + adj);
271 slew_ppb_ = static_cast<std::int64_t>(adj * 1'000'000'000 /
272 static_cast<Int128>(fresh.ns_per_cycle_q32));
273 }
274 calib_.tsc0 = at.v;
275 calib_.ns0 = old_ns;
276 calib_.ns_per_cycle_q32 = rate;
277 calib_.ghz = fresh.ghz;
278 ++reanchors_;
280 }
281
282 // Diagnostics (owner thread, or after the owner stopped).
283 [[nodiscard]] std::uint64_t steps() const noexcept { return steps_; }
284 [[nodiscard]] std::uint64_t reanchors() const noexcept { return reanchors_; }
285 // fresh - old at the last re-anchor or step, in ns.
286 [[nodiscard]] std::int64_t last_offset_ns() const noexcept { return last_offset_ns_; }
287 [[nodiscard]] Duration step_threshold() const noexcept { return Duration{step_threshold_ns_}; }
288 [[nodiscard]] Duration slew_horizon() const noexcept { return Duration{slew_horizon_ns_}; }
289 // Rate correction applied at the last re-anchor, in parts per billion of the fresh rate.
290 [[nodiscard]] std::int64_t slew_ppb() const noexcept { return slew_ppb_; }
291
292 FASTMM_FORCE_INLINE Timestamp now() const noexcept {
293 if (FASTMM_LIKELY(calib_.use_tsc)) return to_timestamp(rdtsc());
294 return wall_now();
295 }
296 // TSC cycles whenever the rate is known (also without an invariant TSC), so that they share
297 // a time base with the network threads' rdtscp stamps; wall ns without a rate.
298 FASTMM_FORCE_INLINE Cycles cycles() const noexcept {
299 if (FASTMM_LIKELY(calib_.has_rate())) return rdtscp();
300 return Cycles{static_cast<std::uint64_t>(wall_now().ns)};
301 }
302 [[nodiscard]] FASTMM_FORCE_INLINE Timestamp to_timestamp(Cycles c) const noexcept {
303 if (FASTMM_LIKELY(calib_.has_rate())) return Timestamp{tsc_to_ns(calib_, c.v)};
304 return Timestamp{static_cast<std::int64_t>(c.v)};
305 }
306 [[nodiscard]] FASTMM_FORCE_INLINE std::int64_t cycles_to_ns(std::uint64_t dc) const noexcept {
307 if (FASTMM_LIKELY(calib_.has_rate()))
308 return static_cast<std::int64_t>(tsc_interval_ns(calib_, dc));
309 return static_cast<std::int64_t>(dc);
310 }
311
312 private:
313 static constexpr std::uint32_t kNoVersion = UINT32_MAX;
314
315 FASTMM_NOINLINE TscRefresh refresh_slow() noexcept {
316 TscCalibration fresh;
317 std::uint32_t version = 0;
318 if (!src_->try_load(fresh, version)) return TscRefresh::None;
319 seen_version_ = version;
320 return reanchor(fresh, rdtsc());
321 }
322
323 TscCalibration calib_{};
324 const Seqlocked<TscCalibration>* src_ = nullptr;
325 std::uint32_t seen_version_ = kNoVersion;
326 std::int64_t step_threshold_ns_ = kDefaultStepThreshold.ns;
327 std::int64_t slew_horizon_ns_ =
328 0; // 0 until attach_calibration_source(): reanchor() alone does not slew
329 std::int64_t slew_ppb_ = 0;
330 std::int64_t last_offset_ns_ = 0;
331 std::uint64_t steps_ = 0;
332 std::uint64_t reanchors_ = 0;
333};
334
335// Deterministic clock for sim/backtest/replay. 1 cycle == 1 ns.
336class SimClock {
337 public:
338 SimClock() noexcept = default;
339 explicit SimClock(Timestamp start) noexcept : now_(start) {}
340 [[nodiscard]] Timestamp now() const noexcept { return now_; }
341 [[nodiscard]] Cycles cycles() const noexcept {
342 return Cycles{static_cast<std::uint64_t>(now_.ns)};
344 [[nodiscard]] Timestamp to_timestamp(Cycles c) const noexcept {
345 return Timestamp{static_cast<std::int64_t>(c.v)};
347 [[nodiscard]] std::int64_t cycles_to_ns(std::uint64_t dc) const noexcept {
348 return static_cast<std::int64_t>(dc);
350 void set(Timestamp t) noexcept {
351 FASTMM_ASSERT(t >= now_);
352 now_ = t;
353 }
354 void advance(Duration d) noexcept { now_.ns += d.ns; }
355 // Replay only: follows a recorded engine clock, which may have stepped backwards.
356 void jump(Timestamp t) noexcept { now_ = t; }
357
358 private:
359 Timestamp now_{};
360};
361
362template <class C>
363concept ClockLike = requires(const C& c, Cycles cy, std::uint64_t dc) {
364 { c.now() } noexcept -> std::same_as<Timestamp>;
365 { c.cycles() } noexcept -> std::same_as<Cycles>;
366 { c.to_timestamp(cy) } noexcept -> std::same_as<Timestamp>;
367 { c.cycles_to_ns(dc) } noexcept -> std::same_as<std::int64_t>;
368};
369static_assert(ClockLike<TscClock> && ClockLike<SimClock>);
370
371} // namespace fastmm
Deterministic clock for sim/backtest/replay. 1 cycle == 1 ns.
Definition time.hpp:338
SimClock() noexcept=default
Timestamp to_timestamp(Cycles c) const noexcept
Definition time.hpp:346
std::int64_t cycles_to_ns(std::uint64_t dc) const noexcept
Definition time.hpp:349
void advance(Duration d) noexcept
Definition time.hpp:356
void jump(Timestamp t) noexcept
Replay only: follows a recorded engine clock, which may have stepped backwards.
Definition time.hpp:358
Cycles cycles() const noexcept
Definition time.hpp:343
void set(Timestamp t) noexcept
Definition time.hpp:352
Timestamp now() const noexcept
Definition time.hpp:342
Long-baseline TSC calibration for the calibrator (control) thread.
Definition time.hpp:148
TscRecalibration update() noexcept
TscCalibration start(Duration window=milliseconds(50)) noexcept
const TscCalibration & current() const noexcept
Definition time.hpp:156
static constexpr Duration kMinBaseline
Definition time.hpp:150
Periodic recalibration (see docs/explanation/architecture.md, "Clock calibration"): a calibrator thre...
Definition time.hpp:202
static constexpr std::int64_t kMaxSlewPpm
Upper bound on the rate adjustment used to absorb an offset (a clock servo, not a jump).
Definition time.hpp:207
void attach_calibration_source(const Seqlocked< TscCalibration > *src, Duration step_threshold=kDefaultStepThreshold, Duration slew_horizon=kDefaultSlewHorizon) noexcept
Subscribes to published calibrations.
Definition time.hpp:221
void set_calibration(const TscCalibration &c) noexcept
Definition time.hpp:214
const TscCalibration & calibration() const noexcept
Definition time.hpp:215
std::int64_t last_offset_ns() const noexcept
fresh - old at the last re-anchor or step, in ns.
Definition time.hpp:288
FASTMM_FORCE_INLINE std::int64_t cycles_to_ns(std::uint64_t dc) const noexcept
Definition time.hpp:308
std::uint64_t reanchors() const noexcept
Definition time.hpp:286
Duration slew_horizon() const noexcept
Definition time.hpp:290
static constexpr Duration kDefaultStepThreshold
Definition time.hpp:204
std::int64_t slew_ppb() const noexcept
Rate correction applied at the last re-anchor, in parts per billion of the fresh rate.
Definition time.hpp:292
TscClock() noexcept=default
FASTMM_FORCE_INLINE Timestamp to_timestamp(Cycles c) const noexcept
Definition time.hpp:304
Duration step_threshold() const noexcept
Definition time.hpp:289
FASTMM_FORCE_INLINE TscRefresh refresh() noexcept
Cheap when nothing changed (one atomic load); copies and re-anchors on a new version.
Definition time.hpp:235
FASTMM_FORCE_INLINE Cycles cycles() const noexcept
TSC cycles whenever the rate is known (also without an invariant TSC), so that they share a time base...
Definition time.hpp:300
void calibrate(Duration window=milliseconds(50)) noexcept
Blocking; call once at startup, before the clock is shared with another thread.
Definition time.hpp:213
static constexpr Duration kDefaultSlewHorizon
Definition time.hpp:205
std::uint64_t steps() const noexcept
Diagnostics (owner thread, or after the owner stopped).
Definition time.hpp:285
FASTMM_FORCE_INLINE Timestamp now() const noexcept
Definition time.hpp:294
const Seqlocked< TscCalibration > * calibration_source() const noexcept
Definition time.hpp:229
TscRefresh reanchor(const TscCalibration &fresh, Cycles at) noexcept
Switches to fresh at TSC reading at (refresh() passes rdtsc()).
Definition time.hpp:247
Definition strategy.hpp:16
constexpr Duration milliseconds(std::int64_t v) noexcept
Definition time.hpp:38
constexpr Duration nanoseconds(std::int64_t v) noexcept
Definition time.hpp:32
ClockReadings system_clock_readings() noexcept
Timestamp wall_now() noexcept
CLOCK_REALTIME via vDSO (~20 ns). Implemented in src/core/time.cpp.
FASTMM_FORCE_INLINE std::uint64_t tsc_interval_ns(const TscCalibration &c, std::uint64_t cycles) noexcept
A TSC cycle interval in ns; 0 without a rate.
Definition time.hpp:101
bool has_invariant_tsc() noexcept
Definition time.hpp:85
FASTMM_FORCE_INLINE Cycles rdtscp() noexcept
Serialising read (rdtscp) is what latency hops want: it waits for prior loads to retire.
Definition time.hpp:66
FASTMM_FORCE_INLINE Cycles rdtsc() noexcept
Definition time.hpp:70
TscKind tsc_kind() noexcept
FASTMM_FORCE_INLINE std::int64_t tsc_to_ns(const TscCalibration &c, std::uint64_t tsc) noexcept
TSC reading -> wall ns under c.
Definition time.hpp:191
Timestamp steady_now() noexcept
TscRefresh
Outcome of TscClock::refresh() / reanchor().
Definition time.hpp:166
@ Reanchored
continuous: same time at the refresh point, new rate from there on
@ Stepped
old mapping disagreed with the measurement by more than the threshold
@ None
no new calibration published (the common case)
@ Adopted
the clock had no TSC mapping yet: took the published one as is
@ Rejected
the published calibration is unusable (no TSC); kept the current mapping
TscCalibration calibrate_tsc(Duration window=milliseconds(50)) noexcept
Startup calibration (spins for window): TscCalibrator::start(), so the rate is measured against CLOCK...
constexpr std::string_view to_string(ParamType t) noexcept
Definition params.hpp:63
TscKind
What /proc/cpuinfo says about the TSC (cached after the first call): Invariant constant_tsc + nonstop...
Definition time.hpp:83
constexpr Duration seconds(std::int64_t v) noexcept
Definition time.hpp:41
constexpr Duration microseconds(std::int64_t v) noexcept
Definition time.hpp:35
Clock readings used by TscCalibrator.
Definition time.hpp:108
bool check_invariant_tsc
false for injected clocks
Definition time.hpp:112
TscKind assumed_kind
injected clocks (check_invariant_tsc false)
Definition time.hpp:113
std::int64_t(* realtime_ns)() noexcept
Definition time.hpp:110
std::int64_t(* monotonic_raw_ns)() noexcept
Definition time.hpp:111
std::uint64_t(* tsc)() noexcept
Definition time.hpp:109
Definition time.hpp:59
constexpr auto operator<=>(const Cycles &) const noexcept=default
friend constexpr std::uint64_t operator-(Cycles a, Cycles b) noexcept
Definition time.hpp:62
std::uint64_t v
Definition time.hpp:60
Definition time.hpp:22
friend constexpr Duration operator-(Duration a, Duration b) noexcept
Definition time.hpp:26
constexpr std::int64_t micros() const noexcept
Definition time.hpp:30
std::int64_t ns
Definition time.hpp:23
friend constexpr Duration operator+(Duration a, Duration b) noexcept
Definition time.hpp:25
friend constexpr Duration operator*(Duration a, std::int64_t k) noexcept
Definition time.hpp:27
constexpr std::int64_t millis() const noexcept
Definition time.hpp:29
constexpr auto operator<=>(const Duration &) const noexcept=default
friend constexpr Duration operator/(Duration a, std::int64_t k) noexcept
Definition time.hpp:28
Definition time.hpp:45
constexpr Timestamp & operator+=(Duration d) noexcept
Definition time.hpp:51
constexpr auto operator<=>(const Timestamp &) const noexcept=default
friend constexpr Timestamp operator+(Timestamp a, Duration d) noexcept
Definition time.hpp:49
friend constexpr Duration operator-(Timestamp a, Timestamp b) noexcept
Definition time.hpp:48
std::int64_t ns
Definition time.hpp:46
static constexpr Timestamp max() noexcept
Definition time.hpp:56
constexpr bool valid() const noexcept
Definition time.hpp:55
One simultaneous reading of the three clocks (the TSC is the midpoint of a tight bracket).
Definition time.hpp:124
Definition time.hpp:89
std::uint64_t tsc0
TSC reading at the anchor.
Definition time.hpp:90
double ghz
diagnostics only
Definition time.hpp:93
std::uint64_t ns_per_cycle_q32
32.32 fixed point; 0: no TSC rate
Definition time.hpp:92
std::int64_t ns0
wall time at the anchor
Definition time.hpp:91
constexpr bool has_rate() const noexcept
Definition time.hpp:97
bool use_tsc
Wall time from the TSC.
Definition time.hpp:96
Definition time.hpp:130
bool ok
false: no TSC, or the baseline since the previous anchor is too short
Definition time.hpp:136
std::int64_t host_step_ns
realtime change minus monotonic-raw change over the baseline
Definition time.hpp:133
double elapsed_s
baseline length
Definition time.hpp:134
TscCalibration calibration
to publish
Definition time.hpp:131
std::int64_t drift_ns
CLOCK_REALTIME at the new anchor minus the old mapping's value.
Definition time.hpp:132
double rate_change_ppm
Definition time.hpp:135