criterion/
routine.rs
1use benchmark::BenchmarkConfig;
2use std::time::{Duration, Instant};
3
4use program::Program;
5use report::{BenchmarkId, ReportContext};
6use std::marker::PhantomData;
7use {Bencher, Criterion, DurationExt};
8
9pub trait Routine<T> {
11 fn start(&mut self, parameter: &T) -> Option<Program>;
12
13 fn bench(&mut self, m: &mut Option<Program>, iters: &[u64], parameter: &T) -> Vec<f64>;
15 fn warm_up(&mut self, m: &mut Option<Program>, how_long: Duration, parameter: &T)
17 -> (u64, u64);
18
19 fn test(&mut self, parameter: &T) {
21 let mut m = self.start(parameter);
22 self.bench(&mut m, &[1u64], parameter);
23 }
24
25 fn profile(
31 &mut self,
32 id: &BenchmarkId,
33 criterion: &Criterion,
34 report_context: &ReportContext,
35 time: Duration,
36 parameter: &T,
37 ) {
38 criterion
39 .report
40 .profile(id, report_context, time.to_nanos() as f64);
41
42 let time = time.to_nanos();
43 let mut m = self.start(parameter);
44
45 let (wu_elapsed, wu_iters) = self.warm_up(&mut m, Duration::from_secs(1), parameter);
47 if wu_elapsed >= time {
48 return;
49 }
50
51 let met = wu_elapsed as f64 / wu_iters as f64;
53
54 let remaining = (time - wu_elapsed) as f64;
56
57 let iters = remaining / met;
58 let iters = iters as u64;
59
60 self.bench(&mut m, &[iters], parameter);
61
62 criterion.report.terminated(id, report_context);
63 }
64
65 fn sample(
66 &mut self,
67 id: &BenchmarkId,
68 config: &BenchmarkConfig,
69 criterion: &Criterion,
70 report_context: &ReportContext,
71 parameter: &T,
72 ) -> (Box<[f64]>, Box<[f64]>) {
73 let wu = config.warm_up_time;
74 let m_ns = config.measurement_time.to_nanos();
75
76 criterion
77 .report
78 .warmup(id, report_context, wu.to_nanos() as f64);
79
80 let mut m = self.start(parameter);
81
82 let (wu_elapsed, wu_iters) = self.warm_up(&mut m, wu, parameter);
83
84 let met = wu_elapsed as f64 / wu_iters as f64;
86
87 let n = config.sample_size as u64;
88 let total_runs = n * (n + 1) / 2;
90 let d = (m_ns as f64 / met / total_runs as f64).ceil() as u64;
91
92 let m_iters = (1..(n + 1) as u64).map(|a| a * d).collect::<Vec<u64>>();
93
94 let m_ns = total_runs as f64 * d as f64 * met;
95 criterion
96 .report
97 .measurement_start(id, report_context, n, m_ns, m_iters.iter().sum());
98 let m_elapsed = self.bench(&mut m, &m_iters, parameter);
99
100 let m_iters_f: Vec<f64> = m_iters.iter().map(|&x| x as f64).collect();
101
102 (m_iters_f.into_boxed_slice(), m_elapsed.into_boxed_slice())
103 }
104}
105
106pub struct Function<F, T>
107where
108 F: FnMut(&mut Bencher, &T),
109{
110 f: F,
111 _phantom: PhantomData<T>,
112}
113impl<F, T> Function<F, T>
114where
115 F: FnMut(&mut Bencher, &T),
116{
117 pub fn new(f: F) -> Function<F, T> {
118 Function {
119 f,
120 _phantom: PhantomData,
121 }
122 }
123}
124
125impl<F, T> Routine<T> for Function<F, T>
126where
127 F: FnMut(&mut Bencher, &T),
128{
129 fn start(&mut self, _: &T) -> Option<Program> {
130 None
131 }
132
133 fn bench(&mut self, _: &mut Option<Program>, iters: &[u64], parameter: &T) -> Vec<f64> {
134 let f = &mut self.f;
135
136 let mut b = Bencher {
137 iterated: false,
138 iters: 0,
139 elapsed: Duration::from_secs(0),
140 };
141
142 iters
143 .iter()
144 .map(|iters| {
145 b.iters = *iters;
146 (*f)(&mut b, parameter);
147 b.assert_iterated();
148 b.elapsed.to_nanos() as f64
149 })
150 .collect()
151 }
152
153 fn warm_up(
154 &mut self,
155 _: &mut Option<Program>,
156 how_long: Duration,
157 parameter: &T,
158 ) -> (u64, u64) {
159 let f = &mut self.f;
160 let mut b = Bencher {
161 iterated: false,
162 iters: 1,
163 elapsed: Duration::from_secs(0),
164 };
165
166 let mut total_iters = 0;
167 let start = Instant::now();
168 loop {
169 (*f)(&mut b, parameter);
170
171 b.assert_iterated();
172
173 total_iters += b.iters;
174 let elapsed = start.elapsed();
175 if elapsed > how_long {
176 return (elapsed.to_nanos(), total_iters);
177 }
178
179 b.iters *= 2;
180 }
181 }
182}