Skip to content
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

Add a x perf command for integrating bootstrap with rustc-perf #126318

Merged
merged 5 commits into from
Jun 22, 2024
Merged
Show file tree
Hide file tree
Changes from 4 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
1 change: 1 addition & 0 deletions src/bootstrap/src/core/build_steps/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ pub(crate) mod doc;
pub(crate) mod format;
pub(crate) mod install;
pub(crate) mod llvm;
pub(crate) mod perf;
pub(crate) mod run;
pub(crate) mod setup;
pub(crate) mod suggest;
Expand Down
45 changes: 45 additions & 0 deletions src/bootstrap/src/core/build_steps/perf.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
use std::process::Command;

use crate::core::build_steps::compile::{Std, Sysroot};
use crate::core::build_steps::tool::RustcPerf;
use crate::core::builder::Builder;
use crate::core::config::DebuginfoLevel;

/// Performs profiling using `rustc-perf` on a built version of the compiler.
pub fn perf(builder: &Builder<'_>) {
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 {
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));
let sysroot = builder.ensure(Sysroot::new(compiler));
let rustc = sysroot.join("bin/rustc");

let results_dir = builder.build.out.join("rustc-perf");
Kobzol marked this conversation as resolved.
Show resolved Hide resolved

let mut cmd = Command::new(collector);
let cmd = cmd
.arg("profile_local")
.arg("eprintln")
.arg("--out-dir")
.arg(&results_dir)
.arg("--include")
.arg("helloworld")
.arg(&rustc);

builder.info(&format!("Running `rustc-perf` using `{}`", rustc.display()));

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

builder.info(&format!("You can find the results at `{}`", results_dir.display()));
}
81 changes: 77 additions & 4 deletions src/bootstrap/src/core/build_steps/tool.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,8 @@ struct ToolBuild {
extra_features: Vec<String>,
/// Nightly-only features that are allowed (comma-separated list).
allow_features: &'static str,
/// Additional arguments to pass to the `cargo` invocation.
cargo_args: Vec<String>,
}

impl Builder<'_> {
Expand Down Expand Up @@ -100,6 +102,7 @@ impl Step for ToolBuild {
if !self.allow_features.is_empty() {
cargo.allow_features(self.allow_features);
}
cargo.args(self.cargo_args);
let _guard = builder.msg_tool(
Kind::Build,
self.mode,
Expand All @@ -126,10 +129,7 @@ impl Step for ToolBuild {
if tool == "tidy" {
tool = "rust-tidy";
}
let cargo_out = builder.cargo_out(compiler, self.mode, target).join(exe(tool, target));
let bin = builder.tools_dir(compiler).join(exe(tool, target));
builder.copy_link(&cargo_out, &bin);
bin
copy_tool_bin(builder, self.compiler, self.target, self.mode, tool)
}
}
}
Expand Down Expand Up @@ -214,6 +214,21 @@ pub fn prepare_tool_cargo(
cargo
}

/// Copies a built tool binary with the given `name` from the build directory to the
/// tools directory.
Kobzol marked this conversation as resolved.
Show resolved Hide resolved
fn copy_tool_bin(
builder: &Builder<'_>,
compiler: Compiler,
target: TargetSelection,
mode: Mode,
name: &str,
) -> PathBuf {
let cargo_out = builder.cargo_out(compiler, mode, target).join(exe(name, target));
let bin = builder.tools_dir(compiler).join(exe(name, target));
builder.copy_link(&cargo_out, &bin);
bin
}

macro_rules! bootstrap_tool {
($(
$name:ident, $path:expr, $tool_name:expr
Expand Down Expand Up @@ -283,6 +298,7 @@ macro_rules! bootstrap_tool {
},
extra_features: vec![],
allow_features: concat!($($allow_features)*),
cargo_args: vec![]
})
}
}
Expand Down Expand Up @@ -349,10 +365,60 @@ impl Step for OptimizedDist {
source_type: SourceType::InTree,
extra_features: Vec::new(),
allow_features: "",
cargo_args: Vec::new(),
})
}
}

/// The [rustc-perf](https://github.com/rust-lang/rustc-perf) benchmark suite, which is added
/// as a submodule at `src/tools/rustc-perf`.
#[derive(Debug, Clone, Hash, PartialEq, Eq)]
pub struct RustcPerf {
pub compiler: Compiler,
pub target: TargetSelection,
}

impl Step for RustcPerf {
/// Path to the built `collector` binary.
type Output = PathBuf;

fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
run.path("src/tools/rustc-perf")
}

