diff options
| author | Urgau <3616612+Urgau@users.noreply.github.com> | 2025-02-09 00:37:27 +0100 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2025-02-09 00:37:27 +0100 |
| commit | d024cef0577ad6f3e370dfc3d5befe47695235b8 (patch) | |
| tree | fbca0d8f9a752ada022faab9bd7679baf2d0dded /src/bootstrap | |
| parent | 9530d243d740eb7f8eddc0a08dad635a8ff4189a (diff) | |
| parent | c73ed895c700bd646af5bfbd111268fcaca9fda0 (diff) | |
| download | rust-d024cef0577ad6f3e370dfc3d5befe47695235b8.tar.gz rust-d024cef0577ad6f3e370dfc3d5befe47695235b8.zip | |
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``
Diffstat (limited to 'src/bootstrap')
| -rw-r--r-- | src/bootstrap/src/core/build_steps/perf.rs | 220 | ||||
| -rw-r--r-- | src/bootstrap/src/core/build_steps/tool.rs | 1 | ||||
| -rw-r--r-- | src/bootstrap/src/core/config/flags.rs | 8 | ||||
| -rw-r--r-- | src/bootstrap/src/lib.rs | 4 |
4 files changed, 213 insertions, 20 deletions
diff --git a/src/bootstrap/src/core/build_steps/perf.rs b/src/bootstrap/src/core/build_steps/perf.rs index 5b83080a326..98c63a41e76 100644 --- a/src/bootstrap/src/core/build_steps/perf.rs +++ b/src/bootstrap/src/core/build_steps/perf.rs @@ -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(",")); + } } diff --git a/src/bootstrap/src/core/build_steps/tool.rs b/src/bootstrap/src/core/build_steps/tool.rs index 75fac88252b..734ade8bfed 100644 --- a/src/bootstrap/src/core/build_steps/tool.rs +++ b/src/bootstrap/src/core/build_steps/tool.rs @@ -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"; diff --git a/src/bootstrap/src/core/config/flags.rs b/src/bootstrap/src/core/config/flags.rs index 27fb00cb06e..c08a041ebcd 100644 --- a/src/bootstrap/src/core/config/flags.rs +++ b/src/bootstrap/src/core/config/flags.rs @@ -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}; @@ -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 { diff --git a/src/bootstrap/src/lib.rs b/src/bootstrap/src/lib.rs index 2dd83d5938e..31cb5823b72 100644 --- a/src/bootstrap/src/lib.rs +++ b/src/bootstrap/src/lib.rs @@ -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"); |
