@@ -183,8 +183,8 @@ impl Command {
183
183
Command :: Doc { flags } => Self :: doc ( flags) ,
184
184
Command :: Fmt { flags } => Self :: fmt ( flags) ,
185
185
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) ,
188
188
Command :: Toolchain { flags } => Self :: toolchain ( flags) ,
189
189
Command :: RustcPull { commit } => Self :: rustc_pull ( commit. clone ( ) ) ,
190
190
Command :: RustcPush { github_user, branch } => Self :: rustc_push ( github_user, branch) ,
@@ -387,20 +387,30 @@ impl Command {
387
387
target : Option < String > ,
388
388
no_install : bool ,
389
389
save_baseline : Option < String > ,
390
+ load_baseline : Option < String > ,
390
391
benches : Vec < String > ,
391
392
) -> 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
+
392
397
// The hyperfine to use
393
398
let hyperfine = env:: var ( "HYPERFINE" ) ;
394
399
let hyperfine = hyperfine. as_deref ( ) . unwrap_or ( "hyperfine -w 1 -m 5 --shell=none" ) ;
395
400
let hyperfine = shell_words:: split ( hyperfine) ?;
396
401
let Some ( ( program_name, args) ) = hyperfine. split_first ( ) else {
397
402
bail ! ( "expected HYPERFINE environment variable to be non-empty" ) ;
398
403
} ;
404
+
399
405
if !no_install {
400
406
// Make sure we have an up-to-date Miri installed and selected the right toolchain.
401
407
Self :: install ( vec ! [ ] ) ?;
402
408
}
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
+ } ;
404
414
405
415
let miri_dir = miri_dir ( ) ?;
406
416
let sh = Shell :: new ( ) ?;
@@ -428,7 +438,7 @@ impl Command {
428
438
for bench in & benches {
429
439
let current_bench = path ! ( benches_dir / bench / "Cargo.toml" ) ;
430
440
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 {
432
442
export_json = Some ( format ! (
433
443
"--export-json={}" ,
434
444
path!( baseline_temp_dir / format!( "{bench}.bench.json" ) ) . display( )
@@ -451,19 +461,47 @@ impl Command {
451
461
stddev : f64 ,
452
462
}
453
463
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 ( ) ;
457
467
for bench in & benches {
458
468
let result = File :: open ( path ! ( baseline_temp_dir / format!( "{bench}.bench.json" ) ) ) ?;
459
469
let mut result: serde_json:: Value =
460
470
serde_json:: from_reader ( BufReader :: new ( result) ) ?;
461
471
let result: BenchResult = serde_json:: from_value ( result[ "results" ] [ 0 ] . take ( ) ) ?;
462
- results. insert ( bench, result) ;
472
+ results. insert ( bench as & str , result) ;
463
473
}
474
+ Ok ( results)
475
+ } ;
464
476
477
+ if let Some ( baseline_file) = save_baseline {
478
+ let results = gather_results ( ) ?;
465
479
let baseline = File :: create ( baseline_file) ?;
466
480
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
+ }
467
505
}
468
506
469
507
Ok ( ( ) )
0 commit comments