use std::time::Instant;
#[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 = chrono::Utc::now().timestamp_micros();
for _ in 0..3 {
let instant_before = Instant::now();
let timestamp = chrono::Utc::now().timestamp_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 = chrono::Utc::now().timestamp_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: once_cell::sync::OnceCell<HighPerformanceClock> =
once_cell::sync::OnceCell::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 };
unsafe {
libc::clock_gettime(clock_id, &mut ts);
}
(ts.tv_sec as i64) * 1_000_000 + (ts.tv_nsec as i64) / 1_000
}
}
#[cfg(test)]
mod tests {
use super::*;
use std::thread;
use std::time::Duration;
#[test]
fn test_high_performance_clock_basic() {
let clock = HighPerformanceClock::new();
let t1 = clock.now_micros();
thread::sleep(Duration::from_millis(10));
let t2 = clock.now_micros();
let elapsed = t2 - t1;
assert!(elapsed >= 10_000, "elapsed: {} μs", elapsed); assert!(elapsed < 20_000, "elapsed: {} μs", elapsed); }
#[test]
fn test_elapsed_micros_since() {
let clock = HighPerformanceClock::new();
let start = clock.now_micros();
thread::sleep(Duration::from_millis(5));
let elapsed = clock.elapsed_micros_since(start);
assert!(elapsed >= 5_000, "elapsed: {} μs", elapsed);
assert!(elapsed < 10_000, "elapsed: {} μs", 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();
thread::sleep(Duration::from_millis(2));
let elapsed = elapsed_micros_since(start);
assert!(elapsed >= 2_000, "elapsed: {} μs", elapsed);
assert!(elapsed < 5_000, "elapsed: {} μs", 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);
}
}