Skip to content

Commit 6d74ffd

Browse files
authored
Rollup merge of #127002 - Kobzol:bootstrap-perf-tool, r=onur-ozkan
Implement `x perf` as a separate tool Continues work from #126318, adds a CLI for running `rustc-perf` profiling commands through a new `rustc-perf-wrapper` tool. The CLI is in a separate tool to enable experimentation outside of `bootstrap`. This is probably most of what we can do so far, I'll add support for benchmarking once `rustc-perf` gets a terminal output for comparing benchmark results. r? ``@onur-ozkan``
2 parents 6df6879 + 6a2638e commit 6d74ffd

File tree

13 files changed

+212
-25
lines changed

13 files changed

+212
-25
lines changed

Cargo.lock

+7
Original file line numberDiff line numberDiff line change
@@ -3461,6 +3461,13 @@ dependencies = [
34613461
"stable_mir",
34623462
]
34633463

3464+
[[package]]
3465+
name = "rustc-perf-wrapper"
3466+
version = "0.1.0"
3467+
dependencies = [
3468+
"clap",
3469+
]
3470+
34643471
[[package]]
34653472
name = "rustc-rayon"
34663473
version = "0.5.0"

Cargo.toml

+1
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@ members = [
4444
"src/tools/rustdoc-gui-test",
4545
"src/tools/opt-dist",
4646
"src/tools/coverage-dump",
47+
"src/tools/rustc-perf-wrapper",
4748
]
4849

4950
exclude = [
+11-21
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,5 @@
1-
use std::process::Command;
2-
31
use crate::core::build_steps::compile::{Std, Sysroot};
4-
use crate::core::build_steps::tool::RustcPerf;
2+
use crate::core::build_steps::tool::{RustcPerf, Tool};
53
use crate::core::builder::Builder;
64
use crate::core::config::DebuginfoLevel;
75

@@ -22,24 +20,16 @@ Consider setting `rust.debuginfo-level = 1` in `config.toml`."#);
2220
let sysroot = builder.ensure(Sysroot::new(compiler));
2321
let rustc = sysroot.join("bin/rustc");
2422

25-
let results_dir = builder.build.tempdir().join("rustc-perf");
26-
27-
let mut cmd = Command::new(collector);
28-
let cmd = cmd
29-
.arg("profile_local")
30-
.arg("eprintln")
31-
.arg("--out-dir")
32-
.arg(&results_dir)
33-
.arg("--include")
34-
.arg("helloworld")
35-
.arg(&rustc);
36-
37-
builder.info(&format!("Running `rustc-perf` using `{}`", rustc.display()));
23+
let rustc_perf_dir = builder.build.tempdir().join("rustc-perf");
24+
let profile_results_dir = rustc_perf_dir.join("results");
3825

39-
// We need to set the working directory to `src/tools/perf`, so that it can find the directory
40-
// with compile-time benchmarks.
41-
let cmd = cmd.current_dir(builder.src.join("src/tools/rustc-perf"));
42-
builder.build.run(cmd);
26+
// We need to take args passed after `--` and pass them to `rustc-perf-wrapper`
27+
let args = std::env::args().skip_while(|a| a != "--").skip(1);
4328

44-
builder.info(&format!("You can find the results at `{}`", results_dir.display()));
29+
let mut cmd = builder.tool_cmd(Tool::RustcPerfWrapper);
30+
cmd.env("RUSTC_REAL", rustc)
31+
.env("PERF_COLLECTOR", collector)
32+
.env("PERF_RESULT_DIR", profile_results_dir)
33+
.args(args);
34+
builder.run(&mut cmd);
4535
}

src/bootstrap/src/core/build_steps/tool.rs

