• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 use std::iter::IntoIterator;
2 use std::time::Duration;
3 use std::time::Instant;
4 
5 use crate::black_box;
6 use crate::measurement::{Measurement, WallTime};
7 use crate::BatchSize;
8 
9 #[cfg(feature = "async")]
10 use std::future::Future;
11 
12 #[cfg(feature = "async")]
13 use crate::async_executor::AsyncExecutor;
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`.
35 pub 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 }
42 impl<'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     /// #[macro_use] extern crate criterion;
61     ///
62     /// use criterion::*;
63     ///
64     /// // The function to benchmark
65     /// fn foo() {
66     ///     // ...
67     /// }
68     ///
69     /// fn bench(c: &mut Criterion) {
70     ///     c.bench_function("iter", move |b| {
71     ///         b.iter(|| foo())
72     ///     });
73     /// }
74     ///
75     /// criterion_group!(benches, bench);
76     /// criterion_main!(benches);
77     /// ```
78     ///
79     #[inline(never)]
iter<O, R>(&mut self, mut routine: R) where R: FnMut() -> O,80     pub fn iter<O, R>(&mut self, mut routine: R)
81     where
82         R: FnMut() -> O,
83     {
84         self.iterated = true;
85         let time_start = Instant::now();
86         let start = self.measurement.start();
87         for _ in 0..self.iters {
88             black_box(routine());
89         }
90         self.value = self.measurement.end(start);
91         self.elapsed_time = time_start.elapsed();
92     }
93 
94     /// Times a `routine` by executing it many times and relying on `routine` to measure its own execution time.
95     ///
96     /// Prefer this timing loop in cases where `routine` has to do its own measurements to
97     /// get accurate timing information (for example in multi-threaded scenarios where you spawn
98     /// and coordinate with multiple threads).
99     ///
100     /// # Timing model
101     /// Custom, the timing model is whatever is returned as the Duration from `routine`.
102     ///
103     /// # Example
104     /// ```rust
105     /// #[macro_use] extern crate criterion;
106     /// use criterion::*;
107     /// use criterion::black_box;
108     /// use std::time::Instant;
109     ///
110     /// fn foo() {
111     ///     // ...
112     /// }
113     ///
114     /// fn bench(c: &mut Criterion) {
115     ///     c.bench_function("iter", move |b| {
116     ///         b.iter_custom(|iters| {
117     ///             let start = Instant::now();
118     ///             for _i in 0..iters {
119     ///                 black_box(foo());
120     ///             }
121     ///             start.elapsed()
122     ///         })
123     ///     });
124     /// }
125     ///
126     /// criterion_group!(benches, bench);
127     /// criterion_main!(benches);
128     /// ```
129     ///
130     #[inline(never)]
iter_custom<R>(&mut self, mut routine: R) where R: FnMut(u64) -> M::Value,131     pub fn iter_custom<R>(&mut self, mut routine: R)
132     where
133         R: FnMut(u64) -> M::Value,
134     {
135         self.iterated = true;
136         let time_start = Instant::now();
137         self.value = routine(self.iters);
138         self.elapsed_time = time_start.elapsed();
139     }
140 
141     #[doc(hidden)]
iter_with_setup<I, O, S, R>(&mut self, setup: S, routine: R) where S: FnMut() -> I, R: FnMut(I) -> O,142     pub fn iter_with_setup<I, O, S, R>(&mut self, setup: S, routine: R)
143     where
144         S: FnMut() -> I,
145         R: FnMut(I) -> O,
146     {
147         self.iter_batched(setup, routine, BatchSize::PerIteration);
148     }
149 
150     /// Times a `routine` by collecting its output on each iteration. This avoids timing the
151     /// destructor of the value returned by `routine`.
152     ///
153     /// WARNING: This requires `O(iters * mem::size_of::<O>())` of memory, and `iters` is not under the
154     /// control of the caller. If this causes out-of-memory errors, use `iter_batched` instead.
155     ///
156     /// # Timing model
157     ///
158     /// ``` text
159     /// elapsed = Instant::now + iters * (routine) + Iterator::collect::<Vec<_>>
160     /// ```
161     ///
162     /// # Example
163     ///
164     /// ```rust
165     /// #[macro_use] extern crate criterion;
166     ///
167     /// use criterion::*;
168     ///
169     /// fn create_vector() -> Vec<u64> {
170     ///     # vec![]
171     ///     // ...
172     /// }
173     ///
174     /// fn bench(c: &mut Criterion) {
175     ///     c.bench_function("with_drop", move |b| {
176     ///         // This will avoid timing the Vec::drop.
177     ///         b.iter_with_large_drop(|| create_vector())
178     ///     });
179     /// }
180     ///
181     /// criterion_group!(benches, bench);
182     /// criterion_main!(benches);
183     /// ```
184     ///
iter_with_large_drop<O, R>(&mut self, mut routine: R) where R: FnMut() -> O,185     pub fn iter_with_large_drop<O, R>(&mut self, mut routine: R)
186     where
187         R: FnMut() -> O,
188     {
189         self.iter_batched(|| (), |_| routine(), BatchSize::SmallInput);
190     }
191 
192     #[doc(hidden)]
iter_with_large_setup<I, O, S, R>(&mut self, setup: S, routine: R) where S: FnMut() -> I, R: FnMut(I) -> O,193     pub fn iter_with_large_setup<I, O, S, R>(&mut self, setup: S, routine: R)
194     where
195         S: FnMut() -> I,
196         R: FnMut(I) -> O,
197     {
198         self.iter_batched(setup, routine, BatchSize::NumBatches(1));
199     }
200 
201     /// Times a `routine` that requires some input by generating a batch of input, then timing the
202     /// iteration of the benchmark over the input. See [`BatchSize`](enum.BatchSize.html) for
203     /// details on choosing the batch size. Use this when the routine must consume its input.
204     ///
205     /// For example, use this loop to benchmark sorting algorithms, because they require unsorted
206     /// data on each iteration.
207     ///
208     /// # Timing model
209     ///
210     /// ```text
211     /// elapsed = (Instant::now * num_batches) + (iters * (routine + O::drop)) + Vec::extend
212     /// ```
213     ///
214     /// # Example
215     ///
216     /// ```rust
217     /// #[macro_use] extern crate criterion;
218     ///
219     /// use criterion::*;
220     ///
221     /// fn create_scrambled_data() -> Vec<u64> {
222     ///     # vec![]
223     ///     // ...
224     /// }
225     ///
226     /// // The sorting algorithm to test
227     /// fn sort(data: &mut [u64]) {
228     ///     // ...
229     /// }
230     ///
231     /// fn bench(c: &mut Criterion) {
232     ///     let data = create_scrambled_data();
233     ///
234     ///     c.bench_function("with_setup", move |b| {
235     ///         // This will avoid timing the to_vec call.
236     ///         b.iter_batched(|| data.clone(), |mut data| sort(&mut data), BatchSize::SmallInput)
237     ///     });
238     /// }
239     ///
240     /// criterion_group!(benches, bench);
241     /// criterion_main!(benches);
242     /// ```
243     ///
244     #[inline(never)]
iter_batched<I, O, S, R>(&mut self, mut setup: S, mut routine: R, size: BatchSize) where S: FnMut() -> I, R: FnMut(I) -> O,245     pub fn iter_batched<I, O, S, R>(&mut self, mut setup: S, mut routine: R, size: BatchSize)
246     where
247         S: FnMut() -> I,
248         R: FnMut(I) -> O,
249     {
250         self.iterated = true;
251         let batch_size = size.iters_per_batch(self.iters);
252         assert!(batch_size != 0, "Batch size must not be zero.");
253         let time_start = Instant::now();
254         self.value = self.measurement.zero();
255 
256         if batch_size == 1 {
257             for _ in 0..self.iters {
258                 let input = black_box(setup());
259 
260                 let start = self.measurement.start();
261                 let output = routine(input);
262                 let end = self.measurement.end(start);
263                 self.value = self.measurement.add(&self.value, &end);
264 
265                 drop(black_box(output));
266             }
267         } else {
268             let mut iteration_counter = 0;
269 
270             while iteration_counter < self.iters {
271                 let batch_size = ::std::cmp::min(batch_size, self.iters - iteration_counter);
272 
273                 let inputs = black_box((0..batch_size).map(|_| setup()).collect::<Vec<_>>());
274                 let mut outputs = Vec::with_capacity(batch_size as usize);
275 
276                 let start = self.measurement.start();
277                 outputs.extend(inputs.into_iter().map(&mut routine));
278                 let end = self.measurement.end(start);
279                 self.value = self.measurement.add(&self.value, &end);
280 
281                 black_box(outputs);
282 
283                 iteration_counter += batch_size;
284             }
285         }
286 
287         self.elapsed_time = time_start.elapsed();
288     }
289 
290     /// Times a `routine` that requires some input by generating a batch of input, then timing the
291     /// iteration of the benchmark over the input. See [`BatchSize`](enum.BatchSize.html) for
292     /// details on choosing the batch size. Use this when the routine should accept the input by
293     /// mutable reference.
294     ///
295     /// For example, use this loop to benchmark sorting algorithms, because they require unsorted
296     /// data on each iteration.
297     ///
298     /// # Timing model
299     ///
300     /// ```text
301     /// elapsed = (Instant::now * num_batches) + (iters * routine) + Vec::extend
302     /// ```
303     ///
304     /// # Example
305     ///
306     /// ```rust
307     /// #[macro_use] extern crate criterion;
308     ///
309     /// use criterion::*;
310     ///
311     /// fn create_scrambled_data() -> Vec<u64> {
312     ///     # vec![]
313     ///     // ...
314     /// }
315     ///
316     /// // The sorting algorithm to test
317     /// fn sort(data: &mut [u64]) {
318     ///     // ...
319     /// }
320     ///
321     /// fn bench(c: &mut Criterion) {
322     ///     let data = create_scrambled_data();
323     ///
324     ///     c.bench_function("with_setup", move |b| {
325     ///         // This will avoid timing the to_vec call.
326     ///         b.iter_batched(|| data.clone(), |mut data| sort(&mut data), BatchSize::SmallInput)
327     ///     });
328     /// }
329     ///
330     /// criterion_group!(benches, bench);
331     /// criterion_main!(benches);
332     /// ```
333     ///
334     #[inline(never)]
iter_batched_ref<I, O, S, R>(&mut self, mut setup: S, mut routine: R, size: BatchSize) where S: FnMut() -> I, R: FnMut(&mut I) -> O,335     pub fn iter_batched_ref<I, O, S, R>(&mut self, mut setup: S, mut routine: R, size: BatchSize)
336     where
337         S: FnMut() -> I,
338         R: FnMut(&mut I) -> O,
339     {
340         self.iterated = true;
341         let batch_size = size.iters_per_batch(self.iters);
342         assert!(batch_size != 0, "Batch size must not be zero.");
343         let time_start = Instant::now();
344         self.value = self.measurement.zero();
345 
346         if batch_size == 1 {
347             for _ in 0..self.iters {
348                 let mut input = black_box(setup());
349 
350                 let start = self.measurement.start();
351                 let output = routine(&mut input);
352                 let end = self.measurement.end(start);
353                 self.value = self.measurement.add(&self.value, &end);
354 
355                 drop(black_box(output));
356                 drop(black_box(input));
357             }
358         } else {
359             let mut iteration_counter = 0;
360 
361             while iteration_counter < self.iters {
362                 let batch_size = ::std::cmp::min(batch_size, self.iters - iteration_counter);
363 
364                 let mut inputs = black_box((0..batch_size).map(|_| setup()).collect::<Vec<_>>());
365                 let mut outputs = Vec::with_capacity(batch_size as usize);
366 
367                 let start = self.measurement.start();
368                 outputs.extend(inputs.iter_mut().map(&mut routine));
369                 let end = self.measurement.end(start);
370                 self.value = self.measurement.add(&self.value, &end);
371 
372                 black_box(outputs);
373 
374                 iteration_counter += batch_size;
375             }
376         }
377         self.elapsed_time = time_start.elapsed();
378     }
379 
380     // Benchmarks must actually call one of the iter methods. This causes benchmarks to fail loudly
381     // if they don't.
assert_iterated(&mut self)382     pub(crate) fn assert_iterated(&mut self) {
383         if !self.iterated {
384             panic!("Benchmark function must call Bencher::iter or related method.");
385         }
386         self.iterated = false;
387     }
388 
389     /// Convert this bencher into an AsyncBencher, which enables async/await support.
390     #[cfg(feature = "async")]
to_async<'b, A: AsyncExecutor>(&'b mut self, runner: A) -> AsyncBencher<'a, 'b, A, M>391     pub fn to_async<'b, A: AsyncExecutor>(&'b mut self, runner: A) -> AsyncBencher<'a, 'b, A, M> {
392         AsyncBencher { b: self, runner }
393     }
394 }
395 
396 /// Async/await variant of the Bencher struct.
397 #[cfg(feature = "async")]
398 pub struct AsyncBencher<'a, 'b, A: AsyncExecutor, M: Measurement = WallTime> {
399     b: &'b mut Bencher<'a, M>,
400     runner: A,
401 }
402 #[cfg(feature = "async")]
403 impl<'a, 'b, A: AsyncExecutor, M: Measurement> AsyncBencher<'a, 'b, A, M> {
404     /// Times a `routine` by executing it many times and timing the total elapsed time.
405     ///
406     /// Prefer this timing loop when `routine` returns a value that doesn't have a destructor.
407     ///
408     /// # Timing model
409     ///
410     /// Note that the `AsyncBencher` also times the time required to destroy the output of `routine()`.
411     /// Therefore prefer this timing loop when the runtime of `mem::drop(O)` is negligible compared
412     /// to the runtime of the `routine`.
413     ///
414     /// ```text
415     /// elapsed = Instant::now + iters * (routine + mem::drop(O) + Range::next)
416     /// ```
417     ///
418     /// # Example
419     ///
420     /// ```rust
421     /// #[macro_use] extern crate criterion;
422     ///
423     /// use criterion::*;
424     /// use criterion::async_executor::FuturesExecutor;
425     ///
426     /// // The function to benchmark
427     /// async fn foo() {
428     ///     // ...
429     /// }
430     ///
431     /// fn bench(c: &mut Criterion) {
432     ///     c.bench_function("iter", move |b| {
433     ///         b.to_async(FuturesExecutor).iter(|| async { foo().await } )
434     ///     });
435     /// }
436     ///
437     /// criterion_group!(benches, bench);
438     /// criterion_main!(benches);
439     /// ```
440     ///
441     #[inline(never)]
iter<O, R, F>(&mut self, mut routine: R) where R: FnMut() -> F, F: Future<Output = O>,442     pub fn iter<O, R, F>(&mut self, mut routine: R)
443     where
444         R: FnMut() -> F,
445         F: Future<Output = O>,
446     {
447         let AsyncBencher { b, runner } = self;
448         runner.block_on(async {
449             b.iterated = true;
450             let time_start = Instant::now();
451             let start = b.measurement.start();
452             for _ in 0..b.iters {
453                 black_box(routine().await);
454             }
455             b.value = b.measurement.end(start);
456             b.elapsed_time = time_start.elapsed();
457         });
458     }
459 
460     /// Times a `routine` by executing it many times and relying on `routine` to measure its own execution time.
461     ///
462     /// Prefer this timing loop in cases where `routine` has to do its own measurements to
463     /// get accurate timing information (for example in multi-threaded scenarios where you spawn
464     /// and coordinate with multiple threads).
465     ///
466     /// # Timing model
467     /// Custom, the timing model is whatever is returned as the Duration from `routine`.
468     ///
469     /// # Example
470     /// ```rust
471     /// #[macro_use] extern crate criterion;
472     /// use criterion::*;
473     /// use criterion::black_box;
474     /// use criterion::async_executor::FuturesExecutor;
475     /// use std::time::Instant;
476     ///
477     /// async fn foo() {
478     ///     // ...
479     /// }
480     ///
481     /// fn bench(c: &mut Criterion) {
482     ///     c.bench_function("iter", move |b| {
483     ///         b.to_async(FuturesExecutor).iter_custom(|iters| {
484     ///             async move {
485     ///                 let start = Instant::now();
486     ///                 for _i in 0..iters {
487     ///                     black_box(foo().await);
488     ///                 }
489     ///                 start.elapsed()
490     ///             }
491     ///         })
492     ///     });
493     /// }
494     ///
495     /// criterion_group!(benches, bench);
496     /// criterion_main!(benches);
497     /// ```
498     ///
499     #[inline(never)]
iter_custom<R, F>(&mut self, mut routine: R) where R: FnMut(u64) -> F, F: Future<Output = M::Value>,500     pub fn iter_custom<R, F>(&mut self, mut routine: R)
501     where
502         R: FnMut(u64) -> F,
503         F: Future<Output = M::Value>,
504     {
505         let AsyncBencher { b, runner } = self;
506         runner.block_on(async {
507             b.iterated = true;
508             let time_start = Instant::now();
509             b.value = routine(b.iters).await;
510             b.elapsed_time = time_start.elapsed();
511         })
512     }
513 
514     #[doc(hidden)]
iter_with_setup<I, O, S, R, F>(&mut self, setup: S, routine: R) where S: FnMut() -> I, R: FnMut(I) -> F, F: Future<Output = O>,515     pub fn iter_with_setup<I, O, S, R, F>(&mut self, setup: S, routine: R)
516     where
517         S: FnMut() -> I,
518         R: FnMut(I) -> F,
519         F: Future<Output = O>,
520     {
521         self.iter_batched(setup, routine, BatchSize::PerIteration);
522     }
523 
524     /// Times a `routine` by collecting its output on each iteration. This avoids timing the
525     /// destructor of the value returned by `routine`.
526     ///
527     /// WARNING: This requires `O(iters * mem::size_of::<O>())` of memory, and `iters` is not under the
528     /// control of the caller. If this causes out-of-memory errors, use `iter_batched` instead.
529     ///
530     /// # Timing model
531     ///
532     /// ``` text
533     /// elapsed = Instant::now + iters * (routine) + Iterator::collect::<Vec<_>>
534     /// ```
535     ///
536     /// # Example
537     ///
538     /// ```rust
539     /// #[macro_use] extern crate criterion;
540     ///
541     /// use criterion::*;
542     /// use criterion::async_executor::FuturesExecutor;
543     ///
544     /// async fn create_vector() -> Vec<u64> {
545     ///     # vec![]
546     ///     // ...
547     /// }
548     ///
549     /// fn bench(c: &mut Criterion) {
550     ///     c.bench_function("with_drop", move |b| {
551     ///         // This will avoid timing the Vec::drop.
552     ///         b.to_async(FuturesExecutor).iter_with_large_drop(|| async { create_vector().await })
553     ///     });
554     /// }
555     ///
556     /// criterion_group!(benches, bench);
557     /// criterion_main!(benches);
558     /// ```
559     ///
iter_with_large_drop<O, R, F>(&mut self, mut routine: R) where R: FnMut() -> F, F: Future<Output = O>,560     pub fn iter_with_large_drop<O, R, F>(&mut self, mut routine: R)
561     where
562         R: FnMut() -> F,
563         F: Future<Output = O>,
564     {
565         self.iter_batched(|| (), |_| routine(), BatchSize::SmallInput);
566     }
567 
568     #[doc(hidden)]
iter_with_large_setup<I, O, S, R, F>(&mut self, setup: S, routine: R) where S: FnMut() -> I, R: FnMut(I) -> F, F: Future<Output = O>,569     pub fn iter_with_large_setup<I, O, S, R, F>(&mut self, setup: S, routine: R)
570     where
571         S: FnMut() -> I,
572         R: FnMut(I) -> F,
573         F: Future<Output = O>,
574     {
575         self.iter_batched(setup, routine, BatchSize::NumBatches(1));
576     }
577 
578     /// Times a `routine` that requires some input by generating a batch of input, then timing the
579     /// iteration of the benchmark over the input. See [`BatchSize`](enum.BatchSize.html) for
580     /// details on choosing the batch size. Use this when the routine must consume its input.
581     ///
582     /// For example, use this loop to benchmark sorting algorithms, because they require unsorted
583     /// data on each iteration.
584     ///
585     /// # Timing model
586     ///
587     /// ```text
588     /// elapsed = (Instant::now * num_batches) + (iters * (routine + O::drop)) + Vec::extend
589     /// ```
590     ///
591     /// # Example
592     ///
593     /// ```rust
594     /// #[macro_use] extern crate criterion;
595     ///
596     /// use criterion::*;
597     /// use criterion::async_executor::FuturesExecutor;
598     ///
599     /// fn create_scrambled_data() -> Vec<u64> {
600     ///     # vec![]
601     ///     // ...
602     /// }
603     ///
604     /// // The sorting algorithm to test
605     /// async fn sort(data: &mut [u64]) {
606     ///     // ...
607     /// }
608     ///
609     /// fn bench(c: &mut Criterion) {
610     ///     let data = create_scrambled_data();
611     ///
612     ///     c.bench_function("with_setup", move |b| {
613     ///         // This will avoid timing the to_vec call.
614     ///         b.iter_batched(|| data.clone(), |mut data| async move { sort(&mut data).await }, BatchSize::SmallInput)
615     ///     });
616     /// }
617     ///
618     /// criterion_group!(benches, bench);
619     /// criterion_main!(benches);
620     /// ```
621     ///
622     #[inline(never)]
iter_batched<I, O, S, R, F>(&mut self, mut setup: S, mut routine: R, size: BatchSize) where S: FnMut() -> I, R: FnMut(I) -> F, F: Future<Output = O>,623     pub fn iter_batched<I, O, S, R, F>(&mut self, mut setup: S, mut routine: R, size: BatchSize)
624     where
625         S: FnMut() -> I,
626         R: FnMut(I) -> F,
627         F: Future<Output = O>,
628     {
629         let AsyncBencher { b, runner } = self;
630         runner.block_on(async {
631             b.iterated = true;
632             let batch_size = size.iters_per_batch(b.iters);
633             assert!(batch_size != 0, "Batch size must not be zero.");
634             let time_start = Instant::now();
635             b.value = b.measurement.zero();
636 
637             if batch_size == 1 {
638                 for _ in 0..b.iters {
639                     let input = black_box(setup());
640 
641                     let start = b.measurement.start();
642                     let output = routine(input).await;
643                     let end = b.measurement.end(start);
644                     b.value = b.measurement.add(&b.value, &end);
645 
646                     drop(black_box(output));
647                 }
648             } else {
649                 let mut iteration_counter = 0;
650 
651                 while iteration_counter < b.iters {
652                     let batch_size = ::std::cmp::min(batch_size, b.iters - iteration_counter);
653 
654                     let inputs = black_box((0..batch_size).map(|_| setup()).collect::<Vec<_>>());
655                     let mut outputs = Vec::with_capacity(batch_size as usize);
656 
657                     let start = b.measurement.start();
658                     // Can't use .extend here like the sync version does
659                     for input in inputs {
660                         outputs.push(routine(input).await);
661                     }
662                     let end = b.measurement.end(start);
663                     b.value = b.measurement.add(&b.value, &end);
664 
665                     black_box(outputs);
666 
667                     iteration_counter += batch_size;
668                 }
669             }
670 
671             b.elapsed_time = time_start.elapsed();
672         })
673     }
674 
675     /// Times a `routine` that requires some input by generating a batch of input, then timing the
676     /// iteration of the benchmark over the input. See [`BatchSize`](enum.BatchSize.html) for
677     /// details on choosing the batch size. Use this when the routine should accept the input by
678     /// mutable reference.
679     ///
680     /// For example, use this loop to benchmark sorting algorithms, because they require unsorted
681     /// data on each iteration.
682     ///
683     /// # Timing model
684     ///
685     /// ```text
686     /// elapsed = (Instant::now * num_batches) + (iters * routine) + Vec::extend
687     /// ```
688     ///
689     /// # Example
690     ///
691     /// ```rust
692     /// #[macro_use] extern crate criterion;
693     ///
694     /// use criterion::*;
695     /// use criterion::async_executor::FuturesExecutor;
696     ///
697     /// fn create_scrambled_data() -> Vec<u64> {
698     ///     # vec![]
699     ///     // ...
700     /// }
701     ///
702     /// // The sorting algorithm to test
703     /// async fn sort(data: &mut [u64]) {
704     ///     // ...
705     /// }
706     ///
707     /// fn bench(c: &mut Criterion) {
708     ///     let data = create_scrambled_data();
709     ///
710     ///     c.bench_function("with_setup", move |b| {
711     ///         // This will avoid timing the to_vec call.
712     ///         b.iter_batched(|| data.clone(), |mut data| async move { sort(&mut data).await }, BatchSize::SmallInput)
713     ///     });
714     /// }
715     ///
716     /// criterion_group!(benches, bench);
717     /// criterion_main!(benches);
718     /// ```
719     ///
720     #[inline(never)]
iter_batched_ref<I, O, S, R, F>(&mut self, mut setup: S, mut routine: R, size: BatchSize) where S: FnMut() -> I, R: FnMut(&mut I) -> F, F: Future<Output = O>,721     pub fn iter_batched_ref<I, O, S, R, F>(&mut self, mut setup: S, mut routine: R, size: BatchSize)
722     where
723         S: FnMut() -> I,
724         R: FnMut(&mut I) -> F,
725         F: Future<Output = O>,
726     {
727         let AsyncBencher { b, runner } = self;
728         runner.block_on(async {
729             b.iterated = true;
730             let batch_size = size.iters_per_batch(b.iters);
731             assert!(batch_size != 0, "Batch size must not be zero.");
732             let time_start = Instant::now();
733             b.value = b.measurement.zero();
734 
735             if batch_size == 1 {
736                 for _ in 0..b.iters {
737                     let mut input = black_box(setup());
738 
739                     let start = b.measurement.start();
740                     let output = routine(&mut input).await;
741                     let end = b.measurement.end(start);
742                     b.value = b.measurement.add(&b.value, &end);
743 
744                     drop(black_box(output));
745                     drop(black_box(input));
746                 }
747             } else {
748                 let mut iteration_counter = 0;
749 
750                 while iteration_counter < b.iters {
751                     let batch_size = ::std::cmp::min(batch_size, b.iters - iteration_counter);
752 
753                     let inputs = black_box((0..batch_size).map(|_| setup()).collect::<Vec<_>>());
754                     let mut outputs = Vec::with_capacity(batch_size as usize);
755 
756                     let start = b.measurement.start();
757                     // Can't use .extend here like the sync version does
758                     for mut input in inputs {
759                         outputs.push(routine(&mut input).await);
760                     }
761                     let end = b.measurement.end(start);
762                     b.value = b.measurement.add(&b.value, &end);
763 
764                     black_box(outputs);
765 
766                     iteration_counter += batch_size;
767                 }
768             }
769             b.elapsed_time = time_start.elapsed();
770         });
771     }
772 }
773