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(
15 target_os = "linux",
16 not(target_env = "ohos"),
17 any(target_arch = "x86", target_arch = "x86_64", target_arch = "aarch64")
18 ))]
19 counter: Option<perf_event::Counter>,
20 memory: MemoryUsage,
21}
22
23pub struct StopWatchSpan {
24 pub time: Duration,
25 pub instructions: Option<u64>,
26 pub memory: MemoryUsage,
27}
28
29impl StopWatch {
30 pub fn start() -> StopWatch {
31 #[cfg(all(
32 target_os = "linux",
33 not(target_env = "ohos"),
34 any(target_arch = "x86", target_arch = "x86_64", target_arch = "aarch64")
35 ))]
36 let counter = {
37 use std::sync::OnceLock;
41 static PERF_ENABLED: OnceLock<bool> = OnceLock::new();
42
43 if *PERF_ENABLED.get_or_init(|| std::env::var_os("RA_DISABLE_PERF").is_none()) {
44 let mut counter = perf_event::Builder::new()
45 .build()
46 .map_err(|err| eprintln!("Failed to create perf counter: {err}"))
47 .ok();
48 if let Some(counter) = &mut counter
49 && let Err(err) = counter.enable()
50 {
51 eprintln!("Failed to start perf counter: {err}")
52 }
53 counter
54 } else {
55 None
56 }
57 };
58 let memory = MemoryUsage::now();
59 let time = Instant::now();
60 StopWatch {
61 time,
62 #[cfg(all(
63 target_os = "linux",
64 not(target_env = "ohos"),
65 any(target_arch = "x86", target_arch = "x86_64", target_arch = "aarch64")
66 ))]
67 counter,
68 memory,
69 }
70 }
71
72 pub fn elapsed(&mut self) -> StopWatchSpan {
73 let time = self.time.elapsed();
74
75 #[cfg(all(
76 target_os = "linux",
77 not(target_env = "ohos"),
78 any(target_arch = "x86", target_arch = "x86_64", target_arch = "aarch64")
79 ))]
80 let instructions = self.counter.as_mut().and_then(|it| {
81 it.read().map_err(|err| eprintln!("Failed to read perf counter: {err}")).ok()
82 });
83 #[cfg(not(all(
84 target_os = "linux",
85 not(target_env = "ohos"),
86 any(target_arch = "x86", target_arch = "x86_64", target_arch = "aarch64")
87 )))]
88 let instructions = None;
89
90 let memory = MemoryUsage::now() - self.memory;
91 StopWatchSpan { time, instructions, memory }
92 }
93}
94
95impl fmt::Display for StopWatchSpan {
96 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
97 write!(f, "{:.2?}", self.time)?;
98 if let Some(mut instructions) = self.instructions {
99 let mut prefix = "";
100 if instructions > 10000 {
101 instructions /= 1000;
102 prefix = "k";
103 }
104 if instructions > 10000 {
105 instructions /= 1000;
106 prefix = "m";
107 }
108 if instructions > 10000 {
109 instructions /= 1000;
110 prefix = "g";
111 }
112 write!(f, ", {instructions}{prefix}instr")?;
113 }
114 write!(f, ", {}", self.memory)?;
115 Ok(())
116 }
117}