@@ -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 ( ( ) )
0 commit comments