pub struct SubMsPerfHarness { /* private fields */ }Expand description
A workload run. Owns raw samples + metadata only. Analysis and serialisation
live in crate::bench - call summarize to lift this into a
SubMsBenchSummary.
Implementations§
Source§impl SubMsPerfHarness
impl SubMsPerfHarness
Sourcepub fn new(workload: &str, lang: &str) -> Self
pub fn new(workload: &str, lang: &str) -> Self
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 set_sample_cap(&mut self, cap: usize) -> &mut Self
pub fn set_sample_cap(&mut self, cap: usize) -> &mut Self
Max points kept in each stage’s emitted samples_ns timeline. Default
500; crate::benchmark sets it from crate::SubMsBenchParams::sample_cap.
Clamped to at least 1.
Sourcepub fn sample_cap(&self) -> usize
pub fn sample_cap(&self) -> usize
The configured samples_ns downsample cap (see Self::set_sample_cap).
Sourcepub fn input(&mut self, key: &str, value: &str) -> &mut Self
pub fn input(&mut self, key: &str, value: &str) -> &mut Self
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 add_meta(&mut self, key: &str, value: &str) -> &mut Self
pub fn add_meta(&mut self, key: &str, value: &str) -> &mut Self
Set a meta field. Renamed from {@code meta} so the {@link Self::meta} getter can keep its symmetric name with Java’s getter.
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 stage(&mut self, name: &str, capacity: usize) -> &mut SubMsStage
pub fn stage(&mut self, name: &str, capacity: usize) -> &mut SubMsStage
Create a stage; record samples via SubMsStage::time or SubMsStage::record.
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 with_observer(self, observer: Arc<dyn SubMsObserver>) -> Self
pub fn with_observer(self, observer: Arc<dyn SubMsObserver>) -> Self
Register an observer to receive every recorded sample and the post-bench summary. Replaces any existing observer; updates already- created stages so they fire the new observer too. Returns self for builder-style chaining.
Sourcepub fn set_observer(
&mut self,
observer: Option<Arc<dyn SubMsObserver>>,
) -> &mut Self
pub fn set_observer( &mut self, observer: Option<Arc<dyn SubMsObserver>>, ) -> &mut Self
Mutable setter for late wiring. None clears the observer.
Sourcepub fn observer(&self) -> Option<&Arc<dyn SubMsObserver>>
pub fn observer(&self) -> Option<&Arc<dyn SubMsObserver>>
Read the currently-registered observer, if any. Mostly for tests.
Sourcepub fn stage_mut(&mut self, name: &str) -> Option<&mut SubMsStage>
pub fn stage_mut(&mut self, name: &str) -> Option<&mut SubMsStage>
Borrow a previously-created stage by name.
pub fn stage_by_name(&self, name: &str) -> Option<&SubMsStage>
pub fn stages(&self) -> &[SubMsStage]
pub fn workload(&self) -> &str
pub fn lang(&self) -> &str
pub fn inputs(&self) -> &BTreeMap<String, String>
pub fn meta(&self) -> &BTreeMap<String, String>
Sourcepub fn timestamp(&self) -> String
pub fn timestamp(&self) -> String
ISO-8601 seconds-precision timestamp captured at call time. Matches the
on-disk JSON’s timestamp field.
Sourcepub fn write_json<W: Write>(&self, out: &mut W) -> Result<()>
pub fn write_json<W: Write>(&self, out: &mut W) -> Result<()>
Back-compat: summarise + emit JSON in the standard subms JSON shape. New
code should call summarize then summary_to_json so the
analyser is explicit.
Sourcepub fn discard_stage(&mut self, name: &str)
pub fn discard_stage(&mut self, name: &str)
Drop a stage if you never recorded into it.