WARNING: THIS SITE IS A MIRROR OF GITHUB.COM / IT CANNOT LOGIN OR REGISTER ACCOUNTS / THE CONTENTS ARE PROVIDED AS-IS / THIS SITE ASSUMES NO RESPONSIBILITY FOR ANY DISPLAYED CONTENT OR LINKS / IF YOU FOUND SOMETHING MAY NOT GOOD FOR EVERYONE, CONTACT ADMIN AT ilovescratch@foxmail.com
Skip to content

Commit 31bd2de

Browse files
committed
Use single field for sightglass_data::Engine
This simplifies consuming and building measurement and other related structures but at the cost of somewhat more complex serialization. This is largely due to our use of CSV as an input/output format and rust-csv's longstanding inability to handle flattened structures. See BurntSushi/rust-csv#98
1 parent 3c81ea5 commit 31bd2de

File tree

12 files changed

+420
-205
lines changed

12 files changed

+420
-205
lines changed

crates/analysis/src/effect_size.rs

Lines changed: 11 additions & 75 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
use crate::keys::KeyBuilder;
22
use anyhow::Result;
3-
use sightglass_data::{EffectSize, Measurement, Phase, Summary};
4-
use std::{borrow::Cow, collections::BTreeSet, io::Write};
3+
use sightglass_data::{EffectSize, Engine, Measurement, Phase, Summary};
4+
use std::{collections::BTreeSet, io::Write};
55

66
/// Find the effect size (and confidence interval) of between two different
77
/// engines (i.e. two different commits of Wasmtime).
@@ -35,10 +35,7 @@ pub fn calculate<'a>(
3535
let key_measurements: Vec<_> = measurements.iter().filter(|m| key.matches(m)).collect();
3636

3737
// NB: `BTreeSet` so they're always sorted.
38-
let engines: BTreeSet<_> = key_measurements
39-
.iter()
40-
.map(|m| (&m.engine, &m.engine_flags))
41-
.collect();
38+
let engines: BTreeSet<_> = key_measurements.iter().map(|m| &m.engine).collect();
4239
anyhow::ensure!(
4340
engines.len() == 2,
4441
"Can only test significance between exactly two different engines. Found {} \
@@ -47,17 +44,17 @@ pub fn calculate<'a>(
4744
);
4845

4946
let mut engines = engines.into_iter();
50-
let (engine_a, engine_a_flags) = engines.next().unwrap();
51-
let (engine_b, engine_b_flags) = engines.next().unwrap();
47+
let engine_a = engines.next().unwrap();
48+
let engine_b = engines.next().unwrap();
5249

5350
let a: behrens_fisher::Stats = key_measurements
5451
.iter()
55-
.filter(|m| m.engine.as_ref() == engine_a && &m.engine_flags == engine_a_flags)
52+
.filter(|m| &m.engine == engine_a)
5653
.map(|m| m.count as f64)
5754
.collect();
5855
let b: behrens_fisher::Stats = key_measurements
5956
.iter()
60-
.filter(|m| m.engine.as_ref() == engine_b && &m.engine_flags == engine_b_flags)
57+
.filter(|m| &m.engine == engine_b)
6158
.map(|m| m.count as f64)
6259
.collect();
6360

@@ -68,10 +65,8 @@ pub fn calculate<'a>(
6865
phase: key.phase.unwrap(),
6966
event: key.event.unwrap(),
7067
a_engine: engine_a.clone(),
71-
a_engine_flags: engine_a_flags.clone(),
7268
a_mean: a.mean,
7369
b_engine: engine_b.clone(),
74-
b_engine_flags: engine_b_flags.clone(),
7570
b_mean: b.mean,
7671
significance_level,
7772
half_width_confidence_interval: ci,
@@ -81,18 +76,6 @@ pub fn calculate<'a>(
8176
Ok(results)
8277
}
8378

