use std::time::{Instant, SystemTime, UNIX_EPOCH};
#[inline]
fn system_time_micros() -> i64 {
match SystemTime::now().duration_since(UNIX_EPOCH) {
Ok(duration) => i64::try_from(duration.as_micros()).unwrap_or(i64::MAX),
Err(error) => -i64::try_from(error.duration().as_micros()).unwrap_or(i64::MAX),
}
}
#[derive(Debug)]
pub struct HighPerformanceClock {
base_instant: Instant,
base_timestamp_us: i64,
last_calibration: Instant,
calibration_interval_secs: u64,
}
impl HighPerformanceClock {
pub fn new() -> Self {
Self::new_with_calibration_interval(300)
}
pub fn new_with_calibration_interval(calibration_interval_secs: u64) -> Self {
let mut best_offset = i64::MAX;
let mut best_instant = Instant::now();
let mut best_timestamp = system_time_micros();
for _ in 0..3 {
let instant_before = Instant::now();
let timestamp = system_time_micros();
let instant_after = Instant::now();
let sample_latency = instant_after.duration_since(instant_before).as_nanos() as i64;
if sample_latency < best_offset {
best_offset = sample_latency;
best_instant = instant_before;
best_timestamp = timestamp;
}
}
Self {
base_instant: best_instant,
base_timestamp_us: best_timestamp,
last_calibration: best_instant,
calibration_interval_secs,
}
}
#[inline(always)]
pub fn now_micros(&self) -> i64 {
let elapsed = self.base_instant.elapsed();
self.base_timestamp_us + elapsed.as_micros() as i64
}
pub fn now_micros_with_calibration(&mut self) -> i64 {
if self.last_calibration.elapsed().as_secs() >= self.calibration_interval_secs {
self.recalibrate();
}
self.now_micros()
}
fn recalibrate(&mut self) {
let current_monotonic = Instant::now();
let current_utc = system_time_micros();
let expected_utc = self.base_timestamp_us
+ current_monotonic
.duration_since(self.base_instant)
.as_micros() as i64;
let drift_us = current_utc - expected_utc;
if drift_us.abs() > 1000 {
self.base_instant = current_monotonic;
self.base_timestamp_us = current_utc;
}
self.last_calibration = current_monotonic;
}
#[inline(always)]
pub fn elapsed_micros_since(&self, start_timestamp_us: i64) -> i64 {
self.now_micros() - start_timestamp_us
}
#[inline(always)]
pub fn now_nanos(&self) -> i128 {
let elapsed = self.base_instant.elapsed();
(self.base_timestamp_us as i128 * 1000) + elapsed.as_nanos() as i128
}
pub fn reset(&mut self) {
*self = Self::new_with_calibration_interval(self.calibration_interval_secs);
}
}
impl Default for HighPerformanceClock {
fn default() -> Self {
Self::new()
}
}
static HIGH_PERF_CLOCK: std::sync::OnceLock<HighPerformanceClock> = std::sync::OnceLock::new();
#[inline(always)]
pub fn now_micros() -> i64 {
let clock = HIGH_PERF_CLOCK.get_or_init(HighPerformanceClock::new);
clock.now_micros()
}
#[inline(always)]
pub fn elapsed_micros_since(start_timestamp_us: i64) -> i64 {
now_micros() - start_timestamp_us
}
#[inline(always)]
pub fn now_nanos() -> i128 {
let clock = HIGH_PERF_CLOCK.get_or_init(HighPerformanceClock::new);
clock.now_nanos()
}
#[inline(always)]
pub fn now_us() -> i64 {
#[cfg(target_os = "windows")]
{
now_micros()
}
#[cfg(not(target_os = "windows"))]
{
let clock_id = {
#[cfg(target_os = "linux")]
{
libc::CLOCK_REALTIME_COARSE
}
#[cfg(not(target_os = "linux"))]
{
libc::CLOCK_REALTIME
}
};
let mut ts = libc::timespec {
tv_sec: 0,
tv_nsec: 0,
};
if unsafe { libc::clock_gettime(clock_id, &mut ts) } != 0 {
return now_micros();
}
ts.tv_sec * 1_000_000 + ts.tv_nsec / 1_000
}
}
#[cfg(test)]
mod tests {
use super::*;
use std::thread;
use std::time::{Duration, Instant};
fn assert_matches_wall_clock(elapsed_us: i64, wall_elapsed: Duration) {
let wall_us = i64::try_from(wall_elapsed.as_micros()).expect("test duration fits in i64");
let drift_us = elapsed_us.abs_diff(wall_us);
assert!(
drift_us <= 5_000,
"clock elapsed {elapsed_us} us differs from wall elapsed {wall_us} us"
);
}
#[test]
fn test_high_performance_clock_basic() {
let clock = HighPerformanceClock::new();
let t1 = clock.now_micros();
let wall_start = Instant::now();
thread::sleep(Duration::from_millis(10));
let wall_elapsed = wall_start.elapsed();
let t2 = clock.now_micros();
let elapsed = t2 - t1;
assert!(elapsed >= 10_000, "elapsed: {} μs", elapsed);
assert_matches_wall_clock(elapsed, wall_elapsed);
}
#[test]
fn test_elapsed_micros_since() {
let clock = HighPerformanceClock::new();
let start = clock.now_micros();
let wall_start = Instant::now();
thread::sleep(Duration::from_millis(5));
let wall_elapsed = wall_start.elapsed();
let elapsed = clock.elapsed_micros_since(start);
assert!(elapsed >= 5_000, "elapsed: {} μs", elapsed);
assert_matches_wall_clock(elapsed, wall_elapsed);
}
#[test]
fn test_global_clock() {
let t1 = now_micros();
thread::sleep(Duration::from_millis(1));
let t2 = now_micros();
assert!(t2 > t1);
assert!(t2 - t1 >= 1_000); }
#[test]
fn test_elapsed_global() {
let start = now_micros();
let wall_start = Instant::now();
thread::sleep(Duration::from_millis(2));
let wall_elapsed = wall_start.elapsed();
let elapsed = elapsed_micros_since(start);
assert!(elapsed >= 2_000, "elapsed: {} μs", elapsed);
assert_matches_wall_clock(elapsed, wall_elapsed);
}
#[test]
fn test_clock_precision() {
let clock = HighPerformanceClock::new();
let mut timestamps = Vec::new();
for _ in 0..100 {
timestamps.push(clock.now_micros());
}
for i in 1..timestamps.len() {
assert!(timestamps[i] >= timestamps[i - 1], "时间戳应该单调递增");
}
}
#[test]
fn test_calibration() {
let mut clock = HighPerformanceClock::new_with_calibration_interval(0); let t1 = clock.now_micros_with_calibration();
thread::sleep(Duration::from_millis(10));
let t2 = clock.now_micros_with_calibration();
let elapsed = t2 - t1;
assert!(elapsed >= 10_000, "elapsed: {} μs", elapsed);
}
}