alloc_test/perf/
measure.rs

1#[cfg(not(target_arch = "wasm32"))]
2pub type Instant = std::time::Instant;
3
4#[cfg(target_arch = "wasm32")]
5pub type Instant = wasm_instant::Instant;
6
7#[cfg(target_arch = "wasm32")]
8pub mod wasm_instant {
9    use wasm_bindgen::prelude::wasm_bindgen;
10
11    #[wasm_bindgen(inline_js = r#"
12export function performance_now() {
13  return performance.now();
14}"#)]
15    extern "C" {
16        fn performance_now() -> f64;
17    }
18
19    #[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
20    pub struct Instant(u64);
21
22    impl Instant {
23        pub fn now() -> Self {
24            Self((performance_now() * 1000.0) as u64)
25        }
26        pub fn duration_since(&self, earlier: Instant) -> Duration {
27            Duration::from_micros(self.0 - earlier.0)
28        }
29        pub fn elapsed(&self) -> Duration {
30            Self::now().duration_since(*self)
31        }
32        pub fn checked_add(&self, duration: Duration) -> Option<Self> {
33            match duration.as_micros().try_into() {
34                Ok(duration) => self.0.checked_add(duration).map(|i| Self(i)),
35                Err(_) => None,
36            }
37        }
38        pub fn checked_sub(&self, duration: Duration) -> Option<Self> {
39            match duration.as_micros().try_into() {
40                Ok(duration) => self.0.checked_sub(duration).map(|i| Self(i)),
41                Err(_) => None,
42            }
43        }
44    }
45
46    use std::{ops::*, time::Duration};
47
48    impl Add<Duration> for Instant {
49        type Output = Instant;
50        fn add(self, other: Duration) -> Instant {
51            self.checked_add(other).unwrap()
52        }
53    }
54    impl Sub<Duration> for Instant {
55        type Output = Instant;
56        fn sub(self, other: Duration) -> Instant {
57            self.checked_sub(other).unwrap()
58        }
59    }
60    impl Sub<Instant> for Instant {
61        type Output = Duration;
62        fn sub(self, other: Instant) -> Duration {
63            self.duration_since(other)
64        }
65    }
66    impl AddAssign<Duration> for Instant {
67        fn add_assign(&mut self, other: Duration) {
68            *self = *self + other;
69        }
70    }
71    impl SubAssign<Duration> for Instant {
72        fn sub_assign(&mut self, other: Duration) {
73            *self = *self - other;
74        }
75    }
76}
77
78#[derive(Debug, Default)]
79struct Stats {
80    n: f64,
81    mean: f64,
82    q: f64,
83}
84
85#[derive(Debug, Default, Display, Serialize, Deserialize)]
86#[display(fmt = "mean = {mean}μs")]
87pub struct PerfStats {
88    pub mean: u64,
89}
90
91impl From<Stats> for PerfStats {
92    fn from(source: Stats) -> Self {
93        let mean = Duration::from_secs_f64(source.mean)
94            .as_micros()
95            .try_into()
96            .unwrap();
97        PerfStats { mean }
98    }
99}
100
101impl std::fmt::Display for Stats {
102    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
103        if f.alternate() {
104            todo!()
105        } else {
106            write!(f, "mean: {}, stdev: {}", self.mean, self.q / self.n)
107        }
108    }
109}
110
111impl Stats {
112    pub fn new() -> Self {
113        Self::default()
114    }
115
116    pub fn update(&mut self, dur: Duration) {
117        let x = dur.as_secs_f64();
118        self.n += 1.;
119        let p = x - self.mean;
120        self.mean += p / self.n;
121        self.q += p * (x - self.mean);
122    }
123}
124
125const ITERS: (usize, usize) = (20, 5);
126
127pub fn bench<O, F: Fn() -> O>(f: F) -> PerfStats {
128    bench_internal(ITERS.0, ITERS.1, &f)
129}
130
131pub fn bench_iters<O, F: Fn() -> O>(iters: usize, f: F) -> PerfStats {
132    bench_internal(iters, iters / 10, &f)
133}
134
135fn bench_internal<O, F: Fn() -> O>(iters: usize, wu_cd_iters: usize, f: &F) -> PerfStats {
136    assert!(iters >= 20, "Number of iterations is too low");
137    assert!(iters / wu_cd_iters > 3, "Warm-up/cool-down is too long");
138    let mut stats = Stats::new();
139    for i in 0..iters {
140        let time = duration_of(f);
141        if i >= wu_cd_iters && i < iters - wu_cd_iters {
142            stats.update(time);
143        }
144    }
145    stats.into()
146}
147
148use std::time::Duration;
149
150use derive_more::Display;
151use serde::{Deserialize, Serialize};
152
153pub fn duration_of<F: Fn() -> O, O>(f: F) -> Duration {
154    let then = Instant::now();
155    let _ = f();
156    Instant::now() - then
157}