1use crate::output;
2use crate::output::{
3 fallback_to_anonymous_on_invalid_label, ComparedStdout, LabeledOutput, Output, SimpleStdout,
4};
5use std::time::{Duration, Instant};
6
7pub fn run_timed<T, F: FnMut() -> T>(mut closure: F) -> Duration {
15 let start = Instant::now();
16 (closure)();
17 Instant::now().duration_since(start)
18}
19
20pub fn run_timed_times<T, F: FnMut() -> T>(iterations: usize, mut closure: F) -> TimingData {
28 let mut elapsed = Duration::ZERO;
29 let mut min_nanos = u128::MAX;
30 let mut max_nanos = 0;
31 for _ in 0..iterations {
32 let start = Instant::now();
33 closure();
34 let run_elapsed = Instant::now().duration_since(start);
35 let run_elapsed_nanos = run_elapsed.as_nanos();
36 if run_elapsed_nanos < min_nanos {
37 min_nanos = run_elapsed_nanos;
38 }
39 if run_elapsed_nanos > max_nanos {
40 max_nanos = run_elapsed_nanos;
41 }
42 elapsed += run_elapsed;
43 }
44 TimingData {
45 iterations: iterations as u128,
46 min_nanos,
47 max_nanos,
48 elapsed: elapsed.as_nanos(),
49 }
50}
51
52pub fn run_timed_from_iterator<T, R, F: FnMut(R) -> T, It>(
68 iterator: It,
69 mut closure: F,
70) -> TimingData
71where
72 It: Iterator<Item = R>,
73{
74 let mut elapsed = Duration::ZERO;
75 let mut min_nanos = u128::MAX;
76 let mut max_nanos = 0;
77 let mut iterations = 0;
78 for v in iterator {
79 let start = Instant::now();
80 closure(v);
81 let run_elapsed = Instant::now().duration_since(start);
82 let run_elapsed_nanos = run_elapsed.as_nanos();
83 if run_elapsed_nanos < min_nanos {
84 min_nanos = run_elapsed_nanos;
85 }
86 if run_elapsed_nanos > max_nanos {
87 max_nanos = run_elapsed_nanos;
88 }
89 elapsed += run_elapsed;
90 iterations += 1;
91 }
92 TimingData {
93 iterations,
94 min_nanos,
95 max_nanos,
96 elapsed: elapsed.as_nanos(),
97 }
98}
99
100#[derive(Copy, Clone, Debug)]
102#[cfg(feature = "timer")]
103#[cfg_attr(test, derive(Eq, PartialEq))]
104pub struct TimingData {
105 pub min_nanos: u128,
107 pub max_nanos: u128,
109 pub elapsed: u128,
111 pub iterations: u128,
113}
114
115#[cfg(feature = "timer")]
116impl TimingData {
117 pub fn pretty_print(&self) {
119 output::print_timer_header("anonymous", self);
120 output::timer_print_elapsed(
121 self.min_nanos as f64,
122 self.elapsed as f64 / self.iterations as f64,
123 self.max_nanos as f64,
124 );
125 }
126}
127
128pub trait Timeable<It, T>: Sized
130where
131 It: Iterator<Item = T>,
132{
133 fn timed(self) -> TimedIterator<It, T, SimpleStdout> {
143 self.timed_labeled("anonymous")
144 }
145
146 fn timed_labeled(self, label: &'static str) -> TimedIterator<It, T, SimpleStdout>;
156
157 fn timed_persisted(self) -> TimedIterator<It, T, ComparedStdout> {
160 self.timed_persisted_labeled("anonymous")
161 }
162
163 fn timed_persisted_labeled(self, label: &'static str) -> TimedIterator<It, T, ComparedStdout>;
165}
166
167impl<It, T> Timeable<It, T> for It
168where
169 It: Iterator<Item = T>,
170{
171 fn timed_labeled(self, label: &'static str) -> TimedIterator<It, T, SimpleStdout> {
172 TimedIterator::new(
173 self,
174 LabeledOutput::new(fallback_to_anonymous_on_invalid_label(label), SimpleStdout),
175 )
176 }
177
178 fn timed_persisted_labeled(self, label: &'static str) -> TimedIterator<It, T, ComparedStdout> {
179 TimedIterator::new(
180 self,
181 LabeledOutput::new(
182 fallback_to_anonymous_on_invalid_label(label),
183 ComparedStdout,
184 ),
185 )
186 }
187}
188
189pub struct TimedIterator<It, T, O>
191where
192 It: Iterator<Item = T>,
193{
194 inner: It,
195 iterations: u128,
196 min_nanos: u128,
197 max_nanos: u128,
198 elapsed: Duration,
199 out: LabeledOutput<O>,
200}
201
202impl<It, T, O> TimedIterator<It, T, O>
203where
204 It: Iterator<Item = T>,
205{
206 fn new(inner: It, out: LabeledOutput<O>) -> Self {
207 TimedIterator {
208 inner,
209 iterations: 0,
210 min_nanos: u128::MAX,
211 max_nanos: 0,
212 elapsed: Duration::ZERO,
213 out,
214 }
215 }
216}
217
218impl<It, T, O> Iterator for TimedIterator<It, T, O>
219where
220 It: Iterator<Item = T>,
221 O: Output,
222{
223 type Item = T;
224
225 fn next(&mut self) -> Option<Self::Item> {
226 let start = Instant::now();
227 let maybe_item = self.inner.next();
228 let run_elapsed = Instant::now().duration_since(start);
229 if let Some(item) = maybe_item {
230 let run_elapsed_nanos = run_elapsed.as_nanos();
231 if run_elapsed_nanos < self.min_nanos {
232 self.min_nanos = run_elapsed_nanos;
233 }
234 if run_elapsed_nanos > self.max_nanos {
235 self.max_nanos = run_elapsed_nanos;
236 }
237 self.elapsed += run_elapsed;
238 self.iterations += 1;
239 Some(item)
240 } else {
241 self.out.dump(TimingData {
242 min_nanos: self.min_nanos,
243 max_nanos: self.max_nanos,
244 elapsed: self.elapsed.as_nanos(),
245 iterations: self.iterations,
246 });
247 None
248 }
249 }
250}
251
252#[cfg(test)]
253#[cfg(feature = "timer")]
254mod tests {
255 use crate::timing::Timeable;
256
257 #[test]
258 fn time_iterator() {
259 let _v: Vec<i32> = (0..100).timed().chain(0..10_000).timed().collect();
260 }
261
262 #[test]
263 fn time_persisted_iterator() {
264 for _ in 0..2 {
265 let _v: Vec<i32> = (0..1_000_000).timed_persisted().collect();
266 }
267 }
268
269 #[test]
270 fn time_persisted_labled() {
271 for _ in 0..2 {
272 let _v: Vec<i32> = (0..1_000_000).timed_persisted_labeled("my_test").collect();
273 }
274 }
275}