Skip to main content

fluxbench_core/
group_config.rs

1//! Run config a `#[flux::group]` sets for its benchmarks.
2
3use fluxbench_ipc::Throughput;
4
5/// Run config a `#[flux::group]` sets for every benchmark in it and in its child groups.
6///
7/// Precedence per field: `#[flux::bench]` attribute, then the nearest group, then parent groups,
8/// then CLI flags and flux.toml, then the default.
9#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
10pub struct GroupConfig {
11    /// Warmup time in nanoseconds
12    pub warmup_ns: Option<u64>,
13    /// Measurement time in nanoseconds
14    pub measurement_ns: Option<u64>,
15    /// Target sample count
16    pub sample_count: Option<u64>,
17    /// Fixed iterations per sample. `Some` selects fixed mode.
18    pub iters_per_sample: Option<u64>,
19    /// Minimum total measurement iterations
20    pub min_iterations: Option<u64>,
21    /// Maximum total measurement iterations
22    pub max_iterations: Option<u64>,
23    /// Work per call for throughput rates
24    pub throughput: Option<Throughput>,
25}
26
27impl GroupConfig {
28    /// A config that sets nothing.
29    pub const NONE: GroupConfig = GroupConfig {
30        warmup_ns: None,
31        measurement_ns: None,
32        sample_count: None,
33        iters_per_sample: None,
34        min_iterations: None,
35        max_iterations: None,
36        throughput: None,
37    };
38
39    /// Keep each field set here. Take each unset field from `parent`.
40    pub fn or(self, parent: &GroupConfig) -> GroupConfig {
41        GroupConfig {
42            warmup_ns: self.warmup_ns.or(parent.warmup_ns),
43            measurement_ns: self.measurement_ns.or(parent.measurement_ns),
44            sample_count: self.sample_count.or(parent.sample_count),
45            iters_per_sample: self.iters_per_sample.or(parent.iters_per_sample),
46            min_iterations: self.min_iterations.or(parent.min_iterations),
47            max_iterations: self.max_iterations.or(parent.max_iterations),
48            throughput: self.throughput.or(parent.throughput),
49        }
50    }
51
52    /// Merge a group chain ordered nearest first. The nearest group wins per field.
53    pub fn merge_chain<'a>(chain: impl IntoIterator<Item = &'a GroupConfig>) -> GroupConfig {
54        chain
55            .into_iter()
56            .fold(GroupConfig::NONE, |merged, parent| merged.or(parent))
57    }
58}
59
60#[cfg(test)]
61mod tests {
62    use super::*;
63
64    #[test]
65    fn nearest_group_wins_per_field() {
66        let child = GroupConfig {
67            sample_count: Some(20),
68            ..GroupConfig::NONE
69        };
70        let parent = GroupConfig {
71            sample_count: Some(50),
72            warmup_ns: Some(7),
73            throughput: Some(Throughput::Bytes(1)),
74            ..GroupConfig::NONE
75        };
76        let root = GroupConfig {
77            warmup_ns: Some(9),
78            measurement_ns: Some(11),
79            throughput: Some(Throughput::Elements(2)),
80            ..GroupConfig::NONE
81        };
82        let merged = GroupConfig::merge_chain([&child, &parent, &root]);
83        assert_eq!(merged.sample_count, Some(20));
84        assert_eq!(merged.warmup_ns, Some(7));
85        assert_eq!(merged.measurement_ns, Some(11));
86        assert_eq!(merged.throughput, Some(Throughput::Bytes(1)));
87        assert_eq!(merged.iters_per_sample, None);
88    }
89
90    #[test]
91    fn empty_chain_sets_nothing() {
92        assert_eq!(GroupConfig::merge_chain([]), GroupConfig::NONE);
93        assert_eq!(GroupConfig::default(), GroupConfig::NONE);
94    }
95}