Skip to main content

fluxbench_core/bencher/
config.rs

1//! Measurement config, errors, and results.
2
3use fluxbench_ipc::{BenchmarkConfig, Sample, Throughput};
4
5/// Default number of samples to collect (matches Criterion)
6pub const DEFAULT_SAMPLE_COUNT: usize = fluxbench_ipc::DEFAULT_SAMPLE_COUNT as usize;
7
8/// Minimum sample count. Two samples give a defined variance.
9pub const MIN_SAMPLE_COUNT: usize = 2;
10
11/// Mode of iteration for the benchmark
12#[derive(Debug, Clone, Copy, PartialEq, Eq)]
13pub enum IterationMode {
14    /// Simple iteration - measure entire closure
15    Simple,
16    /// Iteration with setup - separate setup from measurement
17    WithSetup,
18    /// Iteration with teardown
19    WithTeardown,
20}
21
22/// Config for one benchmark measurement.
23#[derive(Debug, Clone, Copy, PartialEq, Eq)]
24pub struct MeasureConfig {
25    /// Warmup budget in nanoseconds. Fixed mode skips warmup.
26    pub warmup_ns: u64,
27    /// Measurement budget in nanoseconds. It sets the iterations per sample.
28    pub measurement_ns: u64,
29    /// Samples to collect. Values below `MIN_SAMPLE_COUNT` rise to it.
30    pub sample_count: usize,
31    /// Fixed iterations per sample. `Some` selects fixed mode: no warmup, no estimate.
32    pub fixed_iters_per_sample: Option<u64>,
33    /// Minimum total measurement iterations. Ignored in fixed mode.
34    pub min_iterations: Option<u64>,
35    /// Maximum total measurement iterations. Ignored in fixed mode.
36    pub max_iterations: Option<u64>,
37    /// Read the allocation counters around each timed region.
38    pub track_allocations: bool,
39}
40
41impl Default for MeasureConfig {
42    fn default() -> Self {
43        Self::from(&BenchmarkConfig::default())
44    }
45}
46
47impl From<&BenchmarkConfig> for MeasureConfig {
48    fn from(config: &BenchmarkConfig) -> Self {
49        Self {
50            warmup_ns: config.warmup_time_ns,
51            measurement_ns: config.measurement_time_ns,
52            sample_count: config.sample_count as usize,
53            fixed_iters_per_sample: config.iters_per_sample,
54            min_iterations: config.min_iterations,
55            max_iterations: config.max_iterations,
56            track_allocations: config.track_allocations,
57        }
58    }
59}
60
61impl MeasureConfig {
62    /// Raise the sample count to `MIN_SAMPLE_COUNT` and a fixed count of 0 to 1.
63    pub fn normalized(mut self) -> Self {
64        self.sample_count = self.sample_count.max(MIN_SAMPLE_COUNT);
65        self.fixed_iters_per_sample = self.fixed_iters_per_sample.map(|n| n.max(1));
66        self
67    }
68}
69
70/// Error from one benchmark measurement.
71#[derive(Debug, Clone, PartialEq, Eq, thiserror::Error)]
72pub enum MeasureError {
73    /// The benchmark body returned without calling an `iter*` method.
74    #[error("benchmark body never called b.iter*")]
75    NoIterCall,
76    /// The benchmark body called a second `iter*` method. The second call does not run.
77    #[error("benchmark body called more than one b.iter* method")]
78    MultipleIterCalls,
79    /// The measurement collected no samples.
80    #[error("benchmark produced zero samples")]
81    NoSamples,
82    /// The async runtime did not build, or `iter_async` ran inside a runtime context.
83    /// Only the `async` feature produces it.
84    #[error("async runtime error: {0}")]
85    AsyncRuntime(String),
86}
87
88/// Result of a single benchmark run
89#[derive(Debug, Clone)]
90pub struct BenchmarkResult {
91    /// Collected samples. Each holds per-iteration means over `iters_per_sample` calls.
92    pub samples: Vec<Sample>,
93    /// Routine calls in the measurement phase. Warmup calls are excluded.
94    pub iterations: u64,
95    /// Routine calls in the warmup phase
96    pub warmup_iterations: u64,
97    /// Sum of each sample's mean times its iterations, in nanoseconds
98    pub total_time_ns: u64,
99    /// Routine calls per sample
100    pub iters_per_sample: u64,
101    /// `true` when the hard deadline or a SIGTERM stop request ended measurement
102    /// before `sample_count` samples
103    pub truncated: bool,
104    /// Work per call the body set with `Bencher::throughput`. `None` without a call.
105    pub throughput: Option<Throughput>,
106}