fast_clock/lib.rs
1//! Low-overhead timing without hiding the details from you.
2//!
3//! # Core abstractions
4//!
5//! - [`Time`]: Defines the `Instant` and `Duration` types for a clock domain and
6//! the arithmetic between them.
7//! - [`Clock`]: Provides [`Clock::now`] and names the associated [`Time`] and
8//! [`DurationCalibration`] types.
9//! - [`DurationCalibration`]: Converts durations to/from nanoseconds as `u64`.
10//! - [`CalibratedClock`]: Bundles a [`Clock`] with its calibration for convenient passing.
11//! - [`ClockSynchronization`]: Correlates instants between two clock domains.
12//!
13//! # Standard library clocks
14//!
15//! [`std_clocks::InstantClock`] and [`std_clocks::SystemClock`] wrap
16//! `std::time::Instant` and `std::time::SystemTime`. They use [`InherentlyCalibrated`],
17//! as their duration type is directly convertible to/from nanoseconds.
18//!
19//! # Hardware clocks
20//!
21//! [`tsc::Tsc`] reads the x86_64 timestamp counter. Its ticks are not nanoseconds,
22//! so calibration via [`wrapping_u64::U64Calibration`] is required. Calibration also
23//! produces a [`ClockSynchronization`] that can convert TSC instants to
24//! `std::time::Instant` and vice versa.
25//!
26//! ```
27//! # #[cfg(all(feature = "tsc", target_arch = "x86_64"))]
28//! # {
29//! # use fast_clock::{Clock, DurationCalibration, CalibratedClock, InherentlyCalibrated};
30//! # use fast_clock::tsc::Tsc;
31//! # use fast_clock::wrapping_u64::{U64Calibration, WrappingU64Time};
32//! # use fast_clock::Time;
33//!
34//! let tsc = Tsc::try_new_assume_stable().unwrap();
35//! let (calibration, sync) = U64Calibration::new_with_std_instant(
36//! &tsc,
37//! std::time::Duration::from_millis(100),
38//! );
39//! let clock = CalibratedClock { clock: tsc, calibration };
40//!
41//! let t0 = clock.clock.now();
42//! // ... timed section ...
43//! let t1 = clock.clock.now();
44//!
45//! let duration = WrappingU64Time::instant_sub(t1, t0);
46//! let duration_ns: u64 = clock.calibration.convert_to_ns(duration);
47//!
48//! // Convert a TSC instant to std::time::Instant using the synchronization point.
49//! let std_instant = sync.to_a(t0, &InherentlyCalibrated, &clock.calibration);
50//! # }
51//! ```
52//!
53//! # Features
54//!
55//! | Feature | Default | Description |
56//! |---------|---------|-------------|
57//! | `std` | yes | Enables [`std_clocks`] and `std`-dependent methods. |
58//! | `tsc` | yes | Enables [`tsc`] (x86_64 only). |
59//!
60//! Contributions adding more clocks are welcome.
61
62#![no_std]
63
64#[cfg(feature = "std")]
65extern crate std;
66
67mod clock_synchronization;
68use core::cmp::{self};
69
70pub use clock_synchronization::ClockSynchronization;
71
72#[cfg(feature = "std")]
73pub mod std_clocks;
74#[cfg(all(feature = "tsc", target_arch = "x86_64"))]
75pub mod tsc;
76pub mod wrapping_u64;
77
78/// Arithmetic types and operations for a clock domain.
79///
80/// Most users will use the provided implementations: [`std_clocks::InstantTime`],
81/// [`std_clocks::SystemTimeTime`], and [`wrapping_u64::WrappingU64Time`].
82pub trait Time {
83 type Instant: Copy;
84 type Duration: Copy;
85 /// Returns `a - b`. Behavior when `a < b` is unspecified: implementations may
86 /// panic or return a meaningless value. Use [`instant_cmp`](Self::instant_cmp) to
87 /// check ordering first if unsure.
88 fn instant_sub(a: Self::Instant, b: Self::Instant) -> Self::Duration;
89 /// Returns `a - b`. Behavior when `a < b` is unspecified: implementations may
90 /// panic or return a meaningless value.
91 fn duration_sub(a: Self::Duration, b: Self::Duration) -> Self::Duration;
92 fn duration_add(a: Self::Duration, b: Self::Duration) -> Self::Duration;
93 fn mixed_sub(a: Self::Instant, b: Self::Duration) -> Self::Instant;
94 fn mixed_add(a: Self::Instant, b: Self::Duration) -> Self::Instant;
95 fn instant_cmp(a: Self::Instant, b: Self::Instant) -> cmp::Ordering;
96}
97
98/// A source of time readings.
99///
100/// Call [`Clock::now`] to sample an instant.
101pub trait Clock {
102 type Time: Time;
103 type Calibration: DurationCalibration<<Self::Time as Time>::Duration>;
104 /// Returns the current instant.
105 fn now(&self) -> <Self::Time as Time>::Instant;
106}
107
108/// Converts a clock's native duration type to and from nanoseconds.
109pub trait DurationCalibration<D> {
110 /// Converts a duration to nanoseconds, rounding to the nearest nanosecond.
111 fn convert_to_ns(&self, d: D) -> u64;
112 /// Converts a nanosecond value to this clock's native duration type.
113 fn convert_from_ns(&self, ns: u64) -> D;
114 #[cfg(feature = "std")]
115 fn to_std(&self, d: D) -> std::time::Duration
116 where
117 Self: Sized,
118 {
119 std::time::Duration::from_nanos(self.convert_to_ns(d))
120 }
121 #[cfg(feature = "std")]
122 fn convert_from_std(&self, d: std::time::Duration) -> D
123 where
124 Self: Sized,
125 {
126 self.convert_from_ns(InherentlyCalibrated.convert_to_ns(d))
127 }
128}
129
130/// A [`Clock`] bundled with its [`DurationCalibration`].
131#[derive(Clone, Copy, Debug)]
132pub struct CalibratedClock<C: Clock> {
133 pub clock: C,
134 pub calibration: C::Calibration,
135}
136
137impl<C: Clock<Calibration = InherentlyCalibrated>> CalibratedClock<C> {
138 /// Construct a `CalibratedClock` for a [Clock] that is inherently calibrated.
139 pub fn inherent(clock: C) -> Self {
140 CalibratedClock {
141 clock,
142 calibration: InherentlyCalibrated,
143 }
144 }
145}
146
147/// Calibration type for clocks whose native duration is already in nanoseconds.
148///
149/// Used with [`std_clocks::InstantClock`] and [`std_clocks::SystemClock`].
150#[derive(Clone, Copy, Debug)]
151pub struct InherentlyCalibrated;