use std::sync::atomic::{AtomicU64, Ordering};
use std::time::Instant;
#[cfg(target_arch = "x86_64")]
#[inline]
pub fn cpu_cycles() -> u64 {
unsafe {
let mut _aux: u32 = 0;
core::arch::x86_64::__rdtscp(&mut _aux)
}
}
#[cfg(target_arch = "aarch64")]
#[inline]
pub fn cpu_cycles() -> u64 {
let cycles: u64;
unsafe {
core::arch::asm!("mrs {}, cntvct_el0", out(reg) cycles);
}
cycles
}
#[cfg(not(any(target_arch = "x86_64", target_arch = "aarch64")))]
#[inline]
pub fn cpu_cycles() -> u64 {
0
}
static CACHED_NANOS: AtomicU64 = AtomicU64::new(0);
static EPOCH: std::sync::OnceLock<Instant> = std::sync::OnceLock::new();
static TIME_SERVICE_INIT: std::sync::OnceLock<()> = std::sync::OnceLock::new();
pub fn init_time_service() {
TIME_SERVICE_INIT.get_or_init(|| {
let epoch = *EPOCH.get_or_init(Instant::now);
CACHED_NANOS.store(0, Ordering::Relaxed);
std::thread::Builder::new()
.name("trueno-time-service".into())
.spawn(move || loop {
std::thread::sleep(std::time::Duration::from_micros(100));
let elapsed = epoch.elapsed().as_nanos() as u64;
CACHED_NANOS.store(elapsed, Ordering::Relaxed);
})
.expect("Failed to spawn time service thread");
});
}
#[inline]
pub fn cached_nanos() -> u64 {
CACHED_NANOS.load(Ordering::Relaxed)
}
#[inline]
pub fn cached_nanos_or_now() -> u64 {
let cached = CACHED_NANOS.load(Ordering::Relaxed);
if cached == 0 && TIME_SERVICE_INIT.get().is_none() {
EPOCH.get_or_init(Instant::now).elapsed().as_nanos() as u64
} else {
cached
}
}
#[cfg(target_os = "linux")]
pub fn get_page_faults() -> (u64, u64) {
use std::fs;
let stat = fs::read_to_string("/proc/self/stat").unwrap_or_default();
let fields: Vec<&str> = stat.split_whitespace().collect();
if fields.len() > 12 {
let minor = fields[9].parse().unwrap_or(0);
let major = fields[11].parse().unwrap_or(0);
(minor, major)
} else {
(0, 0)
}
}
#[cfg(not(target_os = "linux"))]
pub fn get_page_faults() -> (u64, u64) {
(0, 0)
}
pub fn with_page_fault_tracking<T, F: FnOnce() -> T>(name: &str, f: F) -> (T, u64, u64) {
let (minor_before, major_before) = get_page_faults();
let result = f();
let (minor_after, major_after) = get_page_faults();
let minor_delta = minor_after.saturating_sub(minor_before);
let major_delta = major_after.saturating_sub(major_before);
#[cfg(feature = "tracing")]
if minor_delta > 1000 || major_delta > 0 {
tracing::warn!(
operation = name,
minor_faults = minor_delta,
major_faults = major_delta,
"High page fault count detected"
);
}
let _ = name; (result, minor_delta, major_delta)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_cpu_cycles_returns_value() {
let cycles = cpu_cycles();
#[cfg(any(target_arch = "x86_64", target_arch = "aarch64"))]
assert!(cycles > 0);
#[cfg(not(any(target_arch = "x86_64", target_arch = "aarch64")))]
assert_eq!(cycles, 0);
}
#[test]
fn test_cached_nanos_or_now_returns_value() {
let nanos = cached_nanos_or_now();
let _ = nanos; }
#[test]
fn test_page_fault_tracking() {
let (result, minor, major) = with_page_fault_tracking("test", || 42);
assert_eq!(result, 42);
let _ = (minor, major); }
#[test]
fn test_get_page_faults() {
let (minor, major) = get_page_faults();
let _ = (minor, major); }
}