use std::time::Duration;
#[cfg(target_arch = "x86_64")]
#[inline(always)]
fn read_cycles() -> u64 {
unsafe {
let mut _aux: u32 = 0;
std::arch::x86_64::__rdtscp(&mut _aux)
}
}
#[cfg(target_arch = "aarch64")]
#[inline(always)]
fn read_cycles() -> u64 {
let cnt: u64;
unsafe {
std::arch::asm!("mrs {}, cntvct_el0", out(reg) cnt, options(nostack, nomem));
}
cnt
}
#[cfg(not(any(target_arch = "x86_64", target_arch = "aarch64")))]
#[inline(always)]
fn read_cycles() -> u64 {
0
}
pub const HAS_CYCLE_COUNTER: bool = cfg!(target_arch = "x86_64") || cfg!(target_arch = "aarch64");
#[derive(Debug, Clone, Copy)]
pub struct Instant {
instant: std::time::Instant,
tsc: u64,
}
impl Instant {
#[inline(always)]
pub fn now() -> Self {
let tsc = read_cycles();
Self {
instant: std::time::Instant::now(),
tsc,
}
}
#[inline(always)]
pub fn elapsed(&self) -> Duration {
self.instant.elapsed()
}
#[inline(always)]
pub fn cycles(&self) -> u64 {
self.tsc
}
}
pub struct Timer {
start: Instant,
cycles_start: u64,
}
impl Timer {
#[inline(always)]
pub fn start() -> Self {
let cycles_start = read_cycles();
Self {
start: Instant::now(),
cycles_start,
}
}
#[inline(always)]
pub fn stop(&self) -> (u64, u64) {
let elapsed = self.start.elapsed();
let nanos = elapsed.as_nanos() as u64;
let cycles = read_cycles().saturating_sub(self.cycles_start);
(nanos, cycles)
}
}
#[cfg(target_os = "linux")]
pub fn pin_to_cpu(cpu: usize) -> Result<(), std::io::Error> {
use std::mem::MaybeUninit;
unsafe {
let mut set = MaybeUninit::<libc::cpu_set_t>::zeroed();
let set_ref = set.assume_init_mut();
libc::CPU_ZERO(set_ref);
libc::CPU_SET(cpu, set_ref);
let result = libc::sched_setaffinity(0, std::mem::size_of::<libc::cpu_set_t>(), set_ref);
if result == 0 {
Ok(())
} else {
Err(std::io::Error::last_os_error())
}
}
}
#[cfg(not(target_os = "linux"))]
pub fn pin_to_cpu(_cpu: usize) -> Result<(), std::io::Error> {
Ok(())
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_instant_elapsed() {
let start = Instant::now();
std::thread::sleep(Duration::from_millis(10));
let elapsed = start.elapsed();
assert!(elapsed >= Duration::from_millis(5));
assert!(elapsed < Duration::from_millis(100));
}
#[test]
fn test_timer() {
let timer = Timer::start();
std::thread::sleep(Duration::from_millis(10));
let (nanos, _cycles) = timer.stop();
assert!(nanos >= 5_000_000);
}
#[test]
fn test_cycle_counter() {
if HAS_CYCLE_COUNTER {
let a = read_cycles();
let b = read_cycles();
assert!(b >= a, "cycle counter should be monotonic");
}
}
}