subms 0.5.1

The sub-millisecond perf harness for Rust. Zero-dependency std-only library that records timed samples per stage, computes percentiles, supports coordinated-omission correction, runs scale sweeps, and emits a stable JSON contract. Byte-equivalent to the Java sibling com.submillisecond:subms.
Documentation
//! Boilerplate-killer for perf examples. Most recipe benches follow
//! one of two shapes:
//!
//! 1. **Keyed**: generate N keys with [`SubMsLcg`], time each `op(&key)`.
//!    Common for hash maps, bloom filters, caches, dedup gates.
//! 2. **Indexed**: call `op(i)` for `i in 0..N`, time each. Common for
//!    sequential workloads (arena allocations, ring-buffer enqueues,
//!    fixed-size record reads).
//!
//! Both shapes used to repeat ~6 lines per stage in every
//! `examples/perf_main.rs`. These helpers collapse that to one call.
//!
//! ```no_run
//! use subms::{SubMsLcg, SubMsPerfHarness, bench_keyed_op, bench_indexed_op};
//!
//! let mut h = SubMsPerfHarness::new("my-recipe", "rust");
//! let mut data_structure = std::collections::HashMap::new();
//!
//! // 50k keyed adds:
//! bench_keyed_op(&mut h, "add", 50_000, 0, |key| {
//!     data_structure.insert(key.to_string(), 1u32);
//! });
//!
//! // 50k indexed ops:
//! bench_indexed_op(&mut h, "scan", 50_000, |i| {
//!     let _ = data_structure.get(&format!("k{i}"));
//! });
//! ```

use crate::{SubMsLcg, SubMsPerfHarness};

/// Run `count` timed invocations of `op` against keys generated by a
/// deterministic LCG seeded with `seed`. Each key is formatted as
/// `"k{u32}"`. The `op` closure receives the key string by reference.
///
/// Registers a stage named `stage_name` on the harness and records
/// one sample per invocation. Use the same seed across reads/writes
/// (e.g. `add` then `lookup`) so the key universes match.
pub fn bench_keyed_op<F>(
    h: &mut SubMsPerfHarness,
    stage_name: &str,
    count: usize,
    seed: u64,
    mut op: F,
) where
    F: FnMut(&str),
{
    let mut rng = SubMsLcg::new(seed);
    let stage = h.stage(stage_name, count);
    for _ in 0..count {
        let key = format!("k{}", rng.next_u32());
        stage.time(|| op(&key));
    }
}

/// Run `count` timed invocations of `op(i)` for `i in 0..count`. Use
/// for indexed sequential workloads (sequential ID generation, fixed
/// record reads, arena allocations).
pub fn bench_indexed_op<F>(h: &mut SubMsPerfHarness, stage_name: &str, count: usize, mut op: F)
where
    F: FnMut(usize),
{
    let stage = h.stage(stage_name, count);
    for i in 0..count {
        stage.time(|| op(i));
    }
}

/// Run `count` timed invocations of `op` against keys formatted from a
/// caller-provided template. Useful when you specifically want
/// negative-lookup keys ("miss-{i}", "absent-{i}") that don't overlap
/// the positive-lookup universe.
pub fn bench_templated_op<F>(
    h: &mut SubMsPerfHarness,
    stage_name: &str,
    count: usize,
    template: &str,
    mut op: F,
) where
    F: FnMut(&str),
{
    let stage = h.stage(stage_name, count);
    for i in 0..count {
        let key = template.replace("{}", &i.to_string());
        stage.time(|| op(&key));
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn keyed_op_records_count_samples() {
        let mut h = SubMsPerfHarness::new("test", "rust");
        let mut hits = 0usize;
        bench_keyed_op(&mut h, "add", 100, 42, |_key| hits += 1);
        assert_eq!(hits, 100);
        assert_eq!(h.stage_by_name("add").unwrap().samples().len(), 100);
    }

    #[test]
    fn keyed_op_is_deterministic_under_same_seed() {
        let mut h1 = SubMsPerfHarness::new("a", "rust");
        let mut h2 = SubMsPerfHarness::new("b", "rust");
        let mut seen1: Vec<String> = Vec::new();
        let mut seen2: Vec<String> = Vec::new();
        bench_keyed_op(&mut h1, "x", 20, 7, |k| seen1.push(k.to_string()));
        bench_keyed_op(&mut h2, "x", 20, 7, |k| seen2.push(k.to_string()));
        assert_eq!(seen1, seen2);
    }

    #[test]
    fn indexed_op_passes_sequential_indices() {
        let mut h = SubMsPerfHarness::new("test", "rust");
        let mut last = -1i64;
        let mut count = 0usize;
        bench_indexed_op(&mut h, "scan", 50, |i| {
            assert!((i as i64) > last);
            last = i as i64;
            count += 1;
        });
        assert_eq!(count, 50);
    }

    #[test]
    fn templated_op_substitutes_index() {
        let mut h = SubMsPerfHarness::new("test", "rust");
        let mut keys = Vec::new();
        bench_templated_op(&mut h, "miss", 5, "absent-{}", |k| {
            keys.push(k.to_string());
        });
        assert_eq!(
            keys,
            vec!["absent-0", "absent-1", "absent-2", "absent-3", "absent-4"]
        );
    }
}