fn make_run(run: RunConfig<'_>) {
run.builder.ensure(RustcPerf {
compiler: run.builder.compiler(0, run.builder.config.build),
target: run.target,
});
}

fn run(self, builder: &Builder<'_>) -> PathBuf {
// We need to ensure the rustc-perf submodule is initialized.
builder.update_submodule(Path::new("src/tools/rustc-perf"));

let tool = ToolBuild {
compiler: self.compiler,
target: self.target,
tool: "collector",
mode: Mode::ToolBootstrap,
path: "src/tools/rustc-perf",
source_type: SourceType::Submodule,
extra_features: Vec::new(),
allow_features: "",
// Only build the collector package, which is used for benchmarking through
// a CLI.
cargo_args: vec!["-p".to_string(), "collector".to_string()],
};
let collector_bin = builder.ensure(tool.clone());
// We also need to symlink the `rustc-fake` binary to the corresponding directory,
// because `collector` expects it in the same directory.
copy_tool_bin(builder, tool.compiler, tool.target, tool.mode, "rustc-fake");

collector_bin
}
}

#[derive(Debug, Clone, Hash, PartialEq, Eq, Ord, PartialOrd)]
pub struct ErrorIndex {
pub compiler: Compiler,
Expand Down Expand Up @@ -403,6 +469,7 @@ impl Step for ErrorIndex {
source_type: SourceType::InTree,
extra_features: Vec::new(),
allow_features: "",
cargo_args: Vec::new(),
})
}
}
Expand Down Expand Up @@ -437,6 +504,7 @@ impl Step for RemoteTestServer {
source_type: SourceType::InTree,
extra_features: Vec::new(),
allow_features: "",
cargo_args: Vec::new(),
})
}
}
Expand Down Expand Up @@ -598,6 +666,7 @@ impl Step for Cargo {
source_type: SourceType::Submodule,
extra_features: Vec::new(),
allow_features: "",
cargo_args: Vec::new(),
})
}
}
Expand Down Expand Up @@ -625,6 +694,7 @@ impl Step for LldWrapper {
source_type: SourceType::InTree,
extra_features: Vec::new(),
allow_features: "",
cargo_args: Vec::new(),
})
}
}
Expand Down Expand Up @@ -673,6 +743,7 @@ impl Step for RustAnalyzer {
extra_features: vec!["in-rust-tree".to_owned()],
source_type: SourceType::InTree,
allow_features: RustAnalyzer::ALLOW_FEATURES,
cargo_args: Vec::new(),
})
}
}
Expand Down Expand Up @@ -720,6 +791,7 @@ impl Step for RustAnalyzerProcMacroSrv {
extra_features: vec!["in-rust-tree".to_owned()],
source_type: SourceType::InTree,
allow_features: RustAnalyzer::ALLOW_FEATURES,
cargo_args: Vec::new(),
});

// Copy `rust-analyzer-proc-macro-srv` to `<sysroot>/libexec/`
Expand Down Expand Up @@ -916,6 +988,7 @@ macro_rules! tool_extended {
extra_features: $sel.extra_features,
source_type: SourceType::InTree,
allow_features: concat!($($allow_features)*),
cargo_args: vec![]
});

