1#![allow(clippy::print_stderr)]
4
5use std::{
6 fmt,
7 time::{Duration, Instant},
8};
9
10use crate::MemoryUsage;
11
12pub struct StopWatch {
13 time: Instant,
14 #[cfg(all(target_os = "linux", not(target_env = "ohos")))]
15 counter: Option<perf_event::Counter>,
16 memory: MemoryUsage,
17}
18
19pub struct StopWatchSpan {
20 pub time: Duration,
21 pub instructions: Option<u64>,
22 pub memory: MemoryUsage,
23}
24
25impl StopWatch {
26 pub fn start() -> StopWatch {
27 #[cfg(all(target_os = "linux", not(target_env = "ohos")))]
28 let counter = {
29 use std::sync::OnceLock;
33 static PERF_ENABLED: OnceLock<bool> = OnceLock::new();
34
35 if *PERF_ENABLED.get_or_init(|| std::env::var_os("RA_DISABLE_PERF").is_none()) {
36 let mut counter = perf_event::Builder::new()
37 .build()
38 .map_err(|err| eprintln!("Failed to create perf counter: {err}"))
39 .ok();
40 if let Some(counter) = &mut counter
41 && let Err(err) = counter.enable()
42 {
43 eprintln!("Failed to start perf counter: {err}")
44 }
45 counter
46 } else {
47 None
48 }
49 };
50 let memory = MemoryUsage::now();
51 let time = Instant::now();
52 StopWatch {
53 time,
54 #[cfg(all(target_os = "linux", not(target_env = "ohos")))]
55 counter,
56 memory,
57 }
58 }
59
60 pub fn elapsed(&mut self) -> StopWatchSpan {
61 let time = self.time.elapsed();
62
63 #[cfg(all(target_os = "linux", not(target_env = "ohos")))]
64 let instructions = self.counter.as_mut().and_then(|it| {
65 it.read().map_err(|err| eprintln!("Failed to read perf counter: {err}")).ok()
66 });
67 #[cfg(all(target_os = "linux", target_env = "ohos"))]
68 let instructions = None;
69 #[cfg(not(target_os = "linux"))]
70 let instructions = None;
71
72 let memory = MemoryUsage::now() - self.memory;
73 StopWatchSpan { time, instructions, memory }
74 }
75}
76
77impl fmt::Display for StopWatchSpan {
78 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, ", {instructions}{prefix}instr")?;
95 }
96 write!(f, ", {}", self.memory)?;
97 Ok(())
98 }
99}