+1
Original file line numberDiff line numberDiff line change
@@ -337,6 +337,7 @@ bootstrap_tool!(
337337
GenerateWindowsSys, "src/tools/generate-windows-sys", "generate-windows-sys";
338338
RustdocGUITest, "src/tools/rustdoc-gui-test", "rustdoc-gui-test", is_unstable_tool = true, allow_features = "test";
339339
CoverageDump, "src/tools/coverage-dump", "coverage-dump";
340+
RustcPerfWrapper, "src/tools/rustc-perf-wrapper", "rustc-perf-wrapper";
340341
);
341342

342343
#[derive(Debug, Clone, Hash, PartialEq, Eq)]

src/bootstrap/src/core/config/flags.rs

+3-1
Original file line numberDiff line numberDiff line change
@@ -470,7 +470,9 @@ Arguments:
470470
versioned_dirs: bool,
471471
},
472472
/// Perform profiling and benchmarking of the compiler using the
473-
/// `rustc-perf` benchmark suite.
473+
/// `rustc-perf-wrapper` tool.
474+
///
475+
/// You need to pass arguments after `--`, e.g.`x perf -- cachegrind`.
474476
Perf {},
475477
}
476478

src/etc/completions/x.py.fish

+1-1
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ complete -c x.py -n "__fish_use_subcommand" -f -a "run" -d 'Run tools contained
4848
complete -c x.py -n "__fish_use_subcommand" -f -a "setup" -d 'Set up the environment for development'
4949
complete -c x.py -n "__fish_use_subcommand" -f -a "suggest" -d 'Suggest a subset of tests to run, based on modified files'
5050
complete -c x.py -n "__fish_use_subcommand" -f -a "vendor" -d 'Vendor dependencies'
51-
complete -c x.py -n "__fish_use_subcommand" -f -a "perf" -d 'Perform profiling and benchmarking of the compiler using the `rustc-perf` benchmark suite'
51+
complete -c x.py -n "__fish_use_subcommand" -f -a "perf" -d 'Perform profiling and benchmarking of the compiler using the `rustc-perf-wrapper` tool'
5252
complete -c x.py -n "__fish_seen_subcommand_from build" -l config -d 'TOML configuration file for build' -r -F
5353
complete -c x.py -n "__fish_seen_subcommand_from build" -l build-dir -d 'Build directory, overrides `build.build-dir` in `config.toml`' -r -f -a "(__fish_complete_directories)"
5454
complete -c x.py -n "__fish_seen_subcommand_from build" -l build -d 'build target of the stage0 compiler' -r -f

src/etc/completions/x.py.ps1

