1use howlong::*;
2use std::fmt::{self, Display};
3use std::fs::File;
4use std::io;
5use std::io::{BufWriter, Write};
6use std::time::Instant;
7
8use rayon::prelude::*;
9use termion::{color, style};
10
11#[derive(Debug, Clone)]
12pub struct BenchDuration {}
13
14#[derive(Debug, Clone)]
15pub struct BenchVec {
16 pub inner: Vec<Duration>,
17}
18
19impl BenchVec {
22 pub fn new() -> Self {
24 Self { inner: Vec::new() }
25 }
26
27 pub fn from_vec(vec: &Vec<Duration>) -> Self {
29 Self { inner: vec.clone() }
30 }
31
32 pub fn push(&mut self, item: Duration) -> &mut Self {
34 self.inner.push(item);
35
36 self
37 }
38
39 pub fn append(&mut self, other: Self) -> &mut Self {
41 self.inner.append(&mut other.inner.clone());
42
43 self
44 }
45
46 pub fn len(&self) -> usize {
48 self.inner.len()
49 }
50
51 pub fn sum(&self) -> Duration {
53 self.inner.par_iter().sum::<Duration>()
54 }
55
56 pub fn average(&self) -> Duration {
58 self.sum() / self.inner.len() as u32
59 }
60
61 pub fn standard_deviation(&self) -> f64 {
63 (self.sum().as_nanos() as f64 / (self.len() as f64 - 1f64)).sqrt()
64 }
65
66 pub fn compare(&self, other: Self) -> DurationDifference {
68 let avg1 = self.average();
69 let avg2 = other.average();
70 if avg1 > avg2 {
71 DurationDifference {
72 inner: avg1 - avg2,
73 positive: true,
74 }
75 } else {
76 DurationDifference {
77 inner: avg2 - avg1,
78 positive: false,
79 }
80 }
81 }
82}
83
84impl Display for BenchVec {
85 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
86 let avg_duration = self.average();
87 let standard_deviation = self.standard_deviation();
88 write!(
89 f,
90 "{:?} (±{:.2}ns ~ {:.2}%)",
91 avg_duration,
92 standard_deviation,
93 (standard_deviation / avg_duration.as_nanos() as f64) * 100f64
94 )
95 }
96}
97
98#[derive(Debug, Clone)]
99pub struct DurationDifference {
100 pub inner: Duration,
101 pub positive: bool,
102}
103
104impl DurationDifference {
105 pub fn new(left: &BenchVec, right: &BenchVec) -> Self {
106 let left_avg = left.average();
107 let right_avg = right.average();
108 if left_avg > right_avg {
109 Self {
110 inner: left_avg - right_avg,
111 positive: true,
112 }
113 } else {
114 Self {
115 inner: right_avg - left_avg,
116 positive: false,
117 }
118 }
119 }
120}
121
122impl Display for DurationDifference {
123 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
124 write!(
125 f,
126 "{}{:?}",
127 if self.positive { "+" } else { "-" },
128 self.inner
129 )
130 }
131}
132
133pub struct Bencher {
134 measurements: Vec<BenchVec>,
135 iterations: usize,
136 max_auto_iterations: usize,
137 bench_duration: Duration,
138 writer: Option<BufWriter<File>>,
139}
140
141pub const BENCH_FILE_HEAD: &str = "name\tduration\tstandard_deviation\n";
142
143impl Bencher {
144 pub fn new() -> Self {
145 Self {
146 bench_duration: Self::calculate_bench_duration(),
147 measurements: Vec::new(),
148 iterations: 100,
149 max_auto_iterations: 10000,
150 writer: None,
151 }
152 }
153
154 fn calculate_bench_duration() -> Duration {
156 let mut durations = BenchVec::new();
157 for _ in 0..1000 {
158 let start = HighResolutionClock::now();
159 durations.push(HighResolutionClock::now() - start);
160 }
161
162 durations.average()
163 }
164
165 pub fn set_iterations(&mut self, iterations: usize) -> &mut Self {
168 self.iterations = iterations;
169
170 self
171 }
172
173 pub fn set_max_iterations(&mut self, iterations: usize) -> &mut Self {
174 self.max_auto_iterations = iterations;
175
176 self
177 }
178
179 pub fn bench<T, F: FnMut() -> T>(&mut self, name: &str, mut func: F) -> &mut Self {
182 let mut durations = BenchVec::new();
183 println!(
184 "\n{}{}{}{}",
185 color::Fg(color::LightBlue),
186 style::Bold,
187 name,
188 style::Reset
189 );
190 if self.iterations == 0 {
191 let mut count = 0;
192 while count < self.max_auto_iterations {
193 let start = Instant::now();
194 func();
195 let duration = start.elapsed();
196 if duration > self.bench_duration {
197 durations.push(duration - self.bench_duration);
198 } else {
199 durations.push(duration);
200 }
201 if (durations.standard_deviation() / durations.average().as_nanos() as f64) < 0.01
202 && count > 1
203 {
204 break;
205 }
206 count += 1;
207 }
208 println!("{}After {} iterations{}", style::Faint, count, style::Reset);
209 } else {
210 for _ in 0..self.iterations {
211 let start = Instant::now();
212 func();
213 let duration = start.elapsed();
214 if duration > self.bench_duration {
215 durations.push(duration - self.bench_duration);
216 } else {
217 durations.push(duration);
218 }
219 }
220 }
221 println!("Result: {}", durations);
222 if let Some(writer) = &mut self.writer {
223 let _ = writer.write(
224 format!(
225 "{}\t{:?}\t{:.2}ns\n",
226 name,
227 durations.average(),
228 durations.standard_deviation()
229 )
230 .as_bytes(),
231 );
232 }
233 self.measurements.push(durations);
234
235 self
236 }
237
238 pub fn compare(&mut self) -> &mut Self {
241 if self.measurements.len() > 1 {
242 let left = self.measurements.get(self.measurements.len() - 1).unwrap();
243 let right = self.measurements.get(self.measurements.len() - 2).unwrap();
244 let diff = DurationDifference::new(left, right);
245 println!("Difference: {}", diff);
246 }
247
248 self
249 }
250
251 pub fn print_settings(&mut self) -> &mut Self {
253 println!(
254 "\n{}{}Benchmarking Settings{}",
255 color::Fg(color::Green),
256 style::Underline,
257 style::Reset
258 );
259 println!("Benchmarking accuracy delay:\t {:?}", self.bench_duration);
260 println!(
261 "Number of iterations:\t {}",
262 if self.iterations > 0 {
263 self.iterations.to_string()
264 } else {
265 "auto".to_string()
266 }
267 );
268 if self.iterations == 0 {
269 println!("Maximum number of iterations: {}", self.max_auto_iterations)
270 }
271
272 self
273 }
274
275 pub fn write_output_to(&mut self, mut writer: BufWriter<File>) -> &mut Self {
277 writer.write(BENCH_FILE_HEAD.as_bytes()).unwrap();
278 self.writer = Some(writer);
279
280 self
281 }
282
283 pub fn flush(&mut self) -> io::Result<()> {
284 if let Some(writer) = &mut self.writer {
285 writer.flush()
286 } else {
287 Ok(())
288 }
289 }
290}