Skip to main content

fluxbench_core/bencher/
batch_size.rs

1//! `BatchSize`: how many inputs `iter_batched` builds before one timed batch.
2
3/// Most inputs a `BatchSize::SmallInput` batch holds. `iter_with_setup` uses it too.
4///
5/// Peak memory is at most this many live inputs plus this many live outputs.
6pub const SETUP_CHUNK: u64 = 1024;
7
8/// Most inputs a `BatchSize::LargeInput` batch holds.
9pub const LARGE_INPUT_CHUNK: u64 = 16;
10
11/// Inputs per timed batch for `iter_batched` and `iter_batched_ref`.
12///
13/// Setup builds a whole batch untimed. Then one timer covers the routine over that batch.
14/// Larger batches spread the timer cost over more calls. Smaller batches hold less memory.
15#[derive(Debug, Clone, Copy, PartialEq, Eq)]
16pub enum BatchSize {
17    /// Up to `SETUP_CHUNK` (1024) inputs per batch. Fits inputs that are cheap to hold.
18    SmallInput,
19    /// Up to `LARGE_INPUT_CHUNK` (16) inputs per batch. Fits inputs that hold a lot of memory.
20    LargeInput,
21    /// One input per batch. Each call gets its own timer. Overhead per call is highest.
22    PerIteration,
23    /// Split each sample's calls into this many batches. 0 counts as 1.
24    NumBatches(u64),
25    /// Exactly this many inputs per batch, capped at the sample's calls. 0 counts as 1.
26    NumIterations(u64),
27}
28
29impl BatchSize {
30    /// Inputs per batch for a sample of `iters` calls. Always in `1..=max(iters, 1)`.
31    pub(super) fn batch_len(self, iters: u64) -> u64 {
32        let len = match self {
33            BatchSize::SmallInput => SETUP_CHUNK,
34            BatchSize::LargeInput => LARGE_INPUT_CHUNK,
35            BatchSize::PerIteration => 1,
36            BatchSize::NumBatches(batches) => iters.div_ceil(batches.max(1)),
37            BatchSize::NumIterations(size) => size,
38        };
39        len.clamp(1, iters.max(1))
40    }
41}
42
43#[cfg(test)]
44mod tests {
45    use super::*;
46
47    #[test]
48    fn fixed_sizes_cap_at_the_sample() {
49        assert_eq!(BatchSize::SmallInput.batch_len(5_000), 1024);
50        assert_eq!(BatchSize::SmallInput.batch_len(10), 10);
51        assert_eq!(BatchSize::LargeInput.batch_len(5_000), 16);
52        assert_eq!(BatchSize::LargeInput.batch_len(3), 3);
53        assert_eq!(BatchSize::PerIteration.batch_len(5_000), 1);
54    }
55
56    #[test]
57    fn num_batches_rounds_up() {
58        assert_eq!(BatchSize::NumBatches(4).batch_len(10), 3);
59        assert_eq!(BatchSize::NumBatches(1).batch_len(10), 10);
60        assert_eq!(BatchSize::NumBatches(0).batch_len(10), 10);
61        assert_eq!(BatchSize::NumBatches(100).batch_len(10), 1);
62    }
63
64    #[test]
65    fn num_iterations_clamps() {
66        assert_eq!(BatchSize::NumIterations(7).batch_len(10), 7);
67        assert_eq!(BatchSize::NumIterations(70).batch_len(10), 10);
68        assert_eq!(BatchSize::NumIterations(0).batch_len(10), 1);
69    }
70
71    #[test]
72    fn zero_iters_still_gives_one() {
73        assert_eq!(BatchSize::SmallInput.batch_len(0), 1);
74    }
75}