Skip to content

Commit d024cef

Browse files
authored
Rollup merge of #136530 - Kobzol:x-perf, r=onur-ozkan
Implement `x perf` directly in bootstrap Discussed [here](https://rust-lang.zulipchat.com/#narrow/channel/326414-t-infra.2Fbootstrap/topic/Turning.20.60x.20perf.60.20into.20a.20first.20class.20command). Implementing the command directly in bootstrap let's us correctly build the compiler toolchain based on input arguments (such as include rustdoc in the toolchain [only] when needed), and it also makes the CLI interface nicer. r? ``@onur-ozkan``
2 parents 9530d24 + c73ed89 commit d024cef

File tree

20 files changed

+3642
-341
lines changed

20 files changed

+3642
-341
lines changed

Cargo.lock

-7
Original file line numberDiff line numberDiff line change
@@ -3287,13 +3287,6 @@ dependencies = [
32873287
"tikv-jemalloc-sys",
32883288
]
32893289

3290-
[[package]]
3291-
name = "rustc-perf-wrapper"
3292-
version = "0.1.0"
3293-
dependencies = [
3294-
"clap",
3295-
]
3296-
32973290
[[package]]
32983291
name = "rustc-rayon"
32993292
version = "0.5.1"

Cargo.toml

-1
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,6 @@ members = [
4545
"src/tools/rustdoc-gui-test",
4646
"src/tools/opt-dist",
4747
"src/tools/coverage-dump",
48-
"src/tools/rustc-perf-wrapper",
4948
"src/tools/wasm-component-ld",
5049
"src/tools/features-status-dump",
5150
]
+208-12
Original file line numberDiff line numberDiff line change
@@ -1,35 +1,231 @@
1+
use std::fmt::{Display, Formatter};
2+
13
use crate::core::build_steps::compile::{Std, Sysroot};
2-
use crate::core::build_steps::tool::{RustcPerf, Tool};
4+
use crate::core::build_steps::tool::{RustcPerf, Rustdoc};
35
use crate::core::builder::Builder;
46
use crate::core::config::DebuginfoLevel;
7+
use crate::utils::exec::{BootstrapCommand, command};
8+
9+
#[derive(Debug, Clone, clap::Parser)]
10+
pub struct PerfArgs {
11+
#[clap(subcommand)]
12+
cmd: PerfCommand,
13+
}
14+
15+
#[derive(Debug, Clone, clap::Parser)]
16+
enum PerfCommand {
17+
/// Run `profile_local eprintln`.
18+
/// This executes the compiler on the given benchmarks and stores its stderr output.
19+
Eprintln {
20+
#[clap(flatten)]
21+
opts: SharedOpts,
22+
},
23+
/// Run `profile_local samply`
24+
/// This executes the compiler on the given benchmarks and profiles it with `samply`.
25+
/// You need to install `samply`, e.g. using `cargo install samply`.
26+
Samply {
27+
#[clap(flatten)]
28+
opts: SharedOpts,
29+
},
30+
/// Run `profile_local cachegrind`.
31+
/// This executes the compiler on the given benchmarks under `Cachegrind`.
32+
Cachegrind {
33+
#[clap(flatten)]
34+
opts: SharedOpts,
35+
},
36+
/// Run compile benchmarks with a locally built compiler.
37+
Benchmark {
38+
/// Identifier to associate benchmark results with
39+
#[clap(name = "benchmark-id")]
40+
id: String,
41+
42+
#[clap(flatten)]
43+
opts: SharedOpts,
44+
},
45+
/// Compare the results of two previously executed benchmark runs.
46+
Compare {
47+
/// The name of the base artifact to be compared.
48+
base: String,
49+
50+
/// The name of the modified artifact to be compared.
51+
modified: String,
52+
},
53+
}
54+
55+
impl PerfCommand {
56+
fn shared_opts(&self) -> Option<&SharedOpts> {
57+
match self {
58+
PerfCommand::Eprintln { opts, .. }
59+
| PerfCommand::Samply { opts, .. }
60+
| PerfCommand::Cachegrind { opts, .. }
61+
| PerfCommand::Benchmark { opts, .. } => Some(opts),
62+
PerfCommand::Compare { .. } => None,
63+
}
64+
}
65+
}
66+
67+
#[derive(Debug, Clone, clap::Parser)]
68+
struct SharedOpts {
69+
/// Select the benchmarks that you want to run (separated by commas).
70+
/// If unspecified, all benchmarks will be executed.
71+
#[clap(long, global = true, value_delimiter = ',')]
72+
include: Vec<String>,
73+
74+
/// Select the benchmarks matching a prefix in this comma-separated list that you don't want to run.
75+
#[clap(long, global = true, value_delimiter = ',')]
76+
exclude: Vec<String>,
77+
78+
/// Select the scenarios that should be benchmarked.
79+
#[clap(
80+
long,
81+
global = true,
82+
value_delimiter = ',',
83+
default_value = "Full,IncrFull,IncrUnchanged,IncrPatched"
84+
)]
85+
scenarios: Vec<Scenario>,
86+
/// Select the profiles that should be benchmarked.
87+
#[clap(long, global = true, value_delimiter = ',', default_value = "Check,Debug,Opt")]
88+
profiles: Vec<Profile>,
89+
}
90+
91+
#[derive(Clone, Copy, Debug, PartialEq, clap::ValueEnum)]
92+
#[value(rename_all = "PascalCase")]
93+
pub enum Profile {
94+
Check,
95+
Debug,
96+
Doc,
97+
Opt,
98+
Clippy,
99+
}
100+
101+
impl Display for Profile {
102+
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
103+
let name = match self {
104+
Profile::Check => "Check",
105+
Profile::Debug => "Debug",
106+
Profile::Doc => "Doc",
107+
Profile::Opt => "Opt",
108+
Profile::Clippy => "Clippy",
109+
};
110+
f.write_str(name)
111+
}
112+
}
113+
114+
#[derive(Clone, Copy, Debug, clap::ValueEnum)]
115+
#[value(rename_all = "PascalCase")]
116+
pub enum Scenario {
117+
Full,
118+
IncrFull,
119+
IncrUnchanged,
120+
IncrPatched,
121+
}
122+
123+
impl Display for Scenario {
124+
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
125+
let name = match self {
126+
Scenario::Full => "Full",
127+
Scenario::IncrFull => "IncrFull",
128+
Scenario::IncrUnchanged => "IncrUnchanged",
129+
Scenario::IncrPatched => "IncrPatched",
130+
};
131+
f.write_str(name)
132+
}
133+
}
5134

