Skip to content

Implement x perf directly in bootstrap #136530

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 5 commits into from
Feb 9, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 0 additions & 7 deletions Cargo.lock
Original file line number Diff line number Diff line change
Expand Up @@ -3287,13 +3287,6 @@ dependencies = [
"tikv-jemalloc-sys",
]

[[package]]
name = "rustc-perf-wrapper"
version = "0.1.0"
dependencies = [
"clap",
]

[[package]]
name = "rustc-rayon"
version = "0.5.1"
Expand Down
1 change: 0 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,6 @@ members = [
"src/tools/rustdoc-gui-test",
"src/tools/opt-dist",
"src/tools/coverage-dump",
"src/tools/rustc-perf-wrapper",
"src/tools/wasm-component-ld",
"src/tools/features-status-dump",
]
Expand Down
220 changes: 208 additions & 12 deletions src/bootstrap/src/core/build_steps/perf.rs
Original file line number Diff line number Diff line change
@@ -1,35 +1,231 @@
use std::fmt::{Display, Formatter};

use crate::core::build_steps::compile::{Std, Sysroot};
use crate::core::build_steps::tool::{RustcPerf, Tool};
use crate::core::build_steps::tool::{RustcPerf, Rustdoc};
use crate::core::builder::Builder;
use crate::core::config::DebuginfoLevel;
use crate::utils::exec::{BootstrapCommand, command};

#[derive(Debug, Clone, clap::Parser)]
pub struct PerfArgs {
#[clap(subcommand)]
cmd: PerfCommand,
}

#[derive(Debug, Clone, clap::Parser)]
enum PerfCommand {
/// Run `profile_local eprintln`.
/// This executes the compiler on the given benchmarks and stores its stderr output.
Eprintln {
#[clap(flatten)]
opts: SharedOpts,
},
/// Run `profile_local samply`
/// This executes the compiler on the given benchmarks and profiles it with `samply`.
/// You need to install `samply`, e.g. using `cargo install samply`.
Samply {
#[clap(flatten)]
opts: SharedOpts,
},
/// Run `profile_local cachegrind`.
/// This executes the compiler on the given benchmarks under `Cachegrind`.
Cachegrind {
#[clap(flatten)]
opts: SharedOpts,
},
/// Run compile benchmarks with a locally built compiler.
Benchmark {
/// Identifier to associate benchmark results with
#[clap(name = "benchmark-id")]
id: String,

#[clap(flatten)]
opts: SharedOpts,
},
/// Compare the results of two previously executed benchmark runs.
Compare {
/// The name of the base artifact to be compared.
base: String,

/// The name of the modified artifact to be compared.
modified: String,
},
}

impl PerfCommand {
fn shared_opts(&self) -> Option<&SharedOpts> {
match self {
PerfCommand::Eprintln { opts, .. }
| PerfCommand::Samply { opts, .. }
| PerfCommand::Cachegrind { opts, .. }
| PerfCommand::Benchmark { opts, .. } => Some(opts),
PerfCommand::Compare { .. } => None,
}
}
}

#[derive(Debug, Clone, clap::Parser)]
struct SharedOpts {
/// Select the benchmarks that you want to run (separated by commas).
/// If unspecified, all benchmarks will be executed.
#[clap(long, global = true, value_delimiter = ',')]
include: Vec<String>,

/// Select the benchmarks matching a prefix in this comma-separated list that you don't want to run.
#[clap(long, global = true, value_delimiter = ',')]
exclude: Vec<String>,

/// Select the scenarios that should be benchmarked.
#[clap(
long,
global = true,
value_delimiter = ',',
default_value = "Full,IncrFull,IncrUnchanged,IncrPatched"
)]
scenarios: Vec<Scenario>,
/// Select the profiles that should be benchmarked.
#[clap(long, global = true, value_delimiter = ',', default_value = "Check,Debug,Opt")]
profiles: Vec<Profile>,
}

#[derive(Clone, Copy, Debug, PartialEq, clap::ValueEnum)]
#[value(rename_all = "PascalCase")]
pub enum Profile {
Check,
Debug,
Doc,
Opt,
Clippy,
}

impl Display for Profile {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
let name = match self {
Profile::Check => "Check",
Profile::Debug => "Debug",
Profile::Doc => "Doc",
Profile::Opt => "Opt",
Profile::Clippy => "Clippy",
};
f.write_str(name)
}
}

#[derive(Clone, Copy, Debug, clap::ValueEnum)]
#[value(rename_all = "PascalCase")]
pub enum Scenario {
Full,
IncrFull,
IncrUnchanged,
IncrPatched,
}

impl Display for Scenario {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
let name = match self {
Scenario::Full => "Full",
Scenario::IncrFull => "IncrFull",
Scenario::IncrUnchanged => "IncrUnchanged",
Scenario::IncrPatched => "IncrPatched",
};
f.write_str(name)
}
}

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

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

let compiler = builder.compiler(builder.top_stage, builder.config.build);
builder.ensure(Std::new(compiler, builder.config.build));

if let Some(opts) = args.cmd.shared_opts() {
if opts.profiles.contains(&Profile::Doc) {
builder.ensure(Rustdoc { compiler });
}
}

let sysroot = builder.ensure(Sysroot::new(compiler));
let rustc = sysroot.join("bin/rustc");

let rustc_perf_dir = builder.build.tempdir().join("rustc-perf");
let profile_results_dir = rustc_perf_dir.join("results");
let results_dir = rustc_perf_dir.join("results");
builder.create_dir(&results_dir);

