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_config::SubMsBenchConfig;pub use bench_config::SubMsCpuPin;pub use bench_loops::bench_indexed_op;pub use bench_loops::bench_keyed_op;pub use bench_loops::bench_templated_op;pub use feature::Json;pub use feature::SubMsFeatureCategory;pub use feature::SubMsFeatureManifest;pub use feature::SubMsP99Source;pub use feature::SubMsStageClass;pub use feature::classify_feature;pub use feature::parse_json;pub use feature::roll_up_stages;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_
config - Per-recipe bench configuration - the typed view of a recipe’s
.subms/perf/controls.json. - 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.
parseis exposed alongsidefrom_envso tests can drive the matching logic directly without mutating process env. - feature
- Per-feature latency classification + manifest.
- 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
crate::SubMsPerfHarnessto receive samples and summaries as they happen. - params
- Stdin param parsing helpers used by
SubMsRecipeimplementations. - recipe
SubMsRecipetrait + 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§
- SubMs
Paced Stage - Coordinated-omission-corrected stage wrapper. Each
SubMsPacedStage::timecall 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). - SubMs
Perf Harness - A workload run. Owns raw samples + metadata only. Analysis and serialisation
live in
crate::bench- callsummarizeto lift this into aSubMsBenchSummary. - SubMs
Stage - Per-stage sample buffer + recorder. Optionally annotated with a
SubMsStageKindthat sibling adapters (e.g.subms-otel) use to pick histogram bucket boundaries.
Functions§
- read_
stdin_ kv - Parse stdin
key=valuelines into a flat map. Skips blank lines and#comments.