alloc_test/perf/
measure.rs1#[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}