fluxbench_core/bencher/
iter.rs1use super::engine::SampleRaw;
4use super::state::Bencher;
5use crate::measure::Timer;
6use std::hint::black_box;
7
8impl Bencher {
9 #[inline]
14 pub fn iter<T, F>(&mut self, mut f: F)
15 where
16 F: FnMut() -> T,
17 {
18 if !self.begin_iter() {
19 return;
20 }
21 self.run_loop(|b, n| {
22 let mut raw = SampleRaw::default();
23 b.alloc_reset();
24 let timer = Timer::start();
25 for _ in 0..n {
26 black_box(f());
27 }
28 let (ns, cycles) = timer.stop();
29 raw.add(ns, cycles, b.alloc_read());
30 raw
31 });
32 }
33}
34
35#[cfg(test)]
36mod tests {
37 use crate::bencher::test_support::{fixed, quick};
38 use crate::bencher::{Bencher, MeasureConfig, MeasureError, run_benchmark_loop};
39 use std::cell::Cell;
40 use std::sync::atomic::{AtomicUsize, Ordering};
41
42 #[test]
43 fn runner_called_once() {
44 let calls = AtomicUsize::new(0);
45 let result = run_benchmark_loop(Bencher::with_config(quick(10)), |b| {
46 calls.fetch_add(1, Ordering::Relaxed);
47 b.iter(|| 1_u64);
48 });
49 assert!(result.is_ok());
50 assert_eq!(calls.load(Ordering::Relaxed), 1);
51 }
52
53 #[test]
54 fn iter_calls_f_many_times_per_sample() {
55 let count = Cell::new(0_u64);
56 let result = run_benchmark_loop(Bencher::with_config(quick(10)), |b| {
57 b.iter(|| count.set(count.get() + 1));
58 })
59 .expect("measurement succeeds");
60
61 assert_eq!(result.samples.len(), 10);
62 assert!(result.iters_per_sample > 1);
63 assert!(count.get() > 100 * result.samples.len() as u64);
64 assert!(
65 result
66 .samples
67 .iter()
68 .all(|s| s.iterations == result.iters_per_sample)
69 );
70 assert_eq!(count.get(), result.iterations + result.warmup_iterations);
71 }
72
73 #[test]
74 fn sample_count_honoured_above_100() {
75 let result = run_benchmark_loop(Bencher::with_config(quick(250)), |b| b.iter(|| 1_u64))
76 .expect("measurement succeeds");
77 assert_eq!(result.samples.len(), 250);
78 assert!(!result.truncated);
79 }
80
81 #[test]
82 fn fixed_mode_samples_n() {
83 let count = Cell::new(0_u64);
84 let result = run_benchmark_loop(Bencher::with_config(fixed(10, 3)), |b| {
85 b.iter(|| count.set(count.get() + 1));
86 })
87 .expect("measurement succeeds");
88
89 assert_eq!(result.samples.len(), 10);
90 assert!(result.samples.iter().all(|s| s.iterations == 3));
91 assert_eq!(result.iterations, 30);
92 assert_eq!(result.warmup_iterations, 0);
93 assert_eq!(count.get(), 30);
94 }
95
96 #[test]
97 fn fixed_mode_500_samples_of_one_iteration() {
98 let result = run_benchmark_loop(Bencher::with_config(fixed(500, 1)), |b| b.iter(|| 3_u64))
99 .expect("measurement succeeds");
100 assert_eq!(result.samples.len(), 500);
101 assert_eq!(result.iterations, 500);
102 }
103
104 #[test]
105 fn subnanosecond_resolution() {
106 let result = run_benchmark_loop(Bencher::with_config(fixed(20, 10_000)), |b| {
107 b.iter(|| 1_u64);
108 })
109 .expect("measurement succeeds");
110
111 assert!(
114 result
115 .samples
116 .iter()
117 .any(|s| s.duration_nanos < 1.0 || s.duration_nanos.fract() != 0.0),
118 "samples: {:?}",
119 result.samples
120 );
121 }
122
123 #[test]
124 fn warmup_uses_constant_memory() {
125 let config = MeasureConfig {
126 warmup_ns: 20_000_000,
127 ..quick(10)
128 };
129 let mut bencher = Bencher::with_config(config);
130 bencher.iter(|| 1_u64);
131
132 assert!(bencher.warmup_iterations > 10_000);
134 assert_eq!(bencher.samples.len(), 10);
135 assert!(bencher.samples.capacity() <= 2 * 10);
136 }
137
138 #[test]
139 fn run_loop_respects_min_iterations() {
140 let config = MeasureConfig {
141 min_iterations: Some(100),
142 max_iterations: Some(100),
143 measurement_ns: 1,
144 ..quick(10)
145 };
146 let result = run_benchmark_loop(Bencher::with_config(config), |b| b.iter(|| 42_u64))
147 .expect("measurement succeeds");
148 assert_eq!(result.iterations, 100);
149 }
150
151 #[test]
152 fn run_loop_raises_max_to_min() {
153 let config = MeasureConfig {
154 min_iterations: Some(200),
155 max_iterations: Some(50),
156 measurement_ns: 1,
157 ..quick(10)
158 };
159 let result = run_benchmark_loop(Bencher::with_config(config), |b| b.iter(|| 7_u64))
160 .expect("measurement succeeds");
161 assert_eq!(result.iterations, 200);
162 }
163
164 #[test]
165 fn second_iter_call_returns_error() {
166 let second = Cell::new(0_u64);
167 let result = run_benchmark_loop(Bencher::with_config(fixed(2, 1)), |b| {
168 b.iter(|| 1_u64);
169 b.iter(|| second.set(second.get() + 1));
170 });
171 let err = result.err();
172 assert_eq!(err, Some(MeasureError::MultipleIterCalls));
173 assert_eq!(
174 err.map(|e| e.to_string()).as_deref(),
175 Some("benchmark body called more than one b.iter* method")
176 );
177 assert_eq!(second.get(), 0);
178 }
179
180 #[test]
181 fn no_iter_call_returns_error() {
182 let result = run_benchmark_loop(Bencher::with_config(fixed(2, 1)), |_| {});
183 let err = result.err();
184 assert_eq!(err, Some(MeasureError::NoIterCall));
185 assert_eq!(
186 err.map(|e| e.to_string()).as_deref(),
187 Some("benchmark body never called b.iter*")
188 );
189 }
190}