84-
fn engine_label(engine: &str, engine_flags: &Option<Cow<str>>) -> String {
85-
format!(
86-
"{}{}",
87-
engine,
88-
if let Some(ef) = engine_flags {
89-
format!(" ({ef})")
90-
} else {
91-
"".into()
92-
}
93-
)
94-
}
95-
9679
/// Write a vector of [EffectSize] structures to the passed `output_file` in human-readable form.
9780
/// The `summaries` are needed
9881
pub fn write(
@@ -120,50 +103,13 @@ pub fn write(
120103
)?;
121104
writeln!(output_file)?;
122105

123-
let end_of_shared_prefix = |astr: &str, bstr: &str| {
124-
astr.char_indices()
125-
.zip(bstr.char_indices())
126-
.find_map(|((i, a), (j, b))| {
127-
if a == b {
128-
None
129-
} else {
130-
debug_assert_eq!(i, j);
131-
Some(i)
132-
}
133-
})
134-
.unwrap_or(0)
135-
};
136-
137106
// For readability, trim the shared prefix from our two engine names.
138107
//
139108
// Furthermore, there are a few special cases:
140109
// 1. If the engines are the same, show just the flags.
141110
// 2. If not, show the computed full label with common prefix removed.
142-
let (a_eng_label, b_eng_label) = if effect_size.a_engine == effect_size.b_engine {
143-
(
144-
effect_size
145-
.a_engine_flags
146-
.as_ref()
147-
.map(|ref ef| ef.to_string())
148-
.unwrap_or_else(|| "(no flags)".into())
149-
.to_string(),
150-
effect_size
151-
.b_engine_flags
152-
.as_ref()
153-
.map(|ref ef| ef.to_string())
154-
.unwrap_or_else(|| "(no flags)".into())
155-
.to_string(),
156-
)
157-
} else {
158-
let a_label = engine_label(&effect_size.a_engine, &effect_size.a_engine_flags);
159-
let b_label = engine_label(&effect_size.b_engine, &effect_size.b_engine_flags);
160-
let idx_end_of_shared = end_of_shared_prefix(&a_label, &b_label);
161-
162-
(
163-
a_label[idx_end_of_shared..].into(),
164-
b_label[idx_end_of_shared..].into(),
165-
)
166-
};
111+
let (a_eng_label, b_eng_label) =
112+
effect_size.a_engine.relative_labels(&effect_size.b_engine);
167113

168114
if effect_size.is_significant() {
169115
writeln!(
@@ -199,28 +145,19 @@ pub fn write(
199145
}
200146
writeln!(output_file)?;
201147

202-
let get_summary = |engine: &str,
203-
engine_flags: Option<Cow<str>>,
204-
wasm: &str,
205-
phase: Phase,
206-
event: &str| {
148+
let get_summary = |engine: &Engine, wasm: &str, phase: Phase, event: &str| {
207149
// TODO this sorting is not using `arch` which is not guaranteed to be the same in
208150
// result sets; potentially this could re-use `Key` functionality.
209151
summaries
210152
.iter()
211153
.find(|s| {
212-
s.engine == engine
213-
&& s.engine_flags == engine_flags
214-
&& s.wasm == wasm
215-
&& s.phase == phase
216-
&& s.event == event
154+
&s.engine == engine && s.wasm == wasm && s.phase == phase && s.event == event
217155
})
218156
.unwrap()
219157
};
220158

221159
let a_summary = get_summary(
222160
&effect_size.a_engine,
223-
effect_size.a_engine_flags,
224161
&effect_size.wasm,
225162
effect_size.phase,
226163
&effect_size.event,
@@ -233,7 +170,6 @@ pub fn write(
233170

234171
let b_summary = get_summary(
235172
&effect_size.b_engine,
236-
effect_size.b_engine_flags,
237173
&effect_size.wasm,
238174
effect_size.phase,
239175
&effect_size.event,

crates/analysis/src/keys.rs

Lines changed: 6 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
use sightglass_data::{Measurement, Phase};
1+
use sightglass_data::{Engine, Measurement, Phase};
22
use std::{borrow::Cow, collections::BTreeSet};
33

44
/// A builder for finding keys in a set of measurements.
@@ -81,11 +81,6 @@ impl KeyBuilder {
8181
.map(|m| Key {
8282
arch: if self.arch { Some(m.arch) } else { None },
8383
engine: if self.engine { Some(m.engine) } else { None },
84-
engine_flags: if self.engine_flags {
85-
Some(m.engine_flags)
86-
} else {
87-
None
88-
},
8984
wasm: if self.wasm { Some(m.wasm) } else { None },
9085
phase: if self.phase { Some(m.phase) } else { None },
9186
event: if self.event { Some(m.event) } else { None },
@@ -99,8 +94,7 @@ impl KeyBuilder {
9994
#[derive(PartialOrd, Ord, PartialEq, Eq, Hash, Debug)]
10095
pub struct Key<'a> {
10196
pub arch: Option<Cow<'a, str>>,
102-
pub engine: Option<Cow<'a, str>>,
103-
pub engine_flags: Option<Option<Cow<'a, str>>>,
97+
pub engine: Option<Engine<'a>>,
10498
pub wasm: Option<Cow<'a, str>>,
10599
pub phase: Option<Phase>,
106100
pub event: Option<Cow<'a, str>>,
@@ -111,10 +105,6 @@ impl Key<'_> {
111105
pub fn matches(&self, m: &Measurement) -> bool {
112106
self.arch.as_ref().is_none_or(|x| *x == m.arch)
113107
&& self.engine.as_ref().is_none_or(|x| *x == m.engine)
114-
&& self
115-
.engine_flags
116-
.as_ref()
117-
.is_none_or(|x| *x == m.engine_flags)
118108
&& self.wasm.as_ref().is_none_or(|x| *x == m.wasm)
119109
&& self.phase.as_ref().is_none_or(|x| *x == m.phase)
120110
&& self.event.as_ref().is_none_or(|x| *x == m.event)
@@ -130,11 +120,13 @@ mod tests {
130120
fn matching_fields() {
131121
let key = Key {
132122
arch: Some("x86".into()),
133-
engine: Some("wasmtime".into()),
123+
engine: Some(Engine {
124+
name: "wasmtime".into(),
125+
flags: None,
126+
}),
134127
wasm: Some("bench.wasm".into()),
135128
phase: Some(Phase::Compilation),
136129
event: Some("cycles".into()),
137-
engine_flags: Some(Some("-Wfoo=bar".into())),
138130
};
139131

140132
// More test cases are needed, but this provides a sanity check for the matched key and
@@ -148,7 +140,6 @@ mod tests {
148140
phase: Phase::Compilation,
149141
event: "cycles".into(),
150142
count: 42,
151-
engine_flags: Some("-Wfoo=bar".into()),
152143
}));
153144
}
154145
}

crates/analysis/src/summarize.rs

Lines changed: 6 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,6 @@ pub fn calculate<'a>(measurements: &[Measurement<'a>]) -> Vec<Summary<'a>> {
1515
summaries.push(Summary {
1616
arch: k.arch.unwrap(),
1717
engine: k.engine.unwrap(),
18-
engine_flags: k.engine_flags.unwrap(),
1918
wasm: k.wasm.unwrap(),
2019
phase: k.phase.unwrap(),
2120
event: k.event.unwrap(),
@@ -70,7 +69,6 @@ pub fn write(mut summaries: Vec<Summary<'_>>, output_file: &mut dyn Write) -> Re
7069
.then_with(|| x.wasm.cmp(&y.wasm))
7170
.then_with(|| x.event.cmp(&y.event))
7271
.then_with(|| x.engine.cmp(&y.engine))
73-
.then_with(|| x.engine_flags.cmp(&y.engine_flags))
7472
});
7573

7674
let mut last_phase = None;
@@ -95,16 +93,9 @@ pub fn write(mut summaries: Vec<Summary<'_>>, output_file: &mut dyn Write) -> Re
9593
writeln!(output_file, " {}", summary.event)?;
9694
}
9795

98-
let engine_flags = match summary.engine_flags {
99-
None => "".into(),
100-
Some(ef) => {
101-
format!(" ({ef})")
102-
}
103-
};
104-
10596
writeln!(
10697
output_file,
107-
" [{} {:.2} {}] {}{engine_flags}",
98+
" [{} {:.2} {}] {}",
10899
summary.min, summary.mean, summary.max, summary.engine,
109100
)?;
110101
}
@@ -115,15 +106,14 @@ pub fn write(mut summaries: Vec<Summary<'_>>, output_file: &mut dyn Write) -> Re
115106
#[cfg(test)]
116107
mod tests {
117108
use super::*;
118-
use sightglass_data::Phase;
109+
use sightglass_data::{Engine, Phase};
119110

120111
#[test]
121112
fn simple_statistics() {
122113
fn measurement<'a>(count: u64) -> Measurement<'a> {
123114
Measurement {
124115
arch: "x86".into(),
125116
engine: "wasmtime".into(),
126-
engine_flags: None,
127117
wasm: "bench.wasm".into(),
128118
process: 42,
129119
iteration: 0,
@@ -140,7 +130,6 @@ mod tests {
140130
vec![Summary {
141131
arch: "x86".into(),
142132
engine: "wasmtime".into(),
143-
engine_flags: None,
144133
wasm: "bench.wasm".into(),
145134
phase: Phase::Compilation,
146135
event: "cycles".into(),
@@ -159,7 +148,6 @@ mod tests {
159148
Measurement {
160149
arch: "x86".into(),
161150
engine: "wasmtime".into(),
162-
engine_flags: None,
163151
wasm: "bench.wasm".into(),
164152
process: 42,
165153
iteration: 0,
@@ -184,8 +172,10 @@ mod tests {
184172
fn measurement<'a>(flags: Option<Cow<'a, str>>, count: u64) -> Measurement<'a> {
185173
Measurement {
186174
arch: "x86".into(),
187-
engine: "wasmtime".into(),
188-
engine_flags: flags,
175+
engine: Engine {
176+
name: "wasmtime".into(),
177+
flags,
178+
},
189179
wasm: "bench.wasm".into(),
190180
process: 42,
191181
iteration: 0,

crates/cli/src/benchmark.rs

Lines changed: 8 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -174,13 +174,13 @@ impl BenchmarkCommand {
174174
.collect();
175175
let mut all_measurements = vec![];
176176

177-
for (i, engine) in self.engines.iter().enumerate() {
178-
let engine_path = check_engine_path(engine)?;
177+
for (i, engine_name) in self.engines.iter().enumerate() {
178+
let engine_path = check_engine_path(engine_name)?;
179179
let engine_name = self
180180
.names
181181
.as_ref()
182182
.and_then(|names| names.get(i).map(|s| s.as_str()))
183-
.unwrap_or(engine);
183+
.unwrap_or(engine_name);
184184
log::info!("Using benchmark engine: {}", engine_path.display());
185185
let lib = unsafe { libloading::Library::new(&engine_path)? };
186186
let mut bench_api = unsafe { BenchApi::new(&lib)? };
@@ -209,12 +209,11 @@ impl BenchmarkCommand {
209209
let stderr = Path::new(&stderr);
210210
let stdin = None;
211211

212-
let mut measurements = Measurements::with_flags(
213-
this_arch(),
214-
engine_name,
215-
wasm_file,
216-
self.engine_flags.as_deref(),
217-
);
212+
let engine = sightglass_data::Engine {
213+
name: engine_name.into(),
214+
flags: self.engine_flags.as_ref().map(|ef| ef.into()),
215+
};
216+
let mut measurements = Measurements::new(this_arch(), engine, wasm_file);
218217
let mut measure = if self.measures.len() <= 1 {
219218
let measure = self.measures.first().unwrap_or(&MeasureType::Cycles);
220219
measure.build()

crates/cli/tests/all/benchmark.rs

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -117,11 +117,13 @@ fn benchmark_csv() {
117117

118118
assert
119119
.stdout(
120-
predicate::str::starts_with("arch,engine,wasm,process,iteration,phase,event,count\n")
121-
.and(predicate::str::contains(benchmark("noop")))
122-
.and(predicate::str::contains("Compilation"))
123-
.and(predicate::str::contains("Instantiation"))
124-
.and(predicate::str::contains("Execution")),
120+
predicate::str::starts_with(
121+
"arch,engine,engine_flags,wasm,process,iteration,phase,event,count\n",
122+
)
123+
.and(predicate::str::contains(benchmark("noop")))
124+
.and(predicate::str::contains("Compilation"))
125+
.and(predicate::str::contains("Instantiation"))
126+
.and(predicate::str::contains("Execution")),
125127
)
126128
.success();
127129
}

0 commit comments

Comments
 (0)