6135
/// Performs profiling using `rustc-perf` on a built version of the compiler.
7-
pub fn perf(builder: &Builder<'_>) {
136+
pub fn perf(builder: &Builder<'_>, args: &PerfArgs) {
8137
let collector = builder.ensure(RustcPerf {
9138
compiler: builder.compiler(0, builder.config.build),
10139
target: builder.config.build,
11140
});
12141

13-
if builder.build.config.rust_debuginfo_level_rustc == DebuginfoLevel::None {
142+
let is_profiling = match &args.cmd {
143+
PerfCommand::Eprintln { .. }
144+
| PerfCommand::Samply { .. }
145+
| PerfCommand::Cachegrind { .. } => true,
146+
PerfCommand::Benchmark { .. } | PerfCommand::Compare { .. } => false,
147+
};
148+
if is_profiling && builder.build.config.rust_debuginfo_level_rustc == DebuginfoLevel::None {
14149
builder.info(r#"WARNING: You are compiling rustc without debuginfo, this will make profiling less useful.
15150
Consider setting `rust.debuginfo-level = 1` in `config.toml`."#);
16151
}
17152

18153
let compiler = builder.compiler(builder.top_stage, builder.config.build);
19154
builder.ensure(Std::new(compiler, builder.config.build));
155+
156+
if let Some(opts) = args.cmd.shared_opts() {
157+
if opts.profiles.contains(&Profile::Doc) {
158+
builder.ensure(Rustdoc { compiler });
159+
}
160+
}
161+
20162
let sysroot = builder.ensure(Sysroot::new(compiler));
21163
let rustc = sysroot.join("bin/rustc");
22164

23165
let rustc_perf_dir = builder.build.tempdir().join("rustc-perf");
24-
let profile_results_dir = rustc_perf_dir.join("results");
166+
let results_dir = rustc_perf_dir.join("results");
167+
builder.create_dir(&results_dir);
168+
169+
let mut cmd = command(collector);
170+
171+
// We need to set the working directory to `src/tools/rustc-perf`, so that it can find the directory
172+
// with compile-time benchmarks.
173+
cmd.current_dir(builder.src.join("src/tools/rustc-perf"));
174+
175+
let db_path = results_dir.join("results.db");
176+
177+
match &args.cmd {
178+
PerfCommand::Eprintln { opts }
179+
| PerfCommand::Samply { opts }
180+
| PerfCommand::Cachegrind { opts } => {
181+
cmd.arg("profile_local");
182+
cmd.arg(match &args.cmd {
183+
PerfCommand::Eprintln { .. } => "eprintln",
184+
PerfCommand::Samply { .. } => "samply",
185+
PerfCommand::Cachegrind { .. } => "cachegrind",
186+
_ => unreachable!(),
187+
});
25188

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);
189+
cmd.arg("--out-dir").arg(&results_dir);
190+
cmd.arg(rustc);
28191

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-
cmd.run(builder);
192+
apply_shared_opts(&mut cmd, opts);
193+
cmd.run(builder);
194+
195+
println!("You can find the results at `{}`", &results_dir.display());
196+
}
197+
PerfCommand::Benchmark { id, opts } => {
198+
cmd.arg("bench_local");
199+
cmd.arg("--db").arg(&db_path);
200+
cmd.arg("--id").arg(id);
201+
cmd.arg(rustc);
202+
203+
apply_shared_opts(&mut cmd, opts);
204+
cmd.run(builder);
205+
}
206+
PerfCommand::Compare { base, modified } => {
207+
cmd.arg("bench_cmp");
208+
cmd.arg("--db").arg(&db_path);
209+
cmd.arg(base).arg(modified);
210+
211+
cmd.run(builder);
212+
}
213+
}
214+
}
215+
216+
fn apply_shared_opts(cmd: &mut BootstrapCommand, opts: &SharedOpts) {
217+
if !opts.include.is_empty() {
218+
cmd.arg("--include").arg(opts.include.join(","));
219+
}
220+
if !opts.exclude.is_empty() {
221+
cmd.arg("--exclude").arg(opts.exclude.join(","));
222+
}
223+
if !opts.profiles.is_empty() {
224+
cmd.arg("--profiles")
225+
.arg(opts.profiles.iter().map(|p| p.to_string()).collect::<Vec<_>>().join(","));
226+
}
227+
if !opts.scenarios.is_empty() {
228+
cmd.arg("--scenarios")
229+
.arg(opts.scenarios.iter().map(|p| p.to_string()).collect::<Vec<_>>().join(","));
230+
}
35231
}

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

-1
Original file line numberDiff line numberDiff line change
@@ -362,7 +362,6 @@ bootstrap_tool!(
362362
GenerateWindowsSys, "src/tools/generate-windows-sys", "generate-windows-sys";
363363
RustdocGUITest, "src/tools/rustdoc-gui-test", "rustdoc-gui-test", is_unstable_tool = true, allow_features = "test";
364364
CoverageDump, "src/tools/coverage-dump", "coverage-dump";
365-
RustcPerfWrapper, "src/tools/rustc-perf-wrapper", "rustc-perf-wrapper";
366365
WasmComponentLd, "src/tools/wasm-component-ld", "wasm-component-ld", is_unstable_tool = true, allow_features = "min_specialization";
367366
UnicodeTableGenerator, "src/tools/unicode-table-generator", "unicode-table-generator";
368367
FeaturesStatusDump, "src/tools/features-status-dump", "features-status-dump";

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

+3-5
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ use clap::{CommandFactory, Parser, ValueEnum};
99
#[cfg(feature = "tracing")]
1010
use tracing::instrument;
1111

12+
use crate::core::build_steps::perf::PerfArgs;
1213
use crate::core::build_steps::setup::Profile;
1314
use crate::core::builder::{Builder, Kind};
1415
use crate::core::config::{Config, TargetSelectionList, target_selection_list};
@@ -481,11 +482,8 @@ Arguments:
481482
#[arg(long)]
482483
versioned_dirs: bool,
483484
},
484-
/// Perform profiling and benchmarking of the compiler using the
485-
/// `rustc-perf-wrapper` tool.
486-
///
487-
/// You need to pass arguments after `--`, e.g.`x perf -- cachegrind`.
488-
Perf {},
485+
/// Perform profiling and benchmarking of the compiler using `rustc-perf`.
486+
Perf(PerfArgs),
489487
}
490488

491489
impl Subcommand {

src/bootstrap/src/lib.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -571,8 +571,8 @@ impl Build {
571571
Subcommand::Suggest { run } => {
572572
return core::build_steps::suggest::suggest(&builder::Builder::new(self), *run);
573573
}
574-
Subcommand::Perf { .. } => {
575-
return core::build_steps::perf::perf(&builder::Builder::new(self));
574+
Subcommand::Perf(args) => {
575+
return core::build_steps::perf::perf(&builder::Builder::new(self), args);
576576
}
577577
_cmd => {
578578
debug!(cmd = ?_cmd, "not a hardcoded subcommand; returning to normal handling");

src/doc/rustc-dev-guide/src/profiling/with_rustc_perf.md

+1-3
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,7 @@ However, using the suite manually can be a bit cumbersome. To make this easier f
77
the compiler build system (`bootstrap`) also provides built-in integration with the benchmarking suite,
88
which will download and build the suite for you, build a local compiler toolchain and let you profile it using a simplified command-line interface.
99

10-
You can use the `./x perf -- <command> [options]` command to use this integration.
11-
12-
> Note that you need to specify arguments after `--` in the `x perf` command! You will not be able to pass arguments without the double dashes.
10+
You can use the `./x perf <command> [options]` command to use this integration.
1311

1412
You can use normal bootstrap flags for this command, such as `--stage 1` or `--stage 2`, for example to modify the stage of the created sysroot. It might also be useful to configure `config.toml` to better support profiling, e.g. set `rust.debuginfo-level = 1` to add source line information to the built compiler.
1513

0 commit comments

Comments
 (0)