11//! CodSpeed addition: manual control over benchmark iteration counts.
22//!
3- //! `iter_manual *` lets the user pin down the exact number of measurement
3+ //! `iter_manual_setup *` lets the user pin down the exact number of measurement
44//! rounds, inner iterations per round, and warmup rounds. It bypasses
55//! Criterion's adaptive sampler entirely — see `routine.rs::sample` for the
66//! short-circuit.
77
8- use std:: time:: Instant ;
8+ use std:: time:: { Duration , Instant } ;
99
1010use codspeed:: instrument_hooks:: InstrumentHooks ;
1111
@@ -20,28 +20,55 @@ use crate::Bencher;
2020#[ cfg( feature = "async" ) ]
2121use std:: future:: Future ;
2222
23- /// Options for the [`iter_manual`](Bencher::iter_manual) family.
23+ /// Options for the [`iter_manual_setup`](Bencher::iter_manual_setup) family.
24+ ///
25+ /// Built with a consuming-self builder to match the rest of criterion's
26+ /// configuration APIs (e.g. [`Criterion`](crate::Criterion)).
2427#[ derive( Debug , Clone , Copy ) ]
2528pub struct IterManualOptions {
26- /// Number of measurement rounds (each produces one sample).
27- pub rounds : u64 ,
28- /// Number of routine invocations inside the measured region of each round.
29- pub iterations : u64 ,
30- /// Number of unmeasured warmup rounds run before measurement starts.
31- pub warmup_rounds : u64 ,
29+ rounds : u64 ,
30+ iterations : u64 ,
31+ warmup_iterations : u64 ,
3232}
3333
34- impl IterManualOptions {
35- /// Build options with the given rounds and iterations; `warmup_rounds` defaults to 0.
36- pub fn new ( rounds : u64 , iterations : u64 ) -> Self {
34+ impl Default for IterManualOptions {
35+ fn default ( ) -> Self {
3736 Self {
38- rounds,
39- iterations,
40- warmup_rounds : 0 ,
37+ rounds : 1 ,
38+ iterations : 1 ,
39+ warmup_iterations : 0 ,
4140 }
4241 }
4342}
4443
44+ impl IterManualOptions {
45+ /// Start with defaults: 1 round, 1 iteration per round, 0 warmup rounds.
46+ pub fn new ( ) -> Self {
47+ Self :: default ( )
48+ }
49+
50+ /// Number of measurement rounds (each produces one sample).
51+ #[ must_use]
52+ pub fn rounds ( mut self , rounds : u64 ) -> Self {
53+ self . rounds = rounds;
54+ self
55+ }
56+
57+ /// Number of routine invocations inside a measurement round.
58+ #[ must_use]
59+ pub fn iters ( mut self , iterations : u64 ) -> Self {
60+ self . iterations = iterations;
61+ self
62+ }
63+
64+ /// Number of unmeasured warmup iterations run before measurement starts.
65+ #[ must_use]
66+ pub fn warmup ( mut self , warmup_iterations : u64 ) -> Self {
67+ self . warmup_iterations = warmup_iterations;
68+ self
69+ }
70+ }
71+
4572/// Captured output of a manual run. Stored on the `Bencher` and read by
4673/// `routine.rs::sample` to short-circuit the adaptive sampler.
4774pub ( crate ) struct ManualMeasurement {
@@ -54,32 +81,40 @@ pub(crate) struct ManualMeasurement {
5481
5582impl < ' a , M : Measurement > Bencher < ' a , M > {
5683 /// Run `routine` exactly `opts.iterations` times inside each of `opts.rounds`
57- /// measurement rounds, optionally preceded by `opts.warmup_rounds` unmeasured rounds.
84+ /// measurement rounds, with a `setup` closure producing fresh input for
85+ /// each round (outside the measured region). Optionally preceded by
86+ /// `opts.warmup_rounds` unmeasured rounds.
5887 ///
5988 /// Criterion's adaptive sampler is bypassed for this benchmark.
6089 #[ inline( never) ]
61- pub fn iter_manual < O , R > ( & mut self , opts : IterManualOptions , mut routine : R )
62- where
63- R : FnMut ( ) -> O ,
64- {
65- self . iter_manual_setup_teardown ( opts, || ( ) , |_| routine ( ) , |_| ( ) ) ;
66- }
67-
68- /// Like [`iter_manual`](Self::iter_manual), with a `setup` closure producing
69- /// fresh input for each round. The routine borrows the input mutably.
70- #[ inline( never) ]
7190 pub fn iter_manual_setup < I , O , S , R > ( & mut self , opts : IterManualOptions , setup : S , routine : R )
7291 where
7392 S : FnMut ( ) -> I ,
7493 R : FnMut ( & mut I ) -> O ,
7594 {
76- self . iter_manual_setup_teardown ( opts, setup, routine, |_| ( ) ) ;
95+ self . __codspeed_root_frame__iter_manual_setup_teardown ( opts, setup, routine, |_| ( ) ) ;
7796 }
7897
7998 /// Like [`iter_manual_setup`](Self::iter_manual_setup), with a `teardown`
8099 /// closure called after each round, outside the measured region.
81100 #[ inline( never) ]
82101 pub fn iter_manual_setup_teardown < I , O , S , R , T > (
102+ & mut self ,
103+ opts : IterManualOptions ,
104+ setup : S ,
105+ routine : R ,
106+ teardown : T ,
107+ ) where
108+ S : FnMut ( ) -> I ,
109+ R : FnMut ( & mut I ) -> O ,
110+ T : FnMut ( I ) ,
111+ {
112+ self . __codspeed_root_frame__iter_manual_setup_teardown ( opts, setup, routine, teardown) ;
113+ }
114+
115+ #[ inline( never) ]
116+ #[ allow( missing_docs, non_snake_case) ]
117+ pub fn __codspeed_root_frame__iter_manual_setup_teardown < I , O , S , R , T > (
83118 & mut self ,
84119 opts : IterManualOptions ,
85120 mut setup : S ,
@@ -92,10 +127,9 @@ impl<'a, M: Measurement> Bencher<'a, M> {
92127 {
93128 self . iterated = true ;
94129
95- let bench_start = InstrumentHooks :: current_timestamp ( ) ;
96- let time_start = Instant :: now ( ) ;
130+ let mut elapsed_time = Duration :: ZERO ;
97131
98- for _ in 0 ..opts. warmup_rounds {
132+ for _ in 0 ..opts. warmup_iterations {
99133 let mut input = black_box ( setup ( ) ) ;
100134 for _ in 0 ..opts. iterations {
101135 black_box ( routine ( & mut input) ) ;
@@ -106,18 +140,23 @@ impl<'a, M: Measurement> Bencher<'a, M> {
106140 let mut samples = Vec :: with_capacity ( opts. rounds as usize ) ;
107141 for _ in 0 ..opts. rounds {
108142 let mut input = black_box ( setup ( ) ) ;
143+
144+ let bench_start = InstrumentHooks :: current_timestamp ( ) ;
145+ let round_start = Instant :: now ( ) ;
109146 let start = self . measurement . start ( ) ;
110147 for _ in 0 ..opts. iterations {
111148 black_box ( routine ( & mut input) ) ;
112149 }
113150 let value = self . measurement . end ( start) ;
151+ elapsed_time += round_start. elapsed ( ) ;
152+ let bench_end = InstrumentHooks :: current_timestamp ( ) ;
153+ InstrumentHooks :: instance ( ) . add_benchmark_timestamps ( bench_start, bench_end) ;
154+
114155 teardown ( input) ;
115156 samples. push ( self . measurement . to_f64 ( & value) ) ;
116157 }
117158
118- self . elapsed_time = time_start. elapsed ( ) ;
119- let bench_end = InstrumentHooks :: current_timestamp ( ) ;
120- InstrumentHooks :: instance ( ) . add_benchmark_timestamps ( bench_start, bench_end) ;
159+ self . elapsed_time = elapsed_time;
121160
122161 self . codspeed_manual = Some ( ManualMeasurement {
123162 samples,
@@ -128,16 +167,6 @@ impl<'a, M: Measurement> Bencher<'a, M> {
128167
129168#[ cfg( feature = "async" ) ]
130169impl < ' a , ' b , A : AsyncExecutor , M : Measurement > AsyncBencher < ' a , ' b , A , M > {
131- /// Async/await variant of [`Bencher::iter_manual`].
132- #[ inline( never) ]
133- pub fn iter_manual < O , R , F > ( & mut self , opts : IterManualOptions , mut routine : R )
134- where
135- R : FnMut ( ) -> F ,
136- F : Future < Output = O > ,
137- {
138- self . iter_manual_setup_teardown ( opts, || ( ) , |_| routine ( ) , |_| std:: future:: ready ( ( ) ) ) ;
139- }
140-
141170 /// Async/await variant of [`Bencher::iter_manual_setup`].
142171 #[ inline( never) ]
143172 pub fn iter_manual_setup < I , O , S , R , F > (
@@ -150,12 +179,32 @@ impl<'a, 'b, A: AsyncExecutor, M: Measurement> AsyncBencher<'a, 'b, A, M> {
150179 R : FnMut ( & mut I ) -> F ,
151180 F : Future < Output = O > ,
152181 {
153- self . iter_manual_setup_teardown ( opts, setup, routine, |_| std:: future:: ready ( ( ) ) ) ;
182+ self . __codspeed_root_frame__iter_manual_setup_teardown ( opts, setup, routine, |_| {
183+ std:: future:: ready ( ( ) )
184+ } ) ;
154185 }
155186
156187 /// Async/await variant of [`Bencher::iter_manual_setup_teardown`].
157188 #[ inline( never) ]
158189 pub fn iter_manual_setup_teardown < I , O , S , R , T , RF , TF > (
190+ & mut self ,
191+ opts : IterManualOptions ,
192+ setup : S ,
193+ routine : R ,
194+ teardown : T ,
195+ ) where
196+ S : FnMut ( ) -> I ,
197+ R : FnMut ( & mut I ) -> RF ,
198+ T : FnMut ( I ) -> TF ,
199+ RF : Future < Output = O > ,
200+ TF : Future < Output = ( ) > ,
201+ {
202+ self . __codspeed_root_frame__iter_manual_setup_teardown ( opts, setup, routine, teardown) ;
203+ }
204+
205+ #[ inline( never) ]
206+ #[ allow( missing_docs, non_snake_case) ]
207+ pub fn __codspeed_root_frame__iter_manual_setup_teardown < I , O , S , R , T , RF , TF > (
159208 & mut self ,
160209 opts : IterManualOptions ,
161210 mut setup : S ,
@@ -172,8 +221,7 @@ impl<'a, 'b, A: AsyncExecutor, M: Measurement> AsyncBencher<'a, 'b, A, M> {
172221 runner. block_on ( async {
173222 b. iterated = true ;
174223
175- let bench_start = InstrumentHooks :: current_timestamp ( ) ;
176- let time_start = Instant :: now ( ) ;
224+ let mut elapsed_time = Duration :: ZERO ;
177225
178226 for _ in 0 ..opts. warmup_rounds {
179227 let mut input = black_box ( setup ( ) ) ;
@@ -186,18 +234,23 @@ impl<'a, 'b, A: AsyncExecutor, M: Measurement> AsyncBencher<'a, 'b, A, M> {
186234 let mut samples = Vec :: with_capacity ( opts. rounds as usize ) ;
187235 for _ in 0 ..opts. rounds {
188236 let mut input = black_box ( setup ( ) ) ;
237+
238+ let bench_start = InstrumentHooks :: current_timestamp ( ) ;
239+ let round_start = Instant :: now ( ) ;
189240 let start = b. measurement . start ( ) ;
190241 for _ in 0 ..opts. iterations {
191242 black_box ( routine ( & mut input) . await ) ;
192243 }
193244 let value = b. measurement . end ( start) ;
245+ elapsed_time += round_start. elapsed ( ) ;
246+ let bench_end = InstrumentHooks :: current_timestamp ( ) ;
247+ InstrumentHooks :: instance ( ) . add_benchmark_timestamps ( bench_start, bench_end) ;
248+
194249 teardown ( input) . await ;
195250 samples. push ( b. measurement . to_f64 ( & value) ) ;
196251 }
197252
198- b. elapsed_time = time_start. elapsed ( ) ;
199- let bench_end = InstrumentHooks :: current_timestamp ( ) ;
200- InstrumentHooks :: instance ( ) . add_benchmark_timestamps ( bench_start, bench_end) ;
253+ b. elapsed_time = elapsed_time;
201254
202255 b. codspeed_manual = Some ( ManualMeasurement {
203256 samples,
0 commit comments