performance_mark/
performance.rs1use std::collections::HashMap;
2use std::time::{Duration, Instant};
3
4use crate::error::Error;
5
6#[derive(Debug, Default)]
7#[cfg_attr(feature = "derive_serde", derive(serde::Serialize))]
8#[must_use]
9pub struct Performance {
12 events: Vec<PerformanceMark>,
13 periods: HashMap<String, PerformancePeriod>,
14}
15
16impl Performance {
17 pub fn new() -> Self {
19 Self::default()
20 }
21
22 pub fn mark<T: Into<String>>(&mut self, label: T) {
24 self.events.push(PerformanceMark::new(label.into()));
25 }
26
27 pub fn start<T: Into<String>>(&mut self, label: T) {
29 self.periods.insert(label.into(), PerformancePeriod::new());
30 }
31
32 pub fn end(&mut self, label: &str) -> Result<(), Error> {
34 let period = self.periods.get_mut(label).ok_or(Error::EndBeforeStart)?;
35 period.end();
36 Ok(())
37 }
38
39 #[must_use]
41 pub const fn periods(&self) -> &HashMap<String, PerformancePeriod> {
42 &self.periods
43 }
44
45 #[must_use]
47 pub const fn events(&self) -> &Vec<PerformanceMark> {
48 &self.events
49 }
50}
51
52#[derive(Debug, Clone, PartialEq)]
53#[cfg_attr(feature = "derive_serde", derive(serde::Serialize))]
54#[must_use]
55pub struct PerformanceMark {
57 label: String,
58 #[cfg_attr(feature = "derive_serde", serde(serialize_with = "crate::serde::approx_instant"))]
59 instant: Instant,
60}
61
62impl PerformanceMark {
63 pub fn new(label: String) -> Self {
65 Self {
66 label,
67 instant: Instant::now(),
68 }
69 }
70
71 #[must_use]
73 pub const fn instant(&self) -> Instant {
74 self.instant
75 }
76
77 #[must_use]
79 pub fn label(&self) -> &str {
80 &self.label
81 }
82}
83
84impl PartialOrd for PerformanceMark {
85 fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
86 self.instant.partial_cmp(&other.instant)
87 }
88}
89
90#[derive(Debug, Clone, Copy, PartialEq)]
91#[cfg_attr(feature = "derive_serde", derive(serde::Serialize))]
92#[must_use]
93pub struct PerformancePeriod {
95 #[cfg_attr(feature = "derive_serde", serde(serialize_with = "crate::serde::approx_instant"))]
96 start: Instant,
97 #[cfg_attr(feature = "derive_serde", serde(serialize_with = "crate::serde::approx_opt_instant"))]
98 end: Option<Instant>,
99 duration: Duration,
100}
101
102impl Default for PerformancePeriod {
103 fn default() -> Self {
104 Self::new()
105 }
106}
107
108impl PerformancePeriod {
109 pub fn new() -> Self {
111 Self {
112 start: Instant::now(),
113 end: None,
114 duration: Duration::new(0, 0),
115 }
116 }
117
118 pub fn end(&mut self) {
120 let now = Instant::now();
121 self.end = Some(now);
122 self.duration = now - self.start;
123 }
124
125 #[must_use]
126 pub fn duration(&self) -> Duration {
129 self.end.unwrap_or_else(Instant::now) - self.start
130 }
131}
132
133impl PartialOrd for PerformancePeriod {
134 fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
135 self.duration().partial_cmp(&other.duration())
136 }
137}
138
139#[cfg(test)]
140mod tests {
141 use std::thread::sleep;
142
143 use anyhow::Result;
144
145 use super::*;
146
147 const fn is_sync_send<T>()
148 where
149 T: Send + Sync,
150 {
151 }
152
153 #[test]
154 const fn test_sync_send() {
155 is_sync_send::<Performance>();
156 }
157
158 #[test]
159 fn test() -> Result<()> {
160 let wait = Duration::from_millis(100);
161 let mut perf = Performance::new();
162 perf.mark("start");
163 sleep(wait);
164 perf.start("middle");
165 sleep(wait);
166 perf.end("middle")?;
167 sleep(wait);
168 perf.mark("end");
169
170 println!("{:?}", perf.events);
171 assert_eq!(perf.events.len(), 2);
172 assert!(perf.events[0] < perf.events[1]);
173 assert_eq!(perf.periods.len(), 1);
174 assert!(perf.periods["middle"].duration() >= wait);
175
176 Ok(())
177 }
178
179 #[test]
180 #[cfg(feature = "derive_serde")]
181 fn test_serde() -> Result<()> {
182 let mut perf = Performance::new();
183 perf.mark("start");
184 perf.start("middle");
185 perf.end("middle")?;
186 perf.mark("end");
187 let json = serde_json::to_string_pretty(&perf)?;
188 println!("{}", json);
189 Ok(())
190 }
191}