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}