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}