performance_mark/
performance.rs

1use 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]
9/// The [Performance] struct groups all the [PerformanceMark] and [PerformancePeriod] objects
10/// created with the event methods.
11pub struct Performance {
12  events: Vec<PerformanceMark>,
13  periods: HashMap<String, PerformancePeriod>,
14}
15
16impl Performance {
17  /// Create a new [Performance] object.
18  pub fn new() -> Self {
19    Self::default()
20  }
21
22  /// Create a new [PerformanceMark] indicating a point in time.
23  pub fn mark<T: Into<String>>(&mut self, label: T) {
24    self.events.push(PerformanceMark::new(label.into()));
25  }
26
27  /// Mark the start of a new [PerformancePeriod].
28  pub fn start<T: Into<String>>(&mut self, label: T) {
29    self.periods.insert(label.into(), PerformancePeriod::new());
30  }
31
32  /// Mark the end of an existing [PerformancePeriod].
33  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  /// Get the map of [PerformancePeriod]s and their labels.
40  #[must_use]
41  pub const fn periods(&self) -> &HashMap<String, PerformancePeriod> {
42    &self.periods
43  }
44
45  /// Get the list of [PerformanceMark]s.
46  #[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]
55/// A [PerformanceMark] records a point in time.
56pub 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  /// Create a new [PerformanceMark].
64  pub fn new(label: String) -> Self {
65    Self {
66      label,
67      instant: Instant::now(),
68    }
69  }
70
71  /// Get the [Instant] the event was marked.
72  #[must_use]
73  pub const fn instant(&self) -> Instant {
74    self.instant
75  }
76
77  /// Get the [PerformanceMark]'s label.
78  #[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]
93/// A [PerformanceMark] records a start and end point.
94pub 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  /// Create a new [PerformancePeriod] starting now.
110  pub fn new() -> Self {
111    Self {
112      start: Instant::now(),
113      end: None,
114      duration: Duration::new(0, 0),
115    }
116  }
117
118  /// Mark the end of a [PerformancePeriod]
119  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  /// Get the duration of a [PerformancePeriod]. If the end has not been marked, then this
127  /// returns the duration since the [PerformancePeriod] started.
128  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}