blob: 990b59cad42caf429070415539e6197f4918baa2 [file] [log] [blame]
//! Like `std::time::Instant`, but also measures memory & CPU cycles.
#![allow(clippy::print_stderr)]
use std::{
fmt,
time::{Duration, Instant},
};
use crate::MemoryUsage;
pub struct StopWatch {
time: Instant,
#[cfg(target_os = "linux")]
counter: Option<perf_event::Counter>,
memory: MemoryUsage,
}
pub struct StopWatchSpan {
pub time: Duration,
pub instructions: Option<u64>,
pub memory: MemoryUsage,
}
impl StopWatch {
pub fn start() -> StopWatch {
#[cfg(target_os = "linux")]
let counter = {
// When debugging rust-analyzer using rr, the perf-related syscalls cause it to abort.
// We allow disabling perf by setting the env var `RA_DISABLE_PERF`.
use once_cell::sync::Lazy;
static PERF_ENABLED: Lazy<bool> =
Lazy::new(|| std::env::var_os("RA_DISABLE_PERF").is_none());
if *PERF_ENABLED {
let mut counter = perf_event::Builder::new()
.build()
.map_err(|err| eprintln!("Failed to create perf counter: {err}"))
.ok();
if let Some(counter) = &mut counter {
if let Err(err) = counter.enable() {
eprintln!("Failed to start perf counter: {err}")
}
}
counter
} else {
None
}
};
let memory = MemoryUsage::now();
let time = Instant::now();
StopWatch {
time,
#[cfg(target_os = "linux")]
counter,
memory,
}
}
pub fn elapsed(&mut self) -> StopWatchSpan {
let time = self.time.elapsed();
#[cfg(target_os = "linux")]
let instructions = self.counter.as_mut().and_then(|it| {
it.read().map_err(|err| eprintln!("Failed to read perf counter: {err}")).ok()
});
#[cfg(not(target_os = "linux"))]
let instructions = None;
let memory = MemoryUsage::now() - self.memory;
StopWatchSpan { time, instructions, memory }
}
}
impl fmt::Display for StopWatchSpan {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{:.2?}", self.time)?;
if let Some(mut instructions) = self.instructions {
let mut prefix = "";
if instructions > 10000 {
instructions /= 1000;
prefix = "k";
}
if instructions > 10000 {
instructions /= 1000;
prefix = "m";
}
if instructions > 10000 {
instructions /= 1000;
prefix = "g";
}
write!(f, ", {instructions}{prefix}instr")?;
}
write!(f, ", {}", self.memory)?;
Ok(())
}
}