xuko_misc/profiling.rs
1//! Profiling
2
3use std::time::{Duration, SystemTime, SystemTimeError};
4
5/// The [`Profile`] structure stores how long it takes to do a task.
6///
7/// # Examples
8///
9/// ```
10/// use std::{time::Duration, thread::sleep};
11/// use xuko_misc::profiling::Profile;
12///
13/// let mut profile = Profile::new("sleep");
14/// sleep(Duration::from_millis(100));
15///
16/// let elapsed = profile.elapsed().unwrap();
17/// assert!(elapsed >= 100);
18///
19/// sleep(Duration::from_millis(100));
20/// // the result is saved!
21/// assert_eq!(profile.elapsed().unwrap(), elapsed);
22/// ```
23pub struct Profile {
24 name: String,
25 start: SystemTime,
26
27 time_taken: Option<Duration>,
28}
29
30impl Profile {
31 /// Create a new [`Profile`]
32 pub fn new<S: AsRef<str>>(name: S) -> Self {
33 Self {
34 name: name.as_ref().to_owned(),
35 start: SystemTime::now(),
36 time_taken: None,
37 }
38 }
39
40 /// Same as [`Self::elapsed_duration`], except maps the result with [`Duration::as_millis`]
41 pub fn elapsed(&mut self) -> Result<u128, SystemTimeError> {
42 self.elapsed_duration().map(|d| d.as_millis())
43 }
44
45 /// Return how much time has passed since the start of this [`Profile`] as a [`Duration`]
46 ///
47 /// This can only fail once, as the result will be saved in the [`Profile`] structure.
48 pub fn elapsed_duration(&mut self) -> Result<Duration, SystemTimeError> {
49 if let Some(d) = self.time_taken {
50 Ok(d)
51 } else {
52 let d = self.start.elapsed()?;
53 self.time_taken = Some(d);
54 Ok(d)
55 }
56 }
57
58 /// Get the name of this [`Profile`]
59 pub fn name(&self) -> &str {
60 &self.name
61 }
62}
63
64#[cfg(test)]
65mod tests {
66 use super::*;
67
68 #[test]
69 fn elapsed_millis() {
70 let mut profile = Profile::new("fake network request");
71
72 std::thread::sleep(Duration::from_secs_f64(0.25));
73
74 let elapsed = profile.elapsed().expect("time went backwards!");
75 assert!(elapsed >= 250);
76 println!("`{}` took {elapsed} milliseconds", profile.name());
77 }
78}