Skip to main content

Crate subms

Crate subms 

Source
Expand description

subms - tiny std-only perf harness. Records timed samples per stage and emits a stable JSON shape consumed by submillisecond.com.

§Pipeline

recipe -> SubMsPerfHarness -> SubMsBenchSummary -> { print, assert, JSON }

summarize turns the raw harness into a typed SubMsBenchSummary. print_summary, assert_p99_under, and summary_to_json are presenters / asserters that consume the summary - they never recompute stats.

§Example

use subms::{SubMsPerfHarness, summarize, print_summary, summary_to_json};

let mut h = SubMsPerfHarness::new("lsm-tree", "rust");
h.input("entries", &50_000.to_string());
h.input("bloom_mode", "on");
h.add_meta("sstables", "46");

let put = h.stage("put", 50_000);
for _ in 0..50_000 {
    put.time(|| { /* work under test */ });
}

let summary = summarize(&h);
print_summary(&summary, &mut std::io::stdout()).unwrap();
summary_to_json(&summary, &mut std::io::stdout()).unwrap();

§JSON shape (stable; matches the Java sibling jar)

{
  "workload": "lsm-tree",
  "lang": "rust",
  "timestamp": "2026-05-13T20:24:38Z",
  "inputs":  { "<k>": "<v>", ... },
  "meta":    { "<k>": "<v>", ... },
  "stages": {
    "<name>": {
      "count": <int>,
      "p50_ns": <int>, "p99_ns": <int>, "p999_ns": <int>, "max_ns": <int>,
      "mean_ns": <int>,
      "samples_ns": [<int>, ...]
    }
  }
}

Re-exports§

pub use bench::DEFAULT_REGRESSION_THRESHOLD_PCT;
pub use bench::SubMsBenchAssertion;
pub use bench::assert_p99_under;
pub use bench::contended_warmup;
pub use bench::diff_summary;
pub use bench::diff_summary_with;
pub use bench::diff_to_json;
pub use bench::format_ns;
pub use bench::print_diff;
pub use bench::print_summary;
pub use bench::print_sweep;
pub use bench::run_bench;
pub use bench::run_sweep;
pub use bench::summarize;
pub use bench::summarize_lean;
pub use bench::summarize_skipping;
pub use bench::summarize_sweep;
pub use bench::summarize_windowed;
pub use bench::summary_to_json;
pub use bench::sweep_to_json;
pub use bench_loops::bench_indexed_op;
pub use bench_loops::bench_keyed_op;
pub use bench_loops::bench_templated_op;
pub use growth::GROWTH_VERSION;
pub use growth::SubMsGrowthClass;
pub use growth::SubMsGrowthRecipe;
pub use growth::SubMsGrowthReport;
pub use growth::SubMsGrowthRound;
pub use growth::SubMsGrowthVerdict;
pub use growth::assert_growth_holds;
pub use growth::grow;
pub use growth::growth_to_json;
pub use params::parse_bool;
pub use params::parse_string;
pub use params::parse_u64;
pub use params::parse_usize;
pub use recipe::SubMsBenchParams;
pub use recipe::SubMsRecipe;
pub use recipe::benchmark;
pub use summary::SubMsBenchDiff;
pub use summary::SubMsBenchSummary;
pub use summary::SubMsBenchSweep;
pub use summary::SubMsMetricDiff;
pub use summary::SubMsStageDiff;
pub use summary::SubMsStageSummary;
pub use env::SubMsAppEnv;
pub use env::SubMsAppRegion;
pub use env::env_bool;
pub use env::env_f64;
pub use env::env_i64;
pub use env::env_or;
pub use env::env_str;
pub use env::env_u64;
pub use observer::ObservationCtx;
pub use observer::SubMsObserver;
pub use observer::SubMsStageKind;
pub use timer::SubMsTick;
pub use timer::SubMsTimer;
pub use timer::SubMsTimerCheckpoint;
pub use util::SubMsLcg;

Modules§

bench
Shared bench helpers. Pipeline:
bench_loops
Boilerplate-killer for perf examples. Most recipe benches follow one of two shapes:
env
App-context enums + tiny env-var utility surface. Std-only; the harness stays zero-dep. parse is exposed alongside from_env so tests can drive the matching logic directly without mutating process env.
growth
Storage-growth harness. Where the latency harness (crate::SubMsPerfHarness) answers “how fast is one op”, this answers “does the footprint stay bounded as work accumulates” - the leak / write-amplification axis.
observer
Observer hook: a no-op-by-default trait other code can register against a [SubMsPerfHarness] to receive samples and summaries as they happen.
params
Stdin param parsing helpers used by SubMsRecipe implementations.
recipe
SubMsRecipe trait + shared bench params.
summary
Structured bench summary - the typed counterpart to the standard subms JSON.
timer
Zero-dep autostart stopwatch with named checkpoints / milestones. Lives alongside the bench harness so callers can drop one in mid-loop without adding a dep.
util
Shared utilities.

Structs§

SubMsPacedStage
Coordinated-omission-corrected stage wrapper. Each SubMsPacedStage::time call blocks until its intended slot, runs the workload, then records the latency from the intended start time to end-of-op (so queue delay is reflected in the per-op latency, not silently dropped).
SubMsPerfHarness
A workload run. Owns raw samples + metadata only. Analysis and serialisation live in crate::bench - call summarize to lift this into a SubMsBenchSummary.
SubMsStage
Per-stage sample buffer + recorder. Optionally annotated with a SubMsStageKind that sibling adapters (e.g. subms-otel) use to pick histogram bucket boundaries.

Functions§

read_stdin_kv
Parse stdin key=value lines into a flat map. Skips blank lines and # comments.