Skip to content

Commit 40b3310

Browse files
committed
miri-script: add option to compare with baseline results
1 parent 38e3ebc commit 40b3310

File tree

2 files changed

+50
-8
lines changed

2 files changed

+50
-8
lines changed

Diff for: src/tools/miri/miri-script/src/commands.rs

+46-8
Original file line numberDiff line numberDiff line change
@@ -183,8 +183,8 @@ impl Command {
183183
Command::Doc { flags } => Self::doc(flags),
184184
Command::Fmt { flags } => Self::fmt(flags),
185185
Command::Clippy { flags } => Self::clippy(flags),
186-
Command::Bench { target, no_install, save_baseline, benches } =>
187-
Self::bench(target, no_install, save_baseline, benches),
186+
Command::Bench { target, no_install, save_baseline, load_baseline, benches } =>
187+
Self::bench(target, no_install, save_baseline, load_baseline, benches),
188188
Command::Toolchain { flags } => Self::toolchain(flags),
189189
Command::RustcPull { commit } => Self::rustc_pull(commit.clone()),
190190
Command::RustcPush { github_user, branch } => Self::rustc_push(github_user, branch),
@@ -387,20 +387,30 @@ impl Command {
387387
target: Option<String>,
388388
no_install: bool,
389389
save_baseline: Option<String>,
390+
load_baseline: Option<String>,
390391
benches: Vec<String>,
391392
) -> Result<()> {
393+
if save_baseline.is_some() && load_baseline.is_some() {
394+
bail!("Only one of `--save-baseline` and `--load-baseline` can be set");
395+
}
396+
392397
// The hyperfine to use
393398
let hyperfine = env::var("HYPERFINE");
394399
let hyperfine = hyperfine.as_deref().unwrap_or("hyperfine -w 1 -m 5 --shell=none");
395400
let hyperfine = shell_words::split(hyperfine)?;
396401
let Some((program_name, args)) = hyperfine.split_first() else {
397402
bail!("expected HYPERFINE environment variable to be non-empty");
398403
};
404+
399405
if !no_install {
400406
// Make sure we have an up-to-date Miri installed and selected the right toolchain.
401407
Self::install(vec![])?;
402408
}
403-
let baseline_temp_dir = if save_baseline.is_some() { Some(TempDir::new()?) } else { None };
409+
let results_json_dir = if save_baseline.is_some() || load_baseline.is_some() {
410+
Some(TempDir::new()?)
411+
} else {
412+
None
413+
};
404414

405415
let miri_dir = miri_dir()?;
406416
let sh = Shell::new()?;
@@ -428,7 +438,7 @@ impl Command {
428438
for bench in &benches {
429439
let current_bench = path!(benches_dir / bench / "Cargo.toml");
430440
let mut export_json = None;
431-
if let Some(baseline_temp_dir) = &baseline_temp_dir {
441+
if let Some(baseline_temp_dir) = &results_json_dir {
432442
export_json = Some(format!(
433443
"--export-json={}",
434444
path!(baseline_temp_dir / format!("{bench}.bench.json")).display()
@@ -451,19 +461,47 @@ impl Command {
451461
stddev: f64,
452462
}
453463

454-
if let Some(baseline_file) = save_baseline {
455-
let baseline_temp_dir = baseline_temp_dir.unwrap();
456-
let mut results: HashMap<&str, BenchResult> = HashMap::new();
464+
let gather_results = || -> Result<HashMap<&str, BenchResult>> {
465+
let baseline_temp_dir = results_json_dir.unwrap();
466+
let mut results = HashMap::new();
457467
for bench in &benches {
458468
let result = File::open(path!(baseline_temp_dir / format!("{bench}.bench.json")))?;
459469
let mut result: serde_json::Value =
460470
serde_json::from_reader(BufReader::new(result))?;
461471
let result: BenchResult = serde_json::from_value(result["results"][0].take())?;
462-
results.insert(bench, result);
472+
results.insert(bench as &str, result);
463473
}
474+
Ok(results)
475+
};
464476

477+
if let Some(baseline_file) = save_baseline {
478+
let results = gather_results()?;
465479
let baseline = File::create(baseline_file)?;
466480
serde_json::to_writer_pretty(BufWriter::new(baseline), &results)?;
481+
} else if let Some(baseline_file) = load_baseline {
482+
let new_results = gather_results()?;
483+
let baseline_results: HashMap<String, BenchResult> = {
484+
let f = File::open(baseline_file)?;
485+
serde_json::from_reader(BufReader::new(f))?
486+
};
487+
println!(
488+
"Comparison with baseline (relative speed, lower is better for the new results):"
489+
);
490+
for (bench, new_result) in new_results.iter() {
491+
let Some(baseline_result) = baseline_results.get(*bench) else { continue };
492+
493+
// Compare results (inspired by hyperfine)
494+
let ratio = new_result.mean / baseline_result.mean;
495+
// https://en.wikipedia.org/wiki/Propagation_of_uncertainty#Example_formulae
496+
// Covariance asssumed to be 0, i.e. variables are assumed to be independent
497+
let ratio_stddev = ratio
498+
* f64::sqrt(
499+
(new_result.stddev / new_result.mean).powi(2)
500+
+ (baseline_result.stddev / baseline_result.mean).powi(2),
501+
);
502+
503+
println!(" {bench}: {ratio:.2} ± {ratio_stddev:.2}");
504+
}
467505
}
468506

469507
Ok(())

Diff for: src/tools/miri/miri-script/src/main.rs

+4
Original file line numberDiff line numberDiff line change
@@ -121,6 +121,10 @@ pub enum Command {
121121
/// as the baseline for a future run.
122122
#[arg(long)]
123123
save_baseline: Option<String>,
124+
/// Load previous stored benchmark results as baseline, and print an analysis of how the
125+
/// current run compares.
126+
#[arg(long)]
127+
load_baseline: Option<String>,
124128
/// List of benchmarks to run (default: run all benchmarks).
125129
benches: Vec<String>,
126130
},

0 commit comments

Comments
 (0)