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}