1use crate::stats::{winsorize, Summary};
2use std::cmp::max;
3use std::hint::black_box;
4use std::time::{Duration, Instant};
5
6pub struct Bencher {
7    summary: Option<Summary>,
8    pub bytes: u64,
9}
10
11impl Bencher {
12    pub(crate) fn new() -> Self {
13        Self {
14            summary: None,
15            bytes: 0,
16        }
17    }
18
19    pub fn iter<T, F>(&mut self, mut inner: F)
21    where
22        F: FnMut() -> T,
23    {
24        self.summary = Some(iter(&mut inner));
25    }
26
27    pub(crate) fn summary(&self) -> Option<Summary> {
28        self.summary
29    }
30}
31
32#[cfg(feature = "tokio")]
33pub struct AsyncBencher {
34    summary: Option<Summary>,
35    pub bytes: u64,
36}
37
38#[cfg(feature = "tokio")]
39impl AsyncBencher {
40    pub(crate) fn new() -> Self {
41        Self {
42            summary: None,
43            bytes: 0,
44        }
45    }
46
47    pub async fn iter<T, F>(&mut self, mut inner: F)
49    where
50        F: FnMut() -> std::pin::Pin<Box<dyn std::future::Future<Output = T> + Send>>
51            + Send
52            + Sync
53            + 'static,
54    {
55        self.summary = Some(async_iter(&mut inner).await);
56    }
57
58    pub(crate) fn summary(&self) -> Option<Summary> {
59        self.summary
60    }
61}
62
63fn ns_iter_inner<T, F>(inner: &mut F, k: u64) -> u64
64where
65    F: FnMut() -> T,
66{
67    let start = Instant::now();
68    for _ in 0..k {
69        black_box(inner());
70    }
71    start.elapsed().as_nanos() as u64
72}
73
74#[cfg(feature = "tokio")]
75async fn async_ns_iter_inner<T, F>(inner: &mut F, k: u64) -> u64
76where
77    F: FnMut() -> std::pin::Pin<Box<dyn std::future::Future<Output = T> + Send>>
78        + Send
79        + Sync
80        + 'static,
81{
82    let start = tokio::time::Instant::now();
83    for _ in 0..k {
84        black_box(inner().await);
85    }
86    start.elapsed().as_nanos() as u64
87}
88
89pub fn iter<T, F>(inner: &mut F) -> Summary
91where
92    F: FnMut() -> T,
93{
94    let ns_single = ns_iter_inner(inner, 1);
96
97    let ns_target_total = 1_000_000; let mut n = ns_target_total / max(1, ns_single);
101
102    n = max(1, n);
108
109    let mut total_run = Duration::new(0, 0);
110    let samples: &mut [f64] = &mut [0.0_f64; 50];
111    loop {
112        let loop_start = Instant::now();
113
114        for p in &mut *samples {
115            *p = ns_iter_inner(inner, n) as f64 / n as f64;
116        }
117
118        winsorize(samples, 5.0);
119        let summ = Summary::new(samples);
120
121        for p in &mut *samples {
122            let ns = ns_iter_inner(inner, 5 * n);
123            *p = ns as f64 / (5 * n) as f64;
124        }
125
126        winsorize(samples, 5.0);
127        let summ5 = Summary::new(samples);
128
129        let loop_run = loop_start.elapsed();
130
131        if loop_run > Duration::from_millis(100)
134            && summ.median_abs_dev_pct < 1.0
135            && summ.median - summ5.median < summ5.median_abs_dev
136        {
137            return summ5;
138        }
139
140        total_run += loop_run;
141        if total_run > Duration::from_secs(3) {
143            return summ5;
144        }
145
146        n = match n.checked_mul(10) {
151            Some(_) => n * 2,
152            None => {
153                return summ5;
154            }
155        };
156    }
157}
158
159#[cfg(feature = "tokio")]
160pub async fn async_iter<T, F>(inner: &mut F) -> Summary
161where
162    F: FnMut() -> std::pin::Pin<Box<dyn std::future::Future<Output = T> + Send>>
163        + Send
164        + Sync
165        + 'static,
166{
167    let ns_single = async_ns_iter_inner(inner, 1).await;
169
170    let ns_target_total = 1_000_000; let mut n = ns_target_total / max(1, ns_single);
174
175    n = max(1, n);
181
182    let mut total_run = Duration::new(0, 0);
183    let samples: &mut [f64] = &mut [0.0_f64; 50];
184    loop {
185        let loop_start = tokio::time::Instant::now();
186
187        for p in &mut *samples {
188            *p = async_ns_iter_inner(inner, n).await as f64 / n as f64;
189        }
190
191        winsorize(samples, 5.0);
192        let summ = Summary::new(samples);
193
194        for p in &mut *samples {
195            let ns = async_ns_iter_inner(inner, 5 * n).await;
196            *p = ns as f64 / (5 * n) as f64;
197        }
198
199        winsorize(samples, 5.0);
200        let summ5 = Summary::new(samples);
201
202        let loop_run = loop_start.elapsed();
203
204        if loop_run > Duration::from_millis(100)
207            && summ.median_abs_dev_pct < 1.0
208            && summ.median - summ5.median < summ5.median_abs_dev
209        {
210            return summ5;
211        }
212
213        total_run += loop_run;
214        if total_run > Duration::from_secs(3) {
216            return summ5;
217        }
218
219        n = match n.checked_mul(10) {
224            Some(_) => n * 2,
225            None => {
226                return summ5;
227            }
228        };
229    }
230}