criterion/
bencher.rs

1#[cfg(feature = "async")]
2use std::future::Future;
3use std::{
4    iter::IntoIterator,
5    time::{Duration, Instant},
6};
7
8#[cfg(feature = "async")]
9use crate::async_executor::AsyncExecutor;
10use crate::{
11    BatchSize, black_box,
12    measurement::{Measurement, WallTime},
13};
14
15// ================================== MAINTENANCE NOTE =============================================
16// Any changes made to either Bencher or AsyncBencher will have to be replicated to the other!
17// ================================== MAINTENANCE NOTE =============================================
18
19/// Timer struct used to iterate a benchmarked function and measure the runtime.
20///
21/// This struct provides different timing loops as methods. Each timing loop provides a different
22/// way to time a routine and each has advantages and disadvantages.
23///
24/// * If you want to do the iteration and measurement yourself (eg. passing the iteration count
25///   to a separate process), use `iter_custom`.
26/// * If your routine requires no per-iteration setup and returns a value with an expensive `drop`
27///   method, use `iter_with_large_drop`.
28/// * If your routine requires some per-iteration setup that shouldn't be timed, use `iter_batched`
29///   or `iter_batched_ref`. See [`BatchSize`](enum.BatchSize.html) for a discussion of batch sizes.
30///   If the setup value implements `Drop` and you don't want to include the `drop` time in the
31///   measurement, use `iter_batched_ref`, otherwise use `iter_batched`. These methods are also
32///   suitable for benchmarking routines which return a value with an expensive `drop` method,
33///   but are more complex than `iter_with_large_drop`.
34/// * Otherwise, use `iter`.
35pub struct Bencher<'a, M: Measurement = WallTime> {
36    pub(crate) iterated: bool,         // Have we iterated this benchmark?
37    pub(crate) iters: u64,             // Number of times to iterate this benchmark
38    pub(crate) value: M::Value,        // The measured value
39    pub(crate) measurement: &'a M,     // Reference to the measurement object
40    pub(crate) elapsed_time: Duration, // How much time did it take to perform the iteration? Used for the warmup period.
41}
42impl<'a, M: Measurement> Bencher<'a, M> {
43    /// Times a `routine` by executing it many times and timing the total elapsed time.
44    ///
45    /// Prefer this timing loop when `routine` returns a value that doesn't have a destructor.
46    ///
47    /// # Timing model
48    ///
49    /// Note that the `Bencher` also times the time required to destroy the output of `routine()`.
50    /// Therefore prefer this timing loop when the runtime of `mem::drop(O)` is negligible compared
51    /// to the runtime of the `routine`.
52    ///
53    /// ```text
54    /// elapsed = Instant::now + iters * (routine + mem::drop(O) + Range::next)
55    /// ```
56    ///
57    /// # Example
58    ///
59    /// ```rust
60    /// use criterion::*;
61    ///
62    /// // The function to benchmark
63    /// fn foo() {
64    ///     // ...
65    /// }
66    ///
67    /// fn bench(c: &mut Criterion) {
68    ///     c.bench_function("iter", move |b| {
69    ///         b.iter(|| foo())
70    ///     });
71    /// }
72    ///
73    /// criterion_group!(benches, bench);
74    /// criterion_main!(benches);
75    /// ```
76    ///
77    #[inline(never)]
78    pub fn iter<O, R>(&mut self, mut routine: R)
79    where
80        R: FnMut() -> O,
81    {
82        self.iterated = true;
83        let time_start = Instant::now();
84        let start = self.measurement.start();
85        for _ in 0..self.iters {
86            black_box(routine());
87        }
88        self.value = self.measurement.end(start);
89        self.elapsed_time = time_start.elapsed();
90    }
91
92    /// Times a `routine` by executing it many times and relying on `routine` to measure its own execution time.
93    ///
94    /// Prefer this timing loop in cases where `routine` has to do its own measurements to
95    /// get accurate timing information (for example in multi-threaded scenarios where you spawn
96    /// and coordinate with multiple threads).
97    ///
98    /// # Timing model
99    /// Custom, the timing model is whatever is returned as the Duration from `routine`.
100    ///
101    /// # Example
102    /// ```rust
103    /// use criterion::*;
104    /// use criterion::black_box;
105    /// use std::time::Instant;
106    ///
107    /// fn foo() {
108    ///     // ...
109    /// }
110    ///
111    /// fn bench(c: &mut Criterion) {
112    ///     c.bench_function("iter", move |b| {
113    ///         b.iter_custom(|iters| {
114    ///             let start = Instant::now();
115    ///             for _i in 0..iters {
116    ///                 black_box(foo());
117    ///             }
118    ///             start.elapsed()
119    ///         })
120    ///     });
121    /// }
122    ///
123    /// criterion_group!(benches, bench);
124    /// criterion_main!(benches);
125    /// ```
126    ///
127    #[inline(never)]
128    pub fn iter_custom<R>(&mut self, mut routine: R)
129    where
130        R: FnMut(u64) -> M::Value,
131    {
132        self.iterated = true;
133        let time_start = Instant::now();
134        self.value = routine(self.iters);
135        self.elapsed_time = time_start.elapsed();
136    }
137
138    #[doc(hidden)]
139    pub fn iter_with_setup<I, O, S, R>(&mut self, setup: S, routine: R)
140    where
141        S: FnMut() -> I,
142        R: FnMut(I) -> O,
143    {
144        self.iter_batched(setup, routine, BatchSize::PerIteration);
145    }
146
147    /// Times a `routine` by collecting its output on each iteration. This avoids timing the
148    /// destructor of the value returned by `routine`.
149    ///
150    /// WARNING: This requires `O(iters * mem::size_of::<O>())` of memory, and `iters` is not under the
151    /// control of the caller. If this causes out-of-memory errors, use `iter_batched` instead.
152    ///
153    /// # Timing model
154    ///
155    /// ``` text
156    /// elapsed = Instant::now + iters * (routine) + Iterator::collect::<Vec<_>>
157    /// ```
158    ///
159    /// # Example
160    ///
161    /// ```rust
162    /// use criterion::*;
163    ///
164    /// fn create_vector() -> Vec<u64> {
165    ///     # vec![]
166    ///     // ...
167    /// }
168    ///
169    /// fn bench(c: &mut Criterion) {
170    ///     c.bench_function("with_drop", move |b| {
171    ///         // This will avoid timing the Vec::drop.
172    ///         b.iter_with_large_drop(|| create_vector())
173    ///     });
174    /// }
175    ///
176    /// criterion_group!(benches, bench);
177    /// criterion_main!(benches);
178    /// ```
179    ///
180    pub fn iter_with_large_drop<O, R>(&mut self, mut routine: R)
181    where
182        R: FnMut() -> O,
183    {
184        self.iter_batched(|| (), |_| routine(), BatchSize::SmallInput);
185    }
186
187    /// Times a `routine` that requires some input by generating a batch of input, then timing the
188    /// iteration of the benchmark over the input. See [`BatchSize`](enum.BatchSize.html) for
189    /// details on choosing the batch size. Use this when the routine must consume its input.
190    ///
191    /// For example, use this loop to benchmark sorting algorithms, because they require unsorted
192    /// data on each iteration.
193    ///
194    /// # Timing model
195    ///
196    /// ```text
197    /// elapsed = (Instant::now * num_batches) + (iters * (routine + O::drop)) + Vec::extend
198    /// ```
199    ///
200    /// # Example
201    ///
202    /// ```rust
203    /// use criterion::*;
204    ///
205    /// fn create_scrambled_data() -> Vec<u64> {
206    ///     # vec![]
207    ///     // ...
208    /// }
209    ///
210    /// // The sorting algorithm to test
211    /// fn sort(data: &mut [u64]) {
212    ///     // ...
213    /// }
214    ///
215    /// fn bench(c: &mut Criterion) {
216    ///     let data = create_scrambled_data();
217    ///
218    ///     c.bench_function("with_setup", move |b| {
219    ///         // This will avoid timing the clone call.
220    ///         b.iter_batched(|| data.clone(), |mut data| sort(&mut data), BatchSize::SmallInput)
221    ///     });
222    /// }
223    ///
224    /// criterion_group!(benches, bench);
225    /// criterion_main!(benches);
226    /// ```
227    ///
228    #[inline(never)]
229    pub fn iter_batched<I, O, S, R>(&mut self, mut setup: S, mut routine: R, size: BatchSize)
230    where
231        S: FnMut() -> I,
232        R: FnMut(I) -> O,
233    {
234        self.iterated = true;
235        let batch_size = size.iters_per_batch(self.iters);
236        assert!(batch_size != 0, "Batch size must not be zero.");
237        let time_start = Instant::now();
238        self.value = self.measurement.zero();
239
240        if batch_size == 1 {
241            for _ in 0..self.iters {
242                let input = black_box(setup());
243
244                let start = self.measurement.start();
245                let output = routine(input);
246                let end = self.measurement.end(start);
247                self.value = self.measurement.add(&self.value, &end);
248
249                drop(black_box(output));
250            }
251        } else {
252            let mut iteration_counter = 0;
253
254            while iteration_counter < self.iters {
255                let batch_size = ::std::cmp::min(batch_size, self.iters - iteration_counter);
256
257                let inputs = black_box((0..batch_size).map(|_| setup()).collect::<Vec<_>>());
258                let mut outputs = Vec::with_capacity(batch_size as usize);
259
260                let start = self.measurement.start();
261                outputs.extend(inputs.into_iter().map(&mut routine));
262                let end = self.measurement.end(start);
263                self.value = self.measurement.add(&self.value, &end);
264
265                black_box(outputs);
266
267                iteration_counter += batch_size;
268            }
269        }
270
271        self.elapsed_time = time_start.elapsed();
272    }
273
274    /// Times a `routine` that requires some input by generating a batch of input, then timing the
275    /// iteration of the benchmark over the input. See [`BatchSize`](enum.BatchSize.html) for
276    /// details on choosing the batch size. Use this when the routine should accept the input by
277    /// mutable reference.
278    ///
279    /// For example, use this loop to benchmark sorting algorithms, because they require unsorted
280    /// data on each iteration.
281    ///
282    /// # Timing model
283    ///
284    /// ```text
285    /// elapsed = (Instant::now * num_batches) + (iters * routine) + Vec::extend
286    /// ```
287    ///
288    /// # Example
289    ///
290    /// ```rust
291    /// use criterion::*;
292    ///
293    /// fn create_scrambled_data() -> Vec<u64> {
294    ///     # vec![]
295    ///     // ...
296    /// }
297    ///
298    /// // The sorting algorithm to test
299    /// fn sort(data: &mut [u64]) {
300    ///     // ...
301    /// }
302    ///
303    /// fn bench(c: &mut Criterion) {
304    ///     let data = create_scrambled_data();
305    ///
306    ///     c.bench_function("with_setup", move |b| {
307    ///         // This will avoid timing the clone call.
308    ///         b.iter_batched(|| data.clone(), |mut data| sort(&mut data), BatchSize::SmallInput)
309    ///     });
310    /// }
311    ///
312    /// criterion_group!(benches, bench);
313    /// criterion_main!(benches);
314    /// ```
315    ///
316    #[inline(never)]
317    pub fn iter_batched_ref<I, O, S, R>(&mut self, mut setup: S, mut routine: R, size: BatchSize)
318    where
319        S: FnMut() -> I,
320        R: FnMut(&mut I) -> O,
321    {
322        self.iterated = true;
323        let batch_size = size.iters_per_batch(self.iters);
324        assert!(batch_size != 0, "Batch size must not be zero.");
325        let time_start = Instant::now();
326        self.value = self.measurement.zero();
327
328        if batch_size == 1 {
329            for _ in 0..self.iters {
330                let mut input = black_box(setup());
331
332                let start = self.measurement.start();
333                let output = routine(&mut input);
334                let end = self.measurement.end(start);
335                self.value = self.measurement.add(&self.value, &end);
336
337                drop(black_box(output));
338                drop(black_box(input));
339            }
340        } else {
341            let mut iteration_counter = 0;
342
343            while iteration_counter < self.iters {
344                let batch_size = ::std::cmp::min(batch_size, self.iters - iteration_counter);
345
346                let mut inputs = black_box((0..batch_size).map(|_| setup()).collect::<Vec<_>>());
347                let mut outputs = Vec::with_capacity(batch_size as usize);
348
349                let start = self.measurement.start();
350                outputs.extend(inputs.iter_mut().map(&mut routine));
351                let end = self.measurement.end(start);
352                self.value = self.measurement.add(&self.value, &end);
353
354                black_box(outputs);
355
356                iteration_counter += batch_size;
357            }
358        }
359        self.elapsed_time = time_start.elapsed();
360    }
361
362    /// Times a routine that requires some setup which mutably borrows data from outside the setup
363    /// function.
364    ///
365    /// The setup function is passed a [`WrapperRunner`]. It should perform whatever setup is required
366    /// and then call `run` with the `routine` function. Only the execution time of the `routine`
367    /// function is measured.
368    ///
369    /// Each iteration of the benchmark is executed in series. So `setup` can mutably borrow data from
370    /// outside its closure mutably and know that it has exclusive access to that data throughout each
371    /// `setup` + `routine` iteration.
372    /// i.e. equivalent to [`BatchSize::PerIteration`].
373    ///
374    /// Value returned by `routine` is returned from `run`. If you do not wish include drop time of
375    /// a value in the measurement, return it from `routine` so it is dropped outside of the measured
376    /// section.
377    ///
378    /// # Example
379    ///
380    /// ```rust
381    /// use criterion::*;
382    ///
383    /// fn create_global_data() -> Vec<u64> {
384    ///     # vec![]
385    ///     // ...
386    /// }
387    ///
388    /// fn reset_global_data(data: &mut Vec<u64>) {
389    ///     // ...
390    /// }
391    ///
392    /// // The algorithm to test
393    /// fn do_something_with(data: &mut [u64]) -> Vec<u64> {
394    ///     # vec![]
395    ///     // ...
396    /// }
397    ///
398    /// fn bench(c: &mut Criterion) {
399    ///     let mut data = create_global_data();
400    ///
401    ///     c.bench_function("with_setup_wrapper", |b| {
402    ///         b.iter_with_setup_wrapper(|runner| {
403    ///             // Perform setup on each iteration. Not included in measurement.
404    ///             reset_global_data(&mut data);
405    ///
406    ///             runner.run(|| {
407    ///                 // Code in this closure is measured
408    ///                 let result = do_something_with(&mut data);
409    ///                 // Return result if do not want to include time dropping it in measure
410    ///                 result
411    ///             });
412    ///         });
413    ///     });
414    /// }
415    ///
416    /// criterion_group!(benches, bench);
417    /// criterion_main!(benches);
418    /// ```
419    ///
420    #[inline(never)]
421    pub fn iter_with_setup_wrapper<S>(&mut self, mut setup: S)
422    where
423        S: FnMut(&mut WrapperRunner<'a, '_, M>),
424    {
425        self.iterated = true;
426        let time_start = Instant::now();
427        self.value = self.measurement.zero();
428
429        for _ in 0..self.iters {
430            WrapperRunner::execute(self, &mut setup);
431        }
432
433        self.elapsed_time = time_start.elapsed();
434    }
435
436    // Benchmarks must actually call one of the iter methods. This causes benchmarks to fail loudly
437    // if they don't.
438    pub(crate) fn assert_iterated(&mut self) {
439        assert!(self.iterated, "Benchmark function must call Bencher::iter or related method.");
440        self.iterated = false;
441    }
442
443    /// Convert this bencher into an AsyncBencher, which enables async/await support.
444    #[cfg(feature = "async")]
445    pub fn to_async<'b, A: AsyncExecutor>(&'b mut self, runner: A) -> AsyncBencher<'a, 'b, A, M> {
446        AsyncBencher { b: self, runner }
447    }
448}
449
450/// Runner used by [`Bencher::iter_with_setup_wrapper`].
451pub struct WrapperRunner<'a, 'b, M: Measurement> {
452    bencher: &'b mut Bencher<'a, M>,
453    has_run: bool,
454}
455
456impl<'a, 'b, M: Measurement> WrapperRunner<'a, 'b, M> {
457    fn execute<S>(bencher: &'b mut Bencher<'a, M>, setup: &mut S)
458    where
459        S: FnMut(&mut Self),
460    {
461        let mut runner = Self { bencher, has_run: false };
462        setup(&mut runner);
463        assert!(runner.has_run, "setup function must call `WrapperRunner::run`");
464    }
465
466    pub fn run<O, R: FnOnce() -> O>(&mut self, routine: R) -> O {
467        assert!(!self.has_run, "setup function must call `WrapperRunner::run` only once");
468        self.has_run = true;
469
470        let bencher = &mut self.bencher;
471
472        let start: <M as Measurement>::Intermediate = bencher.measurement.start();
473        let output = routine();
474        let end = bencher.measurement.end(start);
475        bencher.value = bencher.measurement.add(&bencher.value, &end);
476
477        black_box(output)
478    }
479}
480
481/// Async/await variant of the Bencher struct.
482#[cfg(feature = "async")]
483pub struct AsyncBencher<'a, 'b, A: AsyncExecutor, M: Measurement = WallTime> {
484    b: &'b mut Bencher<'a, M>,
485    runner: A,
486}
487#[cfg(feature = "async")]
488impl<'a, 'b, A: AsyncExecutor, M: Measurement> AsyncBencher<'a, 'b, A, M> {
489    /// Times a `routine` by executing it many times and timing the total elapsed time.
490    ///
491    /// Prefer this timing loop when `routine` returns a value that doesn't have a destructor.
492    ///
493    /// # Timing model
494    ///
495    /// Note that the `AsyncBencher` also times the time required to destroy the output of `routine()`.
496    /// Therefore prefer this timing loop when the runtime of `mem::drop(O)` is negligible compared
497    /// to the runtime of the `routine`.
498    ///
499    /// ```text
500    /// elapsed = Instant::now + iters * (routine + mem::drop(O) + Range::next)
501    /// ```
502    ///
503    /// # Example
504    ///
505    /// ```rust
506    /// use criterion::*;
507    /// use criterion::async_executor::FuturesExecutor;
508    ///
509    /// // The function to benchmark
510    /// async fn foo() {
511    ///     // ...
512    /// }
513    ///
514    /// fn bench(c: &mut Criterion) {
515    ///     c.bench_function("iter", move |b| {
516    ///         b.to_async(FuturesExecutor).iter(|| async { foo().await } )
517    ///     });
518    /// }
519    ///
520    /// criterion_group!(benches, bench);
521    /// criterion_main!(benches);
522    /// ```
523    ///
524    #[inline(never)]
525    pub fn iter<O, R, F>(&mut self, mut routine: R)
526    where
527        R: FnMut() -> F,
528        F: Future<Output = O>,
529    {
530        let AsyncBencher { b, runner } = self;
531        runner.block_on(async {
532            b.iterated = true;
533            let time_start = Instant::now();
534            let start = b.measurement.start();
535            for _ in 0..b.iters {
536                black_box(routine().await);
537            }
538            b.value = b.measurement.end(start);
539            b.elapsed_time = time_start.elapsed();
540        });
541    }
542
543    /// Times a `routine` by executing it many times and relying on `routine` to measure its own execution time.
544    ///
545    /// Prefer this timing loop in cases where `routine` has to do its own measurements to
546    /// get accurate timing information (for example in multi-threaded scenarios where you spawn
547    /// and coordinate with multiple threads).
548    ///
549    /// # Timing model
550    /// Custom, the timing model is whatever is returned as the Duration from `routine`.
551    ///
552    /// # Example
553    /// ```rust
554    /// use criterion::*;
555    /// use criterion::black_box;
556    /// use criterion::async_executor::FuturesExecutor;
557    /// use std::time::Instant;
558    ///
559    /// async fn foo() {
560    ///     // ...
561    /// }
562    ///
563    /// fn bench(c: &mut Criterion) {
564    ///     c.bench_function("iter", move |b| {
565    ///         b.to_async(FuturesExecutor).iter_custom(|iters| {
566    ///             async move {
567    ///                 let start = Instant::now();
568    ///                 for _i in 0..iters {
569    ///                     black_box(foo().await);
570    ///                 }
571    ///                 start.elapsed()
572    ///             }
573    ///         })
574    ///     });
575    /// }
576    ///
577    /// criterion_group!(benches, bench);
578    /// criterion_main!(benches);
579    /// ```
580    ///
581    #[inline(never)]
582    pub fn iter_custom<R, F>(&mut self, mut routine: R)
583    where
584        R: FnMut(u64) -> F,
585        F: Future<Output = M::Value>,
586    {
587        let AsyncBencher { b, runner } = self;
588        runner.block_on(async {
589            b.iterated = true;
590            let time_start = Instant::now();
591            b.value = routine(b.iters).await;
592            b.elapsed_time = time_start.elapsed();
593        })
594    }
595
596    #[doc(hidden)]
597    pub fn iter_with_setup<I, O, S, R, F>(&mut self, setup: S, routine: R)
598    where
599        S: FnMut() -> I,
600        R: FnMut(I) -> F,
601        F: Future<Output = O>,
602    {
603        self.iter_batched(setup, routine, BatchSize::PerIteration);
604    }
605
606    /// Times a `routine` by collecting its output on each iteration. This avoids timing the
607    /// destructor of the value returned by `routine`.
608    ///
609    /// WARNING: This requires `O(iters * mem::size_of::<O>())` of memory, and `iters` is not under the
610    /// control of the caller. If this causes out-of-memory errors, use `iter_batched` instead.
611    ///
612    /// # Timing model
613    ///
614    /// ``` text
615    /// elapsed = Instant::now + iters * (routine) + Iterator::collect::<Vec<_>>
616    /// ```
617    ///
618    /// # Example
619    ///
620    /// ```rust
621    /// use criterion::*;
622    /// use criterion::async_executor::FuturesExecutor;
623    ///
624    /// async fn create_vector() -> Vec<u64> {
625    ///     # vec![]
626    ///     // ...
627    /// }
628    ///
629    /// fn bench(c: &mut Criterion) {
630    ///     c.bench_function("with_drop", move |b| {
631    ///         // This will avoid timing the Vec::drop.
632    ///         b.to_async(FuturesExecutor).iter_with_large_drop(|| async { create_vector().await })
633    ///     });
634    /// }
635    ///
636    /// criterion_group!(benches, bench);
637    /// criterion_main!(benches);
638    /// ```
639    ///
640    pub fn iter_with_large_drop<O, R, F>(&mut self, mut routine: R)
641    where
642        R: FnMut() -> F,
643        F: Future<Output = O>,
644    {
645        self.iter_batched(|| (), |_| routine(), BatchSize::SmallInput);
646    }
647
648    #[doc(hidden)]
649    pub fn iter_with_large_setup<I, O, S, R, F>(&mut self, setup: S, routine: R)
650    where
651        S: FnMut() -> I,
652        R: FnMut(I) -> F,
653        F: Future<Output = O>,
654    {
655        self.iter_batched(setup, routine, BatchSize::NumBatches(1));
656    }
657
658    #[doc(hidden)]
659    pub fn iter_batched_async_setup<FI, I, O, S, R, FO>(
660        &mut self,
661        mut setup: S,
662        mut routine: R,
663        size: BatchSize,
664    ) where
665        S: FnMut() -> FI,
666        FI: Future<Output = I>,
667        R: FnMut(I) -> FO,
668        FO: Future<Output = O>,
669    {
670        let AsyncBencher { b, runner } = self;
671        runner.block_on(async {
672            b.iterated = true;
673            let batch_size = size.iters_per_batch(b.iters);
674            assert!(batch_size != 0, "Batch size must not be zero.");
675            let time_start = Instant::now();
676            b.value = b.measurement.zero();
677
678            if batch_size == 1 {
679                for _ in 0..b.iters {
680                    let input = black_box(setup().await);
681
682                    let start = b.measurement.start();
683                    let output = routine(input).await;
684                    let end = b.measurement.end(start);
685                    b.value = b.measurement.add(&b.value, &end);
686
687                    drop(black_box(output));
688                }
689            } else {
690                let mut iteration_counter = 0;
691
692                while iteration_counter < b.iters {
693                    let batch_size = ::std::cmp::min(batch_size, b.iters - iteration_counter);
694
695                    let mut inputs = Vec::with_capacity(batch_size as usize);
696                    for _ in 0..batch_size {
697                        black_box(inputs.push(setup().await));
698                    }
699                    let mut outputs = Vec::with_capacity(batch_size as usize);
700
701                    let start = b.measurement.start();
702                    // Can't use .extend here like the sync version does
703                    for input in inputs {
704                        outputs.push(routine(input).await);
705                    }
706                    let end = b.measurement.end(start);
707                    b.value = b.measurement.add(&b.value, &end);
708
709                    black_box(outputs);
710
711                    iteration_counter += batch_size;
712                }
713            }
714
715            b.elapsed_time = time_start.elapsed();
716        })
717    }
718
719    /// Times a `routine` that requires some input by generating a batch of input, then timing the
720    /// iteration of the benchmark over the input. See [`BatchSize`](enum.BatchSize.html) for
721    /// details on choosing the batch size. Use this when the routine must consume its input.
722    ///
723    /// For example, use this loop to benchmark sorting algorithms, because they require unsorted
724    /// data on each iteration.
725    ///
726    /// # Timing model
727    ///
728    /// ```text
729    /// elapsed = (Instant::now * num_batches) + (iters * (routine + O::drop)) + Vec::extend
730    /// ```
731    ///
732    /// # Example
733    ///
734    /// ```rust
735    /// use criterion::*;
736    /// use criterion::async_executor::FuturesExecutor;
737    ///
738    /// fn create_scrambled_data() -> Vec<u64> {
739    ///     # vec![]
740    ///     // ...
741    /// }
742    ///
743    /// // The sorting algorithm to test
744    /// async fn sort(data: &mut [u64]) {
745    ///     // ...
746    /// }
747    ///
748    /// fn bench(c: &mut Criterion) {
749    ///     let data = create_scrambled_data();
750    ///
751    ///     c.bench_function("with_setup", move |b| {
752    ///         // This will avoid timing the clone call.
753    ///         b.iter_batched(|| data.clone(), |mut data| async move { sort(&mut data).await }, BatchSize::SmallInput)
754    ///     });
755    /// }
756    ///
757    /// criterion_group!(benches, bench);
758    /// criterion_main!(benches);
759    /// ```
760    ///
761    #[inline(never)]
762    pub fn iter_batched<I, O, S, R, F>(&mut self, mut setup: S, mut routine: R, size: BatchSize)
763    where
764        S: FnMut() -> I,
765        R: FnMut(I) -> F,
766        F: Future<Output = O>,
767    {
768        let AsyncBencher { b, runner } = self;
769        runner.block_on(async {
770            b.iterated = true;
771            let batch_size = size.iters_per_batch(b.iters);
772            assert!(batch_size != 0, "Batch size must not be zero.");
773            let time_start = Instant::now();
774            b.value = b.measurement.zero();
775
776            if batch_size == 1 {
777                for _ in 0..b.iters {
778                    let input = black_box(setup());
779
780                    let start = b.measurement.start();
781                    let output = routine(input).await;
782                    let end = b.measurement.end(start);
783                    b.value = b.measurement.add(&b.value, &end);
784
785                    drop(black_box(output));
786                }
787            } else {
788                let mut iteration_counter = 0;
789
790                while iteration_counter < b.iters {
791                    let batch_size = ::std::cmp::min(batch_size, b.iters - iteration_counter);
792
793                    let inputs = black_box((0..batch_size).map(|_| setup()).collect::<Vec<_>>());
794                    let mut outputs = Vec::with_capacity(batch_size as usize);
795
796                    let start = b.measurement.start();
797                    // Can't use .extend here like the sync version does
798                    for input in inputs {
799                        outputs.push(routine(input).await);
800                    }
801                    let end = b.measurement.end(start);
802                    b.value = b.measurement.add(&b.value, &end);
803
804                    black_box(outputs);
805
806                    iteration_counter += batch_size;
807                }
808            }
809
810            b.elapsed_time = time_start.elapsed();
811        })
812    }
813
814    /// Times a `routine` that requires some input by generating a batch of input, then timing the
815    /// iteration of the benchmark over the input. See [`BatchSize`](enum.BatchSize.html) for
816    /// details on choosing the batch size. Use this when the routine should accept the input by
817    /// mutable reference.
818    ///
819    /// For example, use this loop to benchmark sorting algorithms, because they require unsorted
820    /// data on each iteration.
821    ///
822    /// # Timing model
823    ///
824    /// ```text
825    /// elapsed = (Instant::now * num_batches) + (iters * routine) + Vec::extend
826    /// ```
827    ///
828    /// # Example
829    ///
830    /// ```rust
831    /// use criterion::*;
832    /// use criterion::async_executor::FuturesExecutor;
833    ///
834    /// fn create_scrambled_data() -> Vec<u64> {
835    ///     # vec![]
836    ///     // ...
837    /// }
838    ///
839    /// // The sorting algorithm to test
840    /// async fn sort(data: &mut [u64]) {
841    ///     // ...
842    /// }
843    ///
844    /// fn bench(c: &mut Criterion) {
845    ///     let data = create_scrambled_data();
846    ///
847    ///     c.bench_function("with_setup", move |b| {
848    ///         // This will avoid timing the clone call.
849    ///         b.iter_batched(|| data.clone(), |mut data| async move { sort(&mut data).await }, BatchSize::SmallInput)
850    ///     });
851    /// }
852    ///
853    /// criterion_group!(benches, bench);
854    /// criterion_main!(benches);
855    /// ```
856    ///
857    #[inline(never)]
858    pub fn iter_batched_ref<I, O, S, R, F>(&mut self, mut setup: S, mut routine: R, size: BatchSize)
859    where
860        S: FnMut() -> I,
861        R: FnMut(&mut I) -> F,
862        F: Future<Output = O>,
863    {
864        let AsyncBencher { b, runner } = self;
865        runner.block_on(async {
866            b.iterated = true;
867            let batch_size = size.iters_per_batch(b.iters);
868            assert!(batch_size != 0, "Batch size must not be zero.");
869            let time_start = Instant::now();
870            b.value = b.measurement.zero();
871
872            if batch_size == 1 {
873                for _ in 0..b.iters {
874                    let mut input = black_box(setup());
875
876                    let start = b.measurement.start();
877                    let output = routine(&mut input).await;
878                    let end = b.measurement.end(start);
879                    b.value = b.measurement.add(&b.value, &end);
880
881                    drop(black_box(output));
882                    drop(black_box(input));
883                }
884            } else {
885                let mut iteration_counter = 0;
886
887                while iteration_counter < b.iters {
888                    let batch_size = ::std::cmp::min(batch_size, b.iters - iteration_counter);
889
890                    let inputs = black_box((0..batch_size).map(|_| setup()).collect::<Vec<_>>());
891                    let mut outputs = Vec::with_capacity(batch_size as usize);
892
893                    let start = b.measurement.start();
894                    // Can't use .extend here like the sync version does
895                    for mut input in inputs {
896                        outputs.push(routine(&mut input).await);
897                    }
898                    let end = b.measurement.end(start);
899                    b.value = b.measurement.add(&b.value, &end);
900
901                    black_box(outputs);
902
903                    iteration_counter += batch_size;
904                }
905            }
906            b.elapsed_time = time_start.elapsed();
907        });
908    }
909
910    pub fn iter_with_setup_wrapper<S>(&mut self, mut setup: S)
911    where
912        S: FnMut(&mut WrapperRunner<'a, '_, M>),
913    {
914        unimplemented!("Unsupported at present");
915    }
916}