codspeed_criterion_compat_walltime/
bencher.rs

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