subms 0.9.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)]
#[path = "bench_loops_tests.rs"]
mod tests;