use std::time::{Duration, Instant};
use once_cell::sync::Lazy;
use crate::perf::syscall_bypass::SystemCallBypassManager;
static FAST_TIMER: Lazy<FastTimer> = Lazy::new(|| FastTimer::new());
pub struct FastTimer {
bypass_manager: SystemCallBypassManager,
_base_instant: Instant,
_base_nanos: u64,
}
impl FastTimer {
fn new() -> Self {
use crate::perf::syscall_bypass::SyscallBypassConfig;
let bypass_manager = SystemCallBypassManager::new(SyscallBypassConfig::default())
.expect("Failed to create SystemCallBypassManager");
let base_instant = Instant::now();
let base_nanos = bypass_manager.fast_timestamp_nanos();
Self {
bypass_manager,
_base_instant: base_instant,
_base_nanos: base_nanos,
}
}
#[inline(always)]
pub fn now_nanos(&self) -> u64 {
self.bypass_manager.fast_timestamp_nanos()
}
#[inline(always)]
pub fn now_micros(&self) -> u64 {
self.now_nanos() / 1_000
}
#[inline(always)]
pub fn now_millis(&self) -> u64 {
self.now_nanos() / 1_000_000
}
#[inline(always)]
pub fn elapsed_nanos(&self, start_nanos: u64) -> u64 {
self.now_nanos().saturating_sub(start_nanos)
}
#[inline(always)]
pub fn elapsed_duration(&self, start_nanos: u64) -> Duration {
Duration::from_nanos(self.elapsed_nanos(start_nanos))
}
}
#[inline(always)]
pub fn fast_now_nanos() -> u64 {
FAST_TIMER.now_nanos()
}
#[inline(always)]
pub fn fast_now_micros() -> u64 {
FAST_TIMER.now_micros()
}
#[inline(always)]
pub fn fast_now_millis() -> u64 {
FAST_TIMER.now_millis()
}
#[inline(always)]
pub fn fast_elapsed_nanos(start_nanos: u64) -> u64 {
FAST_TIMER.elapsed_nanos(start_nanos)
}
#[inline(always)]
pub fn fast_elapsed(start_nanos: u64) -> Duration {
FAST_TIMER.elapsed_duration(start_nanos)
}
pub struct FastStopwatch {
start_nanos: u64,
#[allow(dead_code)]
label: &'static str,
}
impl FastStopwatch {
#[inline(always)]
pub fn start(label: &'static str) -> Self {
Self {
start_nanos: fast_now_nanos(),
label,
}
}
#[inline(always)]
pub fn elapsed_nanos(&self) -> u64 {
fast_elapsed_nanos(self.start_nanos)
}
#[inline(always)]
pub fn elapsed(&self) -> Duration {
fast_elapsed(self.start_nanos)
}
#[inline(always)]
pub fn elapsed_micros(&self) -> u64 {
self.elapsed_nanos() / 1_000
}
#[inline(always)]
pub fn elapsed_millis(&self) -> u64 {
self.elapsed_nanos() / 1_000_000
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_fast_timing() {
let start = fast_now_nanos();
std::thread::sleep(Duration::from_millis(10));
let elapsed = fast_elapsed_nanos(start);
assert!(elapsed >= 9_000_000 && elapsed <= 12_000_000);
}
#[test]
fn test_stopwatch() {
let sw = FastStopwatch::start("test");
std::thread::sleep(Duration::from_millis(10));
let elapsed_ms = sw.elapsed_millis();
assert!(elapsed_ms >= 9 && elapsed_ms <= 12);
}
#[test]
fn test_fast_now_overhead() {
let iterations = 10_000;
let start = Instant::now();
for _ in 0..iterations {
let _ = fast_now_nanos();
}
let total_elapsed = start.elapsed();
let avg_per_call = total_elapsed.as_nanos() / iterations;
if crate::common::sdk_log::sdk_log_enabled() {
println!("Average fast_now_nanos() call: {}ns", avg_per_call);
}
assert!(avg_per_call < 100);
}
#[test]
fn test_instant_now_overhead() {
let iterations = 10_000;
let start = Instant::now();
for _ in 0..iterations {
let _ = Instant::now();
}
let total_elapsed = start.elapsed();
let avg_per_call = total_elapsed.as_nanos() / iterations;
if crate::common::sdk_log::sdk_log_enabled() {
println!("Average Instant::now() call: {}ns", avg_per_call);
}
}
}