+1-1
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,7 @@ Register-ArgumentCompleter -Native -CommandName 'x.py' -ScriptBlock {
7575
[CompletionResult]::new('setup', 'setup', [CompletionResultType]::ParameterValue, 'Set up the environment for development')
7676
[CompletionResult]::new('suggest', 'suggest', [CompletionResultType]::ParameterValue, 'Suggest a subset of tests to run, based on modified files')
7777
[CompletionResult]::new('vendor', 'vendor', [CompletionResultType]::ParameterValue, 'Vendor dependencies')
78-
[CompletionResult]::new('perf', 'perf', [CompletionResultType]::ParameterValue, 'Perform profiling and benchmarking of the compiler using the `rustc-perf` benchmark suite')
78+
[CompletionResult]::new('perf', 'perf', [CompletionResultType]::ParameterValue, 'Perform profiling and benchmarking of the compiler using the `rustc-perf-wrapper` tool')
7979
break
8080
}
8181
'x.py;build' {

src/etc/completions/x.py.zsh

+1-1
Original file line numberDiff line numberDiff line change
@@ -856,7 +856,7 @@ _x.py_commands() {
856856
'setup:Set up the environment for development' \
857857
'suggest:Suggest a subset of tests to run, based on modified files' \
858858
'vendor:Vendor dependencies' \
859-
'perf:Perform profiling and benchmarking of the compiler using the \`rustc-perf\` benchmark suite' \
859+
'perf:Perform profiling and benchmarking of the compiler using the \`rustc-perf-wrapper\` tool' \
860860
)
861861
_describe -t commands 'x.py commands' commands "$@"
862862
}
+7
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
[package]
2+
name = "rustc-perf-wrapper"
3+
version = "0.1.0"
4+
edition = "2021"
5+
6+
[dependencies]
7+
clap = { version = "4.5.7", features = ["derive", "env"] }
+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
# rustc-perf wrapper
2+
Utility tool for invoking [`rustc-perf`](https://github.com/rust-lang/rustc-perf) for benchmarking/profiling
3+
a stage1/2 compiler built by bootstrap using `x perf -- <command>`.
+45
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
use std::fmt::{Display, Formatter};
2+
3+
#[derive(Clone, Copy, Debug, clap::ValueEnum)]
4+
#[value(rename_all = "PascalCase")]
5+
pub enum Profile {
6+
Check,
7+
Debug,
8+
Doc,
9+
Opt,
10+
Clippy,
11+
}
12+
13+
impl Display for Profile {
14+
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
15+
let name = match self {
16+
Profile::Check => "Check",
17+
Profile::Debug => "Debug",
18+
Profile::Doc => "Doc",
19+
Profile::Opt => "Opt",
20+
Profile::Clippy => "Clippy",
21+
};
22+
f.write_str(name)
23+
}
24+
}
25+
26+
#[derive(Clone, Copy, Debug, clap::ValueEnum)]
27+
#[value(rename_all = "PascalCase")]
28+
pub enum Scenario {
29+
Full,
30+
IncrFull,
31+
IncrUnchanged,
32+
IncrPatched,
33+
}
34+
35+
impl Display for Scenario {
36+
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
37+
let name = match self {
38+
Scenario::Full => "Full",
39+
Scenario::IncrFull => "IncrFull",
40+
Scenario::IncrUnchanged => "IncrUnchanged",
41+
Scenario::IncrPatched => "IncrPatched",
42+
};
43+
f.write_str(name)
44+
}
45+
}
+130
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,130 @@
1+
use crate::config::{Profile, Scenario};
2+
use clap::Parser;
3+
use std::path::PathBuf;
4+
use std::process::Command;
5+
6+
mod config;
7+
8+
/// Performs profiling or benchmarking with [`rustc-perf`](https://github.com/rust-lang/rustc-perf)
9+
/// using a locally built compiler.
10+
#[derive(Debug, clap::Parser)]
11+
// Hide arguments from BuildContext in the default usage string.
12+
// Clap does not seem to have a way of disabling the usage of these arguments.
13+
#[clap(override_usage = "rustc-perf-wrapper [OPTIONS] <COMMAND>")]
14+
pub struct Args {
15+
#[clap(subcommand)]
16+
cmd: PerfCommand,
17+
18+
#[clap(flatten)]
19+
opts: SharedOpts,
20+
21+
#[clap(flatten)]
22+
ctx: BuildContext,
23+
}
24+
25+
#[derive(Debug, clap::Parser)]
26+
enum PerfCommand {
27+
/// Run `profile_local eprintln`.
28+
/// This executes the compiler on the given benchmarks and stores its stderr output.
29+
Eprintln,
30+
/// Run `profile_local samply`
31+
/// This executes the compiler on the given benchmarks and profiles it with `samply`.
32+
/// You need to install `samply`, e.g. using `cargo install samply`.
33+
Samply,
34+
/// Run `profile_local cachegrind`.
35+
/// This executes the compiler on the given benchmarks under `Cachegrind`.
36+
Cachegrind,
37+
}
38+
39+
impl PerfCommand {
40+
fn is_profiling(&self) -> bool {
41+
match self {
42+
PerfCommand::Eprintln | PerfCommand::Samply | PerfCommand::Cachegrind => true,
43+
}
44+
}
45+
}
46+
47+
#[derive(Debug, clap::Parser)]
48+
struct SharedOpts {
49+
/// Select the benchmarks that you want to run (separated by commas).
50+
/// If unspecified, all benchmarks will be executed.
51+
#[clap(long, global = true, value_delimiter = ',')]
52+
include: Vec<String>,
53+
/// Select the scenarios that should be benchmarked.
54+
#[clap(
55+
long,
56+
global = true,
57+
value_delimiter = ',',
58+
default_value = "Full,IncrFull,IncrUnchanged,IncrPatched"
59+
)]
60+
scenarios: Vec<Scenario>,
61+
/// Select the profiles that should be benchmarked.
62+
#[clap(long, global = true, value_delimiter = ',', default_value = "Check,Debug,Opt")]
63+
profiles: Vec<Profile>,
64+
}
65+
66+
/// These arguments are mostly designed to be passed from bootstrap, not by users
67+
/// directly.
68+
#[derive(Debug, clap::Parser)]
69+
struct BuildContext {
70+
/// Compiler binary that will be benchmarked/profiled.
71+
#[clap(long, hide = true, env = "RUSTC_REAL")]
72+
compiler: PathBuf,
73+
/// rustc-perf collector binary that will be used for running benchmarks/profilers.
74+
#[clap(long, hide = true, env = "PERF_COLLECTOR")]
75+
collector: PathBuf,
76+
/// Directory where to store results.
77+
#[clap(long, hide = true, env = "PERF_RESULT_DIR")]
78+
results_dir: PathBuf,
79+
}
80+
81+
fn main() {
82+
let args = Args::parse();
83+
run(args);
84+
}
85+
86+
fn run(args: Args) {
87+
let mut cmd = Command::new(args.ctx.collector);
88+
match &args.cmd {
89+
PerfCommand::Eprintln => {
90+
cmd.arg("profile_local").arg("eprintln");
91+
}
92+
PerfCommand::Samply => {
93+
cmd.arg("profile_local").arg("samply");
94+
}
95+
PerfCommand::Cachegrind => {
96+
cmd.arg("profile_local").arg("cachegrind");
97+
}
98+
}
99+
if args.cmd.is_profiling() {
100+
cmd.arg("--out-dir").arg(&args.ctx.results_dir);
101+
}
102+
103+
if !args.opts.include.is_empty() {
104+
cmd.arg("--include").arg(args.opts.include.join(","));
105+
}
106+
if !args.opts.profiles.is_empty() {
107+
cmd.arg("--profiles")
108+
.arg(args.opts.profiles.iter().map(|p| p.to_string()).collect::<Vec<_>>().join(","));
109+
}
110+
if !args.opts.scenarios.is_empty() {
111+
cmd.arg("--scenarios")
112+
.arg(args.opts.scenarios.iter().map(|p| p.to_string()).collect::<Vec<_>>().join(","));
113+
}
114+
cmd.arg(&args.ctx.compiler);
115+
116+
println!("Running `rustc-perf` using `{}`", args.ctx.compiler.display());
117+
118+
const MANIFEST_DIR: &str = env!("CARGO_MANIFEST_DIR");
119+
120+
let rustc_perf_dir = PathBuf::from(MANIFEST_DIR).join("../rustc-perf");
121+
122+
// We need to set the working directory to `src/tools/perf`, so that it can find the directory
123+
// with compile-time benchmarks.
124+
let cmd = cmd.current_dir(rustc_perf_dir);
125+
cmd.status().expect("error while running rustc-perf collector");
126+
127+
if args.cmd.is_profiling() {
128+
println!("You can find the results at `{}`", args.ctx.results_dir.display());
129+
}
130+
}

triagebot.toml

+1
Original file line numberDiff line numberDiff line change
@@ -331,6 +331,7 @@ trigger_files = [
331331
"src/tools/tidy",
332332
"src/tools/rustdoc-gui-test",
333333
"src/tools/libcxx-version",
334+
"src/tools/rustc-perf-wrapper",
334335
]
335336

336337
[autolabel."T-infra"]

0 commit comments

Comments
 (0)