1 //! Like `std::time::Instant`, but also measures memory & CPU cycles. 2 use std::{ 3 fmt, 4 time::{Duration, Instant}, 5 }; 6 7 use crate::MemoryUsage; 8 9 pub struct StopWatch { 10 time: Instant, 11 #[cfg(target_os = "linux")] 12 counter: Option<perf_event::Counter>, 13 memory: Option<MemoryUsage>, 14 } 15 16 pub struct StopWatchSpan { 17 pub time: Duration, 18 pub instructions: Option<u64>, 19 pub memory: Option<MemoryUsage>, 20 } 21 22 impl StopWatch { start() -> StopWatch23 pub fn start() -> StopWatch { 24 #[cfg(target_os = "linux")] 25 let counter = { 26 // When debugging rust-analyzer using rr, the perf-related syscalls cause it to abort. 27 // We allow disabling perf by setting the env var `RA_DISABLE_PERF`. 28 29 use once_cell::sync::Lazy; 30 static PERF_ENABLED: Lazy<bool> = 31 Lazy::new(|| std::env::var_os("RA_DISABLE_PERF").is_none()); 32 33 if *PERF_ENABLED { 34 let mut counter = perf_event::Builder::new() 35 .build() 36 .map_err(|err| eprintln!("Failed to create perf counter: {}", err)) 37 .ok(); 38 if let Some(counter) = &mut counter { 39 if let Err(err) = counter.enable() { 40 eprintln!("Failed to start perf counter: {}", err) 41 } 42 } 43 counter 44 } else { 45 None 46 } 47 }; 48 let time = Instant::now(); 49 StopWatch { 50 time, 51 #[cfg(target_os = "linux")] 52 counter, 53 memory: None, 54 } 55 } memory(mut self, yes: bool) -> StopWatch56 pub fn memory(mut self, yes: bool) -> StopWatch { 57 if yes { 58 self.memory = Some(MemoryUsage::now()); 59 } 60 self 61 } elapsed(&mut self) -> StopWatchSpan62 pub fn elapsed(&mut self) -> StopWatchSpan { 63 let time = self.time.elapsed(); 64 65 #[cfg(target_os = "linux")] 66 let instructions = self.counter.as_mut().and_then(|it| { 67 it.read().map_err(|err| eprintln!("Failed to read perf counter: {}", err)).ok() 68 }); 69 #[cfg(not(target_os = "linux"))] 70 let instructions = None; 71 72 let memory = self.memory.map(|it| MemoryUsage::now() - it); 73 StopWatchSpan { time, instructions, memory } 74 } 75 } 76 77 impl fmt::Display for StopWatchSpan { fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result78 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 79 write!(f, "{:.2?}", self.time)?; 80 if let Some(mut instructions) = self.instructions { 81 let mut prefix = ""; 82 if instructions > 10000 { 83 instructions /= 1000; 84 prefix = "k"; 85 } 86 if instructions > 10000 { 87 instructions /= 1000; 88 prefix = "m"; 89 } 90 if instructions > 10000 { 91 instructions /= 1000; 92 prefix = "g"; 93 } 94 write!(f, ", {}{}instr", instructions, prefix)?; 95 } 96 if let Some(memory) = self.memory { 97 write!(f, ", {}", memory)?; 98 } 99 Ok(()) 100 } 101 } 102