pub struct TimestampGenerator { /* private fields */ }Expand description
High-precision timestamp generator using TSC.
This generator provides strictly monotonic timestamps with sub-nanosecond resolution and zero syscall overhead after initialization.
§Single-owner invariant
Each producer should own a dedicated TimestampGenerator. The
type is Send + Sync and next() is safe to call concurrently —
monotonicity is preserved by compare_exchange_weak — but the CAS
loop degenerates into a spin under sustained contention. The whole
design rests on the loop almost never iterating: that’s only true
when one writer at a time accesses the generator.
The codebase enforces this structurally rather than at runtime:
Shardowns itsTimestampGeneratorby value (not behindArc).TimestampGeneratoris notClone, so duplicating one is a deliberatemem::replace/Default::default()away — visible in code review.- The shard’s surrounding
Mutex<Shard>serializes producers, sonext()is invoked by exactly one caller at a time per shard.
If you find yourself reaching for Arc<TimestampGenerator>, stop
— give each producer its own instance instead. Every additional
concurrent caller is one more thread potentially CAS-spinning on
last.
Implementations§
Source§impl TimestampGenerator
impl TimestampGenerator
Sourcepub fn new() -> Self
pub fn new() -> Self
Create a new timestamp generator.
This performs a one-time calibration against the system clock. Subsequent timestamp reads use TSC directly.
Sourcepub fn next(&self) -> u64
pub fn next(&self) -> u64
Generate the next timestamp.
Returns a strictly monotonically increasing value in nanoseconds since this generator was constructed. This operation is lock-free and does not invoke any syscalls.
Previously returned the raw TSC tick count. The docstring
claimed nanoseconds, but on a 3.5 GHz core the value was ~3.5×
larger than ns-since-epoch, breaking any consumer that read
insertion_ts and tried to correlate it with wall-clock-derived
timestamps from elsewhere. Converting via delta_as_nanos here
costs ~1ns extra per call and gives consumers a unit they can
actually use.
§Performance
- Single-threaded: ~6-12ns per call
- Under contention: may loop due to CAS, but still lock-free
Sourcepub fn now_raw(&self) -> u64
pub fn now_raw(&self) -> u64
Get the current raw timestamp without incrementing.
This does NOT guarantee monotonicity and is only useful for measuring elapsed time or debugging.
Sourcepub fn raw_to_nanos(&self, raw: u64) -> u64
pub fn raw_to_nanos(&self, raw: u64) -> u64
Convert a raw timestamp to nanoseconds since this
generator was constructed (i.e. since baseline_raw).
Output units match next(): the value returned by
raw_to_nanos(self.now_raw()) is comparable to
recently-next()-returned timestamps from the same
generator (modulo the monotonicity floor next() enforces).
Note: NOT “nanoseconds since UNIX epoch”. The reference
point is the per-generator construction moment, so two
generators created at different times produce values with
different offsets. For wall-clock-anchored debugging,
combine with SystemTime::now() at generator-construction
time (recorded externally).
Pre-fix this called delta_as_nanos(0, raw), where the
0 baseline was an unspecified quanta-internal reference
(typically system boot under Windows QPC or the clock’s
first-call moment elsewhere). The returned ns values
were in the order of system uptime — not comparable to
next() output, despite the function’s previous “ns
since epoch” doc-claim. Aligning both to baseline_raw
makes the surface consistent.