let mut cmd = command(collector);

// We need to set the working directory to `src/tools/rustc-perf`, so that it can find the directory
// with compile-time benchmarks.
cmd.current_dir(builder.src.join("src/tools/rustc-perf"));

let db_path = results_dir.join("results.db");

match &args.cmd {
PerfCommand::Eprintln { opts }
| PerfCommand::Samply { opts }
| PerfCommand::Cachegrind { opts } => {
cmd.arg("profile_local");
cmd.arg(match &args.cmd {
PerfCommand::Eprintln { .. } => "eprintln",
PerfCommand::Samply { .. } => "samply",
PerfCommand::Cachegrind { .. } => "cachegrind",
_ => unreachable!(),
});

// We need to take args passed after `--` and pass them to `rustc-perf-wrapper`
let args = std::env::args().skip_while(|a| a != "--").skip(1);
cmd.arg("--out-dir").arg(&results_dir);
cmd.arg(rustc);

let mut cmd = builder.tool_cmd(Tool::RustcPerfWrapper);
cmd.env("RUSTC_REAL", rustc)
.env("PERF_COLLECTOR", collector)
.env("PERF_RESULT_DIR", profile_results_dir)
.args(args);
cmd.run(builder);
apply_shared_opts(&mut cmd, opts);
cmd.run(builder);

println!("You can find the results at `{}`", &results_dir.display());
}
PerfCommand::Benchmark { id, opts } => {
cmd.arg("bench_local");
cmd.arg("--db").arg(&db_path);
cmd.arg("--id").arg(id);
cmd.arg(rustc);

apply_shared_opts(&mut cmd, opts);
cmd.run(builder);
}
PerfCommand::Compare { base, modified } => {
cmd.arg("bench_cmp");
cmd.arg("--db").arg(&db_path);
cmd.arg(base).arg(modified);

cmd.run(builder);
}
}
}

fn apply_shared_opts(cmd: &mut BootstrapCommand, opts: &SharedOpts) {
if !opts.include.is_empty() {
cmd.arg("--include").arg(opts.include.join(","));
}
if !opts.exclude.is_empty() {
cmd.arg("--exclude").arg(opts.exclude.join(","));
}
if !opts.profiles.is_empty() {
cmd.arg("--profiles")
.arg(opts.profiles.iter().map(|p| p.to_string()).collect::<Vec<_>>().join(","));
}
if !opts.scenarios.is_empty() {
cmd.arg("--scenarios")
.arg(opts.scenarios.iter().map(|p| p.to_string()).collect::<Vec<_>>().join(","));
}
}
1 change: 0 additions & 1 deletion src/bootstrap/src/core/build_steps/tool.rs
Original file line number Diff line number Diff line change
Expand Up @@ -362,7 +362,6 @@ bootstrap_tool!(
GenerateWindowsSys, "src/tools/generate-windows-sys", "generate-windows-sys";
RustdocGUITest, "src/tools/rustdoc-gui-test", "rustdoc-gui-test", is_unstable_tool = true, allow_features = "test";
CoverageDump, "src/tools/coverage-dump", "coverage-dump";
RustcPerfWrapper, "src/tools/rustc-perf-wrapper", "rustc-perf-wrapper";
WasmComponentLd, "src/tools/wasm-component-ld", "wasm-component-ld", is_unstable_tool = true, allow_features = "min_specialization";
UnicodeTableGenerator, "src/tools/unicode-table-generator", "unicode-table-generator";
FeaturesStatusDump, "src/tools/features-status-dump", "features-status-dump";
Expand Down
8 changes: 3 additions & 5 deletions src/bootstrap/src/core/config/flags.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ use clap::{CommandFactory, Parser, ValueEnum};
#[cfg(feature = "tracing")]
use tracing::instrument;

use crate::core::build_steps::perf::PerfArgs;
use crate::core::build_steps::setup::Profile;
use crate::core::builder::{Builder, Kind};
use crate::core::config::{Config, TargetSelectionList, target_selection_list};
Expand Down Expand Up @@ -481,11 +482,8 @@ Arguments:
#[arg(long)]
versioned_dirs: bool,
},
/// Perform profiling and benchmarking of the compiler using the
/// `rustc-perf-wrapper` tool.
///
/// You need to pass arguments after `--`, e.g.`x perf -- cachegrind`.
Perf {},
/// Perform profiling and benchmarking of the compiler using `rustc-perf`.
Perf(PerfArgs),
}

impl Subcommand {
Expand Down
4 changes: 2 additions & 2 deletions src/bootstrap/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -571,8 +571,8 @@ impl Build {
Subcommand::Suggest { run } => {
return core::build_steps::suggest::suggest(&builder::Builder::new(self), *run);
}
Subcommand::Perf { .. } => {
return core::build_steps::perf::perf(&builder::Builder::new(self));
Subcommand::Perf(args) => {
return core::build_steps::perf::perf(&builder::Builder::new(self), args);
}
_cmd => {
debug!(cmd = ?_cmd, "not a hardcoded subcommand; returning to normal handling");
Expand Down
4 changes: 1 addition & 3 deletions src/doc/rustc-dev-guide/src/profiling/with_rustc_perf.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,7 @@ However, using the suite manually can be a bit cumbersome. To make this easier f
the compiler build system (`bootstrap`) also provides built-in integration with the benchmarking suite,
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.

You can use the `./x perf -- <command> [options]` command to use this integration.

> 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.
You can use the `./x perf <command> [options]` command to use this integration.

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.

Expand Down
Loading
Loading