if (false $(|| !$add_bins_to_sysroot.is_empty())?) && $sel.compiler.stage > 0 {
Expand Down
9 changes: 7 additions & 2 deletions src/bootstrap/src/core/builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -666,6 +666,7 @@ pub enum Kind {
Setup,
Suggest,
Vendor,
Perf,
}

impl Kind {
Expand All @@ -687,6 +688,7 @@ impl Kind {
Kind::Setup => "setup",
Kind::Suggest => "suggest",
Kind::Vendor => "vendor",
Kind::Perf => "perf",
}
}

Expand All @@ -698,6 +700,7 @@ impl Kind {
Kind::Run => "Running",
Kind::Suggest => "Suggesting",
Kind::Clippy => "Linting",
Kind::Perf => "Profiling & benchmarking",
_ => {
let title_letter = self.as_str()[0..1].to_ascii_uppercase();
return format!("{title_letter}{}ing", &self.as_str()[1..]);
Expand Down Expand Up @@ -750,7 +753,8 @@ impl<'a> Builder<'a> {
tool::RustdocGUITest,
tool::OptimizedDist,
tool::CoverageDump,
tool::LlvmBitcodeLinker
tool::LlvmBitcodeLinker,
tool::RustcPerf,
),
Kind::Clippy => describe!(
clippy::Std,
Expand Down Expand Up @@ -950,7 +954,7 @@ impl<'a> Builder<'a> {
Kind::Clean => describe!(clean::CleanAll, clean::Rustc, clean::Std),
Kind::Vendor => describe!(vendor::Vendor),
// special-cased in Build::build()
Kind::Format | Kind::Suggest => vec![],
Kind::Format | Kind::Suggest | Kind::Perf => vec![],
}
}

Expand Down Expand Up @@ -1022,6 +1026,7 @@ impl<'a> Builder<'a> {
path.as_ref().map_or([].as_slice(), |path| std::slice::from_ref(path)),
),
Subcommand::Vendor { .. } => (Kind::Vendor, &paths[..]),
Subcommand::Perf { .. } => (Kind::Perf, &paths[..]),
};

Self::new_internal(build, kind, paths.to_owned())
Expand Down
4 changes: 3 additions & 1 deletion src/bootstrap/src/core/config/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2027,6 +2027,7 @@ impl Config {
Subcommand::Bench { .. } => flags.stage.or(bench_stage).unwrap_or(2),
Subcommand::Dist { .. } => flags.stage.or(dist_stage).unwrap_or(2),
Subcommand::Install { .. } => flags.stage.or(install_stage).unwrap_or(2),
Subcommand::Perf { .. } => flags.stage.unwrap_or(1),
// These are all bootstrap tools, which don't depend on the compiler.
// The stage we pass shouldn't matter, but use 0 just in case.
Subcommand::Clean { .. }
Expand Down Expand Up @@ -2064,7 +2065,8 @@ impl Config {
| Subcommand::Setup { .. }
| Subcommand::Format { .. }
| Subcommand::Suggest { .. }
| Subcommand::Vendor { .. } => {}
| Subcommand::Vendor { .. }
| Subcommand::Perf { .. } => {}
}
}

Expand Down
4 changes: 4 additions & 0 deletions src/bootstrap/src/core/config/flags.rs
Original file line number Diff line number Diff line change
Expand Up @@ -469,6 +469,9 @@ Arguments:
#[arg(long)]
versioned_dirs: bool,
},
/// Perform profiling and benchmarking of the compiler using the
/// `rustc-perf` benchmark suite.
Perf {},
}

impl Subcommand {
Expand All @@ -490,6 +493,7 @@ impl Subcommand {
Subcommand::Setup { .. } => Kind::Setup,
Subcommand::Suggest { .. } => Kind::Suggest,
Subcommand::Vendor { .. } => Kind::Vendor,
Subcommand::Perf { .. } => Kind::Perf,
}
}

Expand Down
3 changes: 3 additions & 0 deletions src/bootstrap/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -672,6 +672,9 @@ 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));
}
_ => (),
}

