1+ use std:: collections:: HashMap ;
12use std:: ffi:: { OsStr , OsString } ;
2- use std:: io:: Write ;
3+ use std:: fs:: File ;
4+ use std:: io:: { BufReader , BufWriter , Write } ;
35use std:: ops:: { Not , Range } ;
46use std:: path:: PathBuf ;
57use std:: time:: Duration ;
68use std:: { env, net, process} ;
79
810use anyhow:: { Context , Result , anyhow, bail} ;
911use path_macro:: path;
12+ use serde_derive:: { Deserialize , Serialize } ;
13+ use tempfile:: TempDir ;
1014use walkdir:: WalkDir ;
1115use xshell:: { Shell , cmd} ;
1216
@@ -179,8 +183,8 @@ impl Command {
179183 Command :: Doc { flags } => Self :: doc ( flags) ,
180184 Command :: Fmt { flags } => Self :: fmt ( flags) ,
181185 Command :: Clippy { flags } => Self :: clippy ( flags) ,
182- Command :: Bench { target, no_install, benches } =>
183- Self :: bench ( target, no_install, benches) ,
186+ Command :: Bench { target, no_install, save_baseline , benches } =>
187+ Self :: bench ( target, no_install, save_baseline , benches) ,
184188 Command :: Toolchain { flags } => Self :: toolchain ( flags) ,
185189 Command :: RustcPull { commit } => Self :: rustc_pull ( commit. clone ( ) ) ,
186190 Command :: RustcPush { github_user, branch } => Self :: rustc_push ( github_user, branch) ,
@@ -379,7 +383,12 @@ impl Command {
379383 Ok ( ( ) )
380384 }
381385
382- fn bench ( target : Option < String > , no_install : bool , benches : Vec < String > ) -> Result < ( ) > {
386+ fn bench (
387+ target : Option < String > ,
388+ no_install : bool ,
389+ save_baseline : Option < String > ,
390+ benches : Vec < String > ,
391+ ) -> Result < ( ) > {
383392 // The hyperfine to use
384393 let hyperfine = env:: var ( "HYPERFINE" ) ;
385394 let hyperfine = hyperfine. as_deref ( ) . unwrap_or ( "hyperfine -w 1 -m 5 --shell=none" ) ;
@@ -391,15 +400,17 @@ impl Command {
391400 // Make sure we have an up-to-date Miri installed and selected the right toolchain.
392401 Self :: install ( vec ! [ ] ) ?;
393402 }
403+ let baseline_temp_dir = if save_baseline. is_some ( ) { Some ( TempDir :: new ( ) ?) } else { None } ;
394404
405+ let miri_dir = miri_dir ( ) ?;
395406 let sh = Shell :: new ( ) ?;
396- sh. change_dir ( miri_dir ( ) ? ) ;
407+ sh. change_dir ( & miri_dir) ;
397408 let benches_dir = "bench-cargo-miri" ;
398- let benches: Vec < OsString > = if benches. is_empty ( ) {
409+ let benches: Vec < String > = if benches. is_empty ( ) {
399410 sh. read_dir ( benches_dir) ?
400411 . into_iter ( )
401412 . filter ( |path| path. is_dir ( ) )
402- . map ( Into :: into )
413+ . map ( |path| path . into_os_string ( ) . into_string ( ) . unwrap ( ) )
403414 . collect ( )
404415 } else {
405416 benches. into_iter ( ) . map ( Into :: into) . collect ( )
@@ -414,16 +425,47 @@ impl Command {
414425 let target_flag = & target_flag;
415426 let toolchain = active_toolchain ( ) ?;
416427 // Run the requested benchmarks
417- for bench in benches {
428+ for bench in & benches {
418429 let current_bench = path ! ( benches_dir / bench / "Cargo.toml" ) ;
430+ let mut export_json = None ;
431+ if let Some ( baseline_temp_dir) = & baseline_temp_dir {
432+ export_json = Some ( format ! (
433+ "--export-json={}" ,
434+ path!( baseline_temp_dir / format!( "{bench}.bench.json" ) ) . display( )
435+ ) ) ;
436+ }
419437 // We don't attempt to escape `current_bench`, but we wrap it in quotes.
420438 // That seems to make Windows CI happy.
421439 cmd ! (
422440 sh,
423- "{program_name} {args...} 'cargo +'{toolchain}' miri run '{target_flag}' --manifest-path \" '{current_bench}'\" '"
441+ "{program_name} {args...} {export_json...} 'cargo +'{toolchain}' miri run '{target_flag}' --manifest-path \" '{current_bench}'\" '"
424442 )
425443 . run ( ) ?;
426444 }
445+
446+ // Gather/load results for baseline saving.
447+
448+ #[ derive( Serialize , Deserialize ) ]
449+ struct BenchResult {
450+ mean : f64 ,
451+ stddev : f64 ,
452+ }
453+
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 ( ) ;
457+ for bench in & benches {
458+ let result = File :: open ( path ! ( baseline_temp_dir / format!( "{bench}.bench.json" ) ) ) ?;
459+ let mut result: serde_json:: Value =
460+ serde_json:: from_reader ( BufReader :: new ( result) ) ?;
461+ let result: BenchResult = serde_json:: from_value ( result[ "results" ] [ 0 ] . take ( ) ) ?;
462+ results. insert ( bench, result) ;
463+ }
464+
465+ let baseline = File :: create ( baseline_file) ?;
466+ serde_json:: to_writer_pretty ( BufWriter :: new ( baseline) , & results) ?;
467+ }
468+
427469 Ok ( ( ) )
428470 }
429471
0 commit comments