pub struct SubMsStage { /* private fields */ }Expand description
Per-stage sample buffer + recorder. Optionally annotated with a
SubMsStageKind that sibling adapters (e.g. subms-otel) use to pick
histogram bucket boundaries.
Implementations§
Source§impl SubMsStage
impl SubMsStage
Sourcepub fn with_kind(&mut self, kind: SubMsStageKind) -> &mut Self
pub fn with_kind(&mut self, kind: SubMsStageKind) -> &mut Self
Annotate this stage’s kind so observers can pick fitting histogram
buckets. Default is SubMsStageKind::Unspecified. Chainable.
Sourcepub fn record(&mut self, ns: u64)
pub fn record(&mut self, ns: u64)
Record an explicit duration in nanoseconds. Also fires the registered observer (if any).
Examples found in repository?
26fn main() {
27 let mut h = SubMsPerfHarness::new("subms-self-bench", "rust");
28 h.input("samples_per_stage", "50000");
29 h.add_meta("rust_version", env!("CARGO_PKG_RUST_VERSION"));
30 h.add_meta("crate_version", env!("CARGO_PKG_VERSION"));
31
32 // 1. time_closure: cost of `stage.time(|| {})` with an empty closure.
33 // This is the per-iteration overhead any user pays when they wrap
34 // their hot path with `stage.time(...)`.
35 {
36 let s = h.stage("time_closure", 50_000);
37 for _ in 0..50_000 {
38 s.time(|| black_box(()));
39 }
40 }
41
42 // 2. record_ns: cost of `stage.record(ns)` alone. No timer, just the
43 // Vec push. Strict lower bound on per-sample bookkeeping.
44 {
45 let s = h.stage("record_ns", 50_000);
46 for i in 0..50_000u64 {
47 s.record(black_box(100 + (i & 63)));
48 }
49 }
50
51 // 3. summarize: sort + percentile extraction. Cost of producing one
52 // SubMsBenchSummary. We use SubMsTimer in a parallel harness so the
53 // measurement of the measurement isn't recursive.
54 {
55 let summary_target = build_sample_harness(50_000);
56 let s = h.stage("summarize", 100);
57 for _ in 0..100 {
58 s.time(|| {
59 let summary = summarize(black_box(&summary_target));
60 black_box(summary);
61 });
62 }
63 }
64
65 // 4. summary_to_json: serialise to the canonical JSON shape. Cost paid
66 // by every CI run that uploads results.
67 {
68 let summary = summarize(&build_sample_harness(50_000));
69 let s = h.stage("summary_to_json", 100);
70 for _ in 0..100 {
71 s.time(|| {
72 let mut buf = Cursor::new(Vec::with_capacity(64 * 1024));
73 summary_to_json(black_box(&summary), &mut buf).unwrap();
74 let _ = buf.flush();
75 black_box(buf);
76 });
77 }
78 }
79
80 // 5. diff_summary: regression-diff math between two SubMsBenchSummary
81 // objects. Cost paid by every PR-time gate.
82 {
83 let base: SubMsBenchSummary = summarize(&build_sample_harness(50_000));
84 let cand: SubMsBenchSummary = summarize(&build_sample_harness(50_000));
85 let s = h.stage("diff_summary", 1_000);
86 for _ in 0..1_000 {
87 s.time(|| {
88 let d = subms::diff_summary(black_box(&base), black_box(&cand));
89 black_box(d);
90 });
91 }
92 }
93
94 // Cross-check: SubMsTimer itself rolls a quick checkpoint walk so the
95 // timer's marking overhead is also visible from this perf JSON's meta.
96 let mut t = SubMsTimer::new("self-bench-wall");
97 t.mark("stages-complete");
98 t.stop("emitting-json");
99 h.add_meta("self_bench_wall_ns", &t.elapsed_ns().to_string());
100
101 let summary = summarize(&h);
102 summary_to_json(&summary, &mut std::io::stdout()).unwrap();
103}
104
105/// Build a SubMsPerfHarness pre-populated with a synthetic stage's worth of
106/// samples - used as fodder for the summarize / json / diff stages above.
107fn build_sample_harness(n: usize) -> SubMsPerfHarness {
108 let mut h = SubMsPerfHarness::new("fixture", "rust");
109 let s = h.stage("fixture-stage", n);
110 for i in 0..n as u64 {
111 // Synthetic log-ish distribution: most samples small, a tail
112 // exercises percentile sort + lookup.
113 let v = if i % 1000 == 0 {
114 10_000 + (i & 1023)
115 } else {
116 100 + (i & 63)
117 };
118 s.record(v);
119 }
120 h
121}Sourcepub fn time<F: FnOnce() -> R, R>(&mut self, f: F) -> R
pub fn time<F: FnOnce() -> R, R>(&mut self, f: F) -> R
Time a closure and record its duration.
Examples found in repository?
26fn main() {
27 let mut h = SubMsPerfHarness::new("subms-self-bench", "rust");
28 h.input("samples_per_stage", "50000");
29 h.add_meta("rust_version", env!("CARGO_PKG_RUST_VERSION"));
30 h.add_meta("crate_version", env!("CARGO_PKG_VERSION"));
31
32 // 1. time_closure: cost of `stage.time(|| {})` with an empty closure.
33 // This is the per-iteration overhead any user pays when they wrap
34 // their hot path with `stage.time(...)`.
35 {
36 let s = h.stage("time_closure", 50_000);
37 for _ in 0..50_000 {
38 s.time(|| black_box(()));
39 }
40 }
41
42 // 2. record_ns: cost of `stage.record(ns)` alone. No timer, just the
43 // Vec push. Strict lower bound on per-sample bookkeeping.
44 {
45 let s = h.stage("record_ns", 50_000);
46 for i in 0..50_000u64 {
47 s.record(black_box(100 + (i & 63)));
48 }
49 }
50
51 // 3. summarize: sort + percentile extraction. Cost of producing one
52 // SubMsBenchSummary. We use SubMsTimer in a parallel harness so the
53 // measurement of the measurement isn't recursive.
54 {
55 let summary_target = build_sample_harness(50_000);
56 let s = h.stage("summarize", 100);
57 for _ in 0..100 {
58 s.time(|| {
59 let summary = summarize(black_box(&summary_target));
60 black_box(summary);
61 });
62 }
63 }
64
65 // 4. summary_to_json: serialise to the canonical JSON shape. Cost paid
66 // by every CI run that uploads results.
67 {
68 let summary = summarize(&build_sample_harness(50_000));
69 let s = h.stage("summary_to_json", 100);
70 for _ in 0..100 {
71 s.time(|| {
72 let mut buf = Cursor::new(Vec::with_capacity(64 * 1024));
73 summary_to_json(black_box(&summary), &mut buf).unwrap();
74 let _ = buf.flush();
75 black_box(buf);
76 });
77 }
78 }
79
80 // 5. diff_summary: regression-diff math between two SubMsBenchSummary
81 // objects. Cost paid by every PR-time gate.
82 {
83 let base: SubMsBenchSummary = summarize(&build_sample_harness(50_000));
84 let cand: SubMsBenchSummary = summarize(&build_sample_harness(50_000));
85 let s = h.stage("diff_summary", 1_000);
86 for _ in 0..1_000 {
87 s.time(|| {
88 let d = subms::diff_summary(black_box(&base), black_box(&cand));
89 black_box(d);
90 });
91 }
92 }
93
94 // Cross-check: SubMsTimer itself rolls a quick checkpoint walk so the
95 // timer's marking overhead is also visible from this perf JSON's meta.
96 let mut t = SubMsTimer::new("self-bench-wall");
97 t.mark("stages-complete");
98 t.stop("emitting-json");
99 h.add_meta("self_bench_wall_ns", &t.elapsed_ns().to_string());
100
101 let summary = summarize(&h);
102 summary_to_json(&summary, &mut std::io::stdout()).unwrap();
103}Sourcepub fn warm_then_time<F: FnMut(usize)>(
&mut self,
warmup: usize,
measured: usize,
op: F,
)
pub fn warm_then_time<F: FnMut(usize)>( &mut self, warmup: usize, measured: usize, op: F, )
Warm, then record measured timed samples of op. Runs op for
warmup untimed iterations first, then times measured more. op
receives the iteration index on both passes (warmup: 0..warmup,
measured: 0..measured); index a shorter input with i % len.
On this AOT-compiled side the warmup mainly primes caches and the
branch predictor. The Java counterpart warmThenTime carries the
real weight: it drives HotSpot to C2 (and lets escape analysis elide
short-lived allocations) before any sample is recorded, without which
a JIT-cold low-iteration stage reads orders of magnitude slow. The two
harnesses expose the method symmetrically so a bench reads the same in
either language.
Sourcepub fn with_pacing(&mut self, target_ops_per_second: f64) -> SubMsPacedStage<'_>
pub fn with_pacing(&mut self, target_ops_per_second: f64) -> SubMsPacedStage<'_>
Wrap the stage in a coordinated-omission-corrected paced recorder. Each
SubMsPacedStage::time call blocks until its intended slot, runs the
workload, then records latency from the intended start time, which
folds queue delay into the per-op number - the correction
HdrHistogram exists for.
let mut h = SubMsPerfHarness::new("queue", "rust");
let stage = h.stage("offer", 100_000);
let mut paced = stage.with_pacing(10_000.0); // target 10k ops/sec
for _ in 0..100_000 { paced.time(|| do_work()); }