Skip to content

Commit bdac6bd

Browse files
Merge pull request #1028 from tmiasko/diff
Add command for profiling and comparing toolchains
2 parents 61466f7 + 3c84c57 commit bdac6bd

File tree

1 file changed

+207
-18
lines changed

1 file changed

+207
-18
lines changed

collector/src/main.rs

Lines changed: 207 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ use std::fs;
1111
use std::io::{stderr, Write};
1212
use std::path::{Path, PathBuf};
1313
use std::process;
14-
use std::process::Command;
14+
use std::process::{Command, Stdio};
1515
use std::{str, time::Instant};
1616
use tokio::runtime::Runtime;
1717

@@ -486,6 +486,117 @@ fn get_local_toolchain(
486486
Ok((rustc, rustdoc, cargo))
487487
}
488488

489+
fn generate_cachegrind_diffs(
490+
id1: &str,
491+
id2: &str,
492+
out_dir: &Path,
493+
benchmarks: &[Benchmark],
494+
build_kinds: &[BuildKind],
495+
scenario_kinds: &[ScenarioKind],
496+
errors: &mut BenchmarkErrors,
497+
) {
498+
for benchmark in benchmarks {
499+
for &build_kind in build_kinds {
500+
for &scenario_kind in scenario_kinds {
501+
if let ScenarioKind::IncrPatched = scenario_kind {
502+
continue;
503+
}
504+
let filename = |prefix, id| {
505+
format!(
506+
"{}-{}-{}-{:?}-{:?}",
507+
prefix, id, benchmark.name, build_kind, scenario_kind
508+
)
509+
};
510+
let id_diff = format!("{}-{}", id1, id2);
511+
let cgout1 = out_dir.join(filename("cgout", id1));
512+
let cgout2 = out_dir.join(filename("cgout", id2));
513+
let cgdiff = out_dir.join(filename("cgdiff", &id_diff));
514+
let cgann = out_dir.join(filename("cgann", &id_diff));
515+
516+
if let Err(e) = cg_diff(&cgout1, &cgout2, &cgdiff) {
517+
errors.incr();
518+
eprintln!("collector error: {:?}", e);
519+
continue;
520+
}
521+
if let Err(e) = cg_annotate(&cgdiff, &cgann) {
522+
errors.incr();
523+
eprintln!("collector error: {:?}", e);
524+
continue;
525+
}
526+
}
527+
}
528+
}
529+
}
530+
531+
/// Compares two Cachegrind output files using cg_diff and writes result to path.
532+
fn cg_diff(cgout1: &Path, cgout2: &Path, path: &Path) -> anyhow::Result<()> {
533+
let output = Command::new("cg_diff")
534+
.arg("--mod-filename=s#/rustc/[^/]*/##")
535+
.arg("--mod-funcname=s/[.]llvm[.].*//")
536+
.arg(cgout1)
537+
.arg(cgout2)
538+
.stderr(Stdio::inherit())
539+
.output()
540+
.context("failed to run `cg_diff`")?;
541+
542+
if !output.status.success() {
543+
anyhow::bail!("failed to generate cachegrind diff");
544+
}
545+
546+
fs::write(path, output.stdout).context("failed to write `cg_diff` output")?;
547+
548+
Ok(())
549+
}
550+
551+
/// Post process Cachegrind output file and writes resutl to path.
552+
fn cg_annotate(cgout: &Path, path: &Path) -> anyhow::Result<()> {
553+
let output = Command::new("cg_annotate")
554+
.arg("--show-percs=no")
555+
.arg(cgout)
556+
.stderr(Stdio::inherit())
557+
.output()
558+
.context("failed to run `cg_annotate`")?;
559+
560+
if !output.status.success() {
561+
anyhow::bail!("failed to annotate cachegrind output");
562+
}
563+
564+
fs::write(path, output.stdout).context("failed to write `cg_annotate` output")?;
565+
566+
Ok(())
567+
}
568+
569+
fn profile(
570+
compiler: Compiler,
571+
id: &str,
572+
profiler: Profiler,
573+
out_dir: &Path,
574+
benchmarks: &[Benchmark],
575+
build_kinds: &[BuildKind],
576+
scenario_kinds: &[ScenarioKind],
577+
errors: &mut BenchmarkErrors,
578+
) {
579+
eprintln!("Profiling {} with {:?}", id, profiler);
580+
for (i, benchmark) in benchmarks.iter().enumerate() {
581+
eprintln!("{}", n_benchmarks_remaining(benchmarks.len() - i));
582+
let mut processor = execute::ProfileProcessor::new(profiler, out_dir, id);
583+
let result = benchmark.measure(
584+
&mut processor,
585+
&build_kinds,
586+
&scenario_kinds,
587+
compiler,
588+
Some(1),
589+
);
590+
if let Err(ref s) = result {
591+
errors.incr();
592+
eprintln!(
593+
"collector error: Failed to profile '{}' with {:?}, recorded: {:?}",
594+
benchmark.name, profiler, s
595+
);
596+
}
597+
}
598+
}
599+
489600
fn main() {
490601
match main_result() {
491602
Ok(code) => process::exit(code),
@@ -585,6 +696,35 @@ fn main_result() -> anyhow::Result<i32> {
585696
(@arg RUSTDOC: --rustdoc +takes_value "The path to the local rustdoc to benchmark")
586697
)
587698

699+
(@subcommand diff_local =>
700+
(about: "Profiles and compares two toolchains with one of several profilers")
701+
702+
// Mandatory arguments
703+
(@arg PROFILER: +required +takes_value
704+
"One of: 'self-profile', 'time-passes', 'perf-record',\n\
705+
'oprofile', 'cachegrind', 'callgrind', 'dhat', 'massif',\n\
706+
'eprintln', 'llvm-lines'")
707+
(@arg RUSTC_BEFORE: +required +takes_value "The path to the local rustc to benchmark")
708+
(@arg RUSTC_AFTER: +required +takes_value "The path to the local rustc to benchmark")
709+
710+
// Options
711+
(@arg BUILDS: --builds +takes_value
712+
"One or more (comma-separated) of: 'Check', \n\
713+
'Debug', 'Doc', 'Opt', 'All'")
714+
(@arg CARGO: --cargo +takes_value "The path to the local Cargo to use")
715+
(@arg EXCLUDE: --exclude +takes_value
716+
"Exclude all benchmarks matching anything in\n\
717+
this comma-separated list of patterns")
718+
(@arg INCLUDE: --include +takes_value
719+
"Include only benchmarks matching something in\n\
720+
this comma-separated list of patterns")
721+
(@arg OUT_DIR: --("out-dir") +takes_value "Output directory")
722+
(@arg RUNS: --runs +takes_value
723+
"One or more (comma-separated) of: 'Full',\n\
724+
'IncrFull', 'IncrUnchanged', 'IncrPatched', 'All'")
725+
(@arg RUSTDOC: --rustdoc +takes_value "The path to the local rustdoc to benchmark")
726+
)
727+
588728
(@subcommand install_next =>
589729
(about: "Installs the next commit for perf.rust-lang.org")
590730

@@ -796,38 +936,87 @@ fn main_result() -> anyhow::Result<i32> {
796936
let rustdoc = sub_m.value_of("RUSTDOC");
797937

798938
let (rustc, rustdoc, cargo) = get_local_toolchain(&build_kinds, rustc, rustdoc, cargo)?;
799-
800939
let compiler = Compiler {
801940
rustc: &rustc,
802941
rustdoc: rustdoc.as_deref(),
803942
cargo: &cargo,
804943
triple: &target_triple,
805944
is_nightly: true,
806945
};
807-
808946
let benchmarks = get_benchmarks(&benchmark_dir, include, exclude)?;
947+
let mut errors = BenchmarkErrors::new();
948+
profile(
949+
compiler,
950+
id,
951+
profiler,
952+
&out_dir,
953+
&benchmarks,
954+
&build_kinds,
955+
&scenario_kinds,
956+
&mut errors,
957+
);
958+
errors.fail_if_nonzero()?;
959+
Ok(0)
960+
}
961+
962+
("diff_local", Some(sub_m)) => {
963+
// Mandatory arguments
964+
let profiler = Profiler::from_name(sub_m.value_of("PROFILER").unwrap())?;
965+
let rustc1 = sub_m.value_of("RUSTC_BEFORE").unwrap();
966+
let rustc2 = sub_m.value_of("RUSTC_AFTER").unwrap();
967+
968+
// Options
969+
let build_kinds = build_kinds_from_arg(&sub_m.value_of("BUILDS"))?;
970+
let cargo = sub_m.value_of("CARGO");
971+
let exclude = sub_m.value_of("EXCLUDE");
972+
let include = sub_m.value_of("INCLUDE");
973+
let out_dir = PathBuf::from(sub_m.value_of_os("OUT_DIR").unwrap_or(default_out_dir));
974+
let scenario_kinds = scenario_kinds_from_arg(sub_m.value_of("RUNS"))?;
975+
let rustdoc = sub_m.value_of("RUSTDOC");
809976

810-
eprintln!("Profiling with {:?}", profiler);
977+
let id1 = rustc1.strip_prefix('+').unwrap_or("before");
978+
let id2 = rustc2.strip_prefix('+').unwrap_or("after");
979+
let mut toolchains = Vec::new();
980+
for (id, rustc) in [(id1, rustc1), (id2, rustc2)] {
981+
let (rustc, rustdoc, cargo) =
982+
get_local_toolchain(&build_kinds, rustc, rustdoc, cargo)?;
983+
toolchains.push((id.to_owned(), rustc, rustdoc, cargo));
984+
}
811985

986+
let benchmarks = get_benchmarks(&benchmark_dir, include, exclude)?;
812987
let mut errors = BenchmarkErrors::new();
813-
for (i, benchmark) in benchmarks.iter().enumerate() {
814-
eprintln!("{}", n_benchmarks_remaining(benchmarks.len() - i));
815-
let mut processor = execute::ProfileProcessor::new(profiler, &out_dir, &id);
816-
let result = benchmark.measure(
817-
&mut processor,
988+
for (id, rustc, rustdoc, cargo) in &toolchains {
989+
let compiler = Compiler {
990+
rustc: &rustc,
991+
rustdoc: rustdoc.as_deref(),
992+
cargo: &cargo,
993+
triple: &target_triple,
994+
is_nightly: true,
995+
};
996+
profile(
997+
compiler,
998+
id,
999+
profiler,
1000+
&out_dir,
1001+
&benchmarks,
8181002
&build_kinds,
8191003
&scenario_kinds,
820-
compiler,
821-
Some(1),
1004+
&mut errors,
8221005
);
823-
if let Err(ref s) = result {
824-
errors.incr();
825-
eprintln!(
826-
"collector error: Failed to profile '{}' with {:?}, recorded: {:?}",
827-
benchmark.name, profiler, s
828-
);
829-
}
8301006
}
1007+
1008+
if let Profiler::Cachegrind = profiler {
1009+
generate_cachegrind_diffs(
1010+
id1,
1011+
id2,
1012+
&out_dir,
1013+
&benchmarks,
1014+
&build_kinds,
1015+
&scenario_kinds,
1016+
&mut errors,
1017+
);
1018+
}
1019+
8311020
errors.fail_if_nonzero()?;
8321021
Ok(0)
8331022
}

0 commit comments

Comments
 (0)