Expand Down
35 changes: 35 additions & 0 deletions src/etc/completions/x.py.fish
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ complete -c x.py -n "__fish_use_subcommand" -f -a "run" -d 'Run tools contained
complete -c x.py -n "__fish_use_subcommand" -f -a "setup" -d 'Set up the environment for development'
complete -c x.py -n "__fish_use_subcommand" -f -a "suggest" -d 'Suggest a subset of tests to run, based on modified files'
complete -c x.py -n "__fish_use_subcommand" -f -a "vendor" -d 'Vendor dependencies'
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'
complete -c x.py -n "__fish_seen_subcommand_from build" -l config -d 'TOML configuration file for build' -r -F
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)"
complete -c x.py -n "__fish_seen_subcommand_from build" -l build -d 'build target of the stage0 compiler' -r -f
Expand Down Expand Up @@ -628,3 +629,37 @@ complete -c x.py -n "__fish_seen_subcommand_from vendor" -l llvm-profile-generat
complete -c x.py -n "__fish_seen_subcommand_from vendor" -l enable-bolt-settings -d 'Enable BOLT link flags'
complete -c x.py -n "__fish_seen_subcommand_from vendor" -l skip-stage0-validation -d 'Skip stage0 compiler validation'
complete -c x.py -n "__fish_seen_subcommand_from vendor" -s h -l help -d 'Print help (see more with \'--help\')'
complete -c x.py -n "__fish_seen_subcommand_from perf" -l config -d 'TOML configuration file for build' -r -F
complete -c x.py -n "__fish_seen_subcommand_from perf" -l build-dir -d 'Build directory, overrides `build.build-dir` in `config.toml`' -r -f -a "(__fish_complete_directories)"
complete -c x.py -n "__fish_seen_subcommand_from perf" -l build -d 'build target of the stage0 compiler' -r -f
complete -c x.py -n "__fish_seen_subcommand_from perf" -l host -d 'host targets to build' -r -f
complete -c x.py -n "__fish_seen_subcommand_from perf" -l target -d 'target targets to build' -r -f
complete -c x.py -n "__fish_seen_subcommand_from perf" -l exclude -d 'build paths to exclude' -r -F
complete -c x.py -n "__fish_seen_subcommand_from perf" -l skip -d 'build paths to skip' -r -F
complete -c x.py -n "__fish_seen_subcommand_from perf" -l rustc-error-format -r -f
complete -c x.py -n "__fish_seen_subcommand_from perf" -l on-fail -d 'command to run on failure' -r -f -a "(__fish_complete_command)"
complete -c x.py -n "__fish_seen_subcommand_from perf" -l stage -d 'stage to build (indicates compiler to use/test, e.g., stage 0 uses the bootstrap compiler, stage 1 the stage 0 rustc artifacts, etc.)' -r -f
complete -c x.py -n "__fish_seen_subcommand_from perf" -l keep-stage -d 'stage(s) to keep without recompiling (pass multiple times to keep e.g., both stages 0 and 1)' -r -f
complete -c x.py -n "__fish_seen_subcommand_from perf" -l keep-stage-std -d 'stage(s) of the standard library to keep without recompiling (pass multiple times to keep e.g., both stages 0 and 1)' -r -f
complete -c x.py -n "__fish_seen_subcommand_from perf" -l src -d 'path to the root of the rust checkout' -r -f -a "(__fish_complete_directories)"
complete -c x.py -n "__fish_seen_subcommand_from perf" -s j -l jobs -d 'number of jobs to run in parallel' -r -f
complete -c x.py -n "__fish_seen_subcommand_from perf" -l warnings -d 'if value is deny, will deny warnings if value is warn, will emit warnings otherwise, use the default configured behaviour' -r -f -a "{deny '',warn '',default ''}"
complete -c x.py -n "__fish_seen_subcommand_from perf" -l error-format -d 'rustc error format' -r -f
complete -c x.py -n "__fish_seen_subcommand_from perf" -l color -d 'whether to use color in cargo and rustc output' -r -f -a "{always '',never '',auto ''}"
complete -c x.py -n "__fish_seen_subcommand_from perf" -l llvm-skip-rebuild -d 'whether rebuilding llvm should be skipped, overriding `skip-rebuld` in config.toml' -r -f -a "{true '',false ''}"
complete -c x.py -n "__fish_seen_subcommand_from perf" -l rust-profile-generate -d 'generate PGO profile with rustc build' -r -F
complete -c x.py -n "__fish_seen_subcommand_from perf" -l rust-profile-use -d 'use PGO profile for rustc build' -r -F
complete -c x.py -n "__fish_seen_subcommand_from perf" -l llvm-profile-use -d 'use PGO profile for LLVM build' -r -F
complete -c x.py -n "__fish_seen_subcommand_from perf" -l reproducible-artifact -d 'Additional reproducible artifacts that should be added to the reproducible artifacts archive' -r
complete -c x.py -n "__fish_seen_subcommand_from perf" -l set -d 'override options in config.toml' -r -f
complete -c x.py -n "__fish_seen_subcommand_from perf" -s v -l verbose -d 'use verbose output (-vv for very verbose)'
complete -c x.py -n "__fish_seen_subcommand_from perf" -s i -l incremental -d 'use incremental compilation'
complete -c x.py -n "__fish_seen_subcommand_from perf" -l include-default-paths -d 'include default paths in addition to the provided ones'
complete -c x.py -n "__fish_seen_subcommand_from perf" -l dry-run -d 'dry run; don\'t build anything'
complete -c x.py -n "__fish_seen_subcommand_from perf" -l dump-bootstrap-shims -d 'Indicates whether to dump the work done from bootstrap shims'
complete -c x.py -n "__fish_seen_subcommand_from perf" -l json-output -d 'use message-format=json'
complete -c x.py -n "__fish_seen_subcommand_from perf" -l bypass-bootstrap-lock -d 'Bootstrap uses this value to decide whether it should bypass locking the build process. This is rarely needed (e.g., compiling the std library for different targets in parallel)'
complete -c x.py -n "__fish_seen_subcommand_from perf" -l llvm-profile-generate -d 'generate PGO profile with llvm built for rustc'
complete -c x.py -n "__fish_seen_subcommand_from perf" -l enable-bolt-settings -d 'Enable BOLT link flags'
complete -c x.py -n "__fish_seen_subcommand_from perf" -l skip-stage0-validation -d 'Skip stage0 compiler validation'
complete -c x.py -n "__fish_seen_subcommand_from perf" -s h -l help -d 'Print help (see more with \'--help\')'
Loading
Loading