summaryrefslogtreecommitdiffstats
path: root/src/tools/opt-dist
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-06-07 05:48:48 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-06-07 05:48:48 +0000
commitef24de24a82fe681581cc130f342363c47c0969a (patch)
tree0d494f7e1a38b95c92426f58fe6eaa877303a86c /src/tools/opt-dist
parentReleasing progress-linux version 1.74.1+dfsg1-1~progress7.99u1. (diff)
downloadrustc-ef24de24a82fe681581cc130f342363c47c0969a.tar.xz
rustc-ef24de24a82fe681581cc130f342363c47c0969a.zip
Merging upstream version 1.75.0+dfsg1.
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'src/tools/opt-dist')
-rw-r--r--src/tools/opt-dist/Cargo.toml1
-rw-r--r--src/tools/opt-dist/src/bolt.rs14
-rw-r--r--src/tools/opt-dist/src/exec.rs9
-rw-r--r--src/tools/opt-dist/src/main.rs76
-rw-r--r--src/tools/opt-dist/src/tests.rs4
-rw-r--r--src/tools/opt-dist/src/training.rs53
-rw-r--r--src/tools/opt-dist/src/utils/artifact_size.rs61
-rw-r--r--src/tools/opt-dist/src/utils/mod.rs51
8 files changed, 194 insertions, 75 deletions
diff --git a/src/tools/opt-dist/Cargo.toml b/src/tools/opt-dist/Cargo.toml
index f1c3dd6aa..9e852b064 100644
--- a/src/tools/opt-dist/Cargo.toml
+++ b/src/tools/opt-dist/Cargo.toml
@@ -23,3 +23,4 @@ glob = "0.3"
tempfile = "3.5"
derive_builder = "0.12"
clap = { version = "4", features = ["derive"] }
+tabled = { version = "0.13", default-features = false, features = ["std"] }
diff --git a/src/tools/opt-dist/src/bolt.rs b/src/tools/opt-dist/src/bolt.rs
index cf9f4fabc..f694c08f9 100644
--- a/src/tools/opt-dist/src/bolt.rs
+++ b/src/tools/opt-dist/src/bolt.rs
@@ -1,14 +1,14 @@
use anyhow::Context;
use crate::exec::cmd;
-use crate::training::LlvmBoltProfile;
+use crate::training::BoltProfile;
use camino::{Utf8Path, Utf8PathBuf};
use crate::utils::io::copy_file;
/// Instruments an artifact at the given `path` (in-place) with BOLT and then calls `func`.
/// After this function finishes, the original file will be restored.
-pub fn with_bolt_instrumented<F: FnOnce() -> anyhow::Result<R>, R>(
+pub fn with_bolt_instrumented<F: FnOnce(&Utf8Path) -> anyhow::Result<R>, R>(
path: &Utf8Path,
func: F,
) -> anyhow::Result<R> {
@@ -20,10 +20,16 @@ pub fn with_bolt_instrumented<F: FnOnce() -> anyhow::Result<R>, R>(
let instrumented_path = tempfile::NamedTempFile::new()?.into_temp_path();
+ let profile_dir =
+ tempfile::TempDir::new().context("Could not create directory for BOLT profiles")?;
+ let profile_prefix = profile_dir.path().join("prof.fdata");
+ let profile_prefix = Utf8Path::from_path(&profile_prefix).unwrap();
+
// Instrument the original file with BOLT, saving the result into `instrumented_path`
cmd(&["llvm-bolt"])
.arg("-instrument")
.arg(path)
+ .arg(&format!("--instrumentation-file={profile_prefix}"))
// Make sure that each process will write its profiles into a separate file
.arg("--instrumentation-file-append-pid")
.arg("-o")
@@ -36,11 +42,11 @@ pub fn with_bolt_instrumented<F: FnOnce() -> anyhow::Result<R>, R>(
// Run the function that will make use of the instrumented artifact.
// The original file will be restored when `_backup_file` is dropped.
- func()
+ func(profile_prefix)
}
/// Optimizes the file at `path` with BOLT in-place using the given `profile`.
-pub fn bolt_optimize(path: &Utf8Path, profile: &LlvmBoltProfile) -> anyhow::Result<()> {
+pub fn bolt_optimize(path: &Utf8Path, profile: &BoltProfile) -> anyhow::Result<()> {
// Copy the artifact to a new location, so that we do not use the same input and output file.
// BOLT cannot handle optimizing when the input and output is the same file, because it performs
// in-place patching.
diff --git a/src/tools/opt-dist/src/exec.rs b/src/tools/opt-dist/src/exec.rs
index 04e018452..90a045e83 100644
--- a/src/tools/opt-dist/src/exec.rs
+++ b/src/tools/opt-dist/src/exec.rs
@@ -1,7 +1,7 @@
use crate::environment::Environment;
use crate::metrics::{load_metrics, record_metrics};
use crate::timer::TimerSection;
-use crate::training::{LlvmBoltProfile, LlvmPGOProfile, RustcPGOProfile};
+use crate::training::{BoltProfile, LlvmPGOProfile, RustcPGOProfile};
use camino::{Utf8Path, Utf8PathBuf};
use std::collections::BTreeMap;
use std::fs::File;
@@ -159,7 +159,12 @@ impl Bootstrap {
self
}
- pub fn with_bolt_profile(mut self, profile: LlvmBoltProfile) -> Self {
+ pub fn with_rustc_bolt_ldflags(mut self) -> Self {
+ self.cmd = self.cmd.arg("--enable-bolt-settings");
+ self
+ }
+
+ pub fn with_bolt_profile(mut self, profile: BoltProfile) -> Self {
self.cmd = self.cmd.arg("--reproducible-artifact").arg(profile.0.as_str());
self
}
diff --git a/src/tools/opt-dist/src/main.rs b/src/tools/opt-dist/src/main.rs
index 978e2dfa4..f9ff1a0a4 100644
--- a/src/tools/opt-dist/src/main.rs
+++ b/src/tools/opt-dist/src/main.rs
@@ -12,11 +12,15 @@ use crate::environment::{Environment, EnvironmentBuilder};
use crate::exec::{cmd, Bootstrap};
use crate::tests::run_tests;
use crate::timer::Timer;
-use crate::training::{gather_llvm_bolt_profiles, gather_llvm_profiles, gather_rustc_profiles};
+use crate::training::{
+ gather_bolt_profiles, gather_llvm_profiles, gather_rustc_profiles, llvm_benchmarks,
+ rustc_benchmarks,
+};
+use crate::utils::artifact_size::print_binary_sizes;
use crate::utils::io::{copy_directory, move_directory, reset_directory};
use crate::utils::{
- clear_llvm_files, format_env_variables, print_binary_sizes, print_free_disk_space,
- retry_action, with_log_group,
+ clear_llvm_files, format_env_variables, print_free_disk_space, retry_action, with_log_group,
+ write_timer_to_summary,
};
mod bolt;
@@ -211,7 +215,12 @@ fn execute_pipeline(
print_free_disk_space()?;
stage.section("Build PGO optimized rustc", |section| {
- Bootstrap::build(env).rustc_pgo_optimize(&profile).run(section)
+ let mut cmd = Bootstrap::build(env).rustc_pgo_optimize(&profile);
+ if env.use_bolt() {
+ cmd = cmd.with_rustc_bolt_ldflags();
+ }
+
+ cmd.run(section)
})?;
Ok(profile)
@@ -245,13 +254,13 @@ fn execute_pipeline(
Ok(profile)
})?;
- let llvm_bolt_profile = if env.use_bolt() {
+ let bolt_profiles = if env.use_bolt() {
// Stage 3: Build BOLT instrumented LLVM
// We build a PGO optimized LLVM in this step, then instrument it with BOLT and gather BOLT profiles.
// Note that we don't remove LLVM artifacts after this step, so that they are reused in the final dist build.
// BOLT instrumentation is performed "on-the-fly" when the LLVM library is copied to the sysroot of rustc,
// therefore the LLVM artifacts on disk are not "tainted" with BOLT instrumentation and they can be reused.
- timer.section("Stage 3 (LLVM BOLT)", |stage| {
+ timer.section("Stage 3 (BOLT)", |stage| {
stage.section("Build PGO optimized LLVM", |stage| {
Bootstrap::build(env)
.with_llvm_bolt_ldflags()
@@ -260,16 +269,17 @@ fn execute_pipeline(
.run(stage)
})?;
- // Find the path to the `libLLVM.so` file
- let llvm_lib = io::find_file_in_dir(
- &env.build_artifacts().join("stage2").join("lib"),
- "libLLVM",
- ".so",
- )?;
+ let libdir = env.build_artifacts().join("stage2").join("lib");
+ let llvm_lib = io::find_file_in_dir(&libdir, "libLLVM", ".so")?;
- // Instrument it and gather profiles
- let profile = with_bolt_instrumented(&llvm_lib, || {
- stage.section("Gather profiles", |_| gather_llvm_bolt_profiles(env))
+ log::info!("Optimizing {llvm_lib} with BOLT");
+
+ // FIXME(kobzol): try gather profiles together, at once for LLVM and rustc
+ // Instrument the libraries and gather profiles
+ let llvm_profile = with_bolt_instrumented(&llvm_lib, |llvm_profile_dir| {
+ stage.section("Gather profiles", |_| {
+ gather_bolt_profiles(env, "LLVM", llvm_benchmarks(env), llvm_profile_dir)
+ })
})?;
print_free_disk_space()?;
@@ -278,13 +288,29 @@ fn execute_pipeline(
// the final dist build. However, when BOLT optimizes an artifact, it does so *in-place*,
// therefore it will actually optimize all the hard links, which means that the final
// packaged `libLLVM.so` file *will* be BOLT optimized.
- bolt_optimize(&llvm_lib, &profile).context("Could not optimize LLVM with BOLT")?;
+ bolt_optimize(&llvm_lib, &llvm_profile).context("Could not optimize LLVM with BOLT")?;
+
+ let rustc_lib = io::find_file_in_dir(&libdir, "librustc_driver", ".so")?;
+
+ log::info!("Optimizing {rustc_lib} with BOLT");
+
+ // Instrument it and gather profiles
+ let rustc_profile = with_bolt_instrumented(&rustc_lib, |rustc_profile_dir| {
+ stage.section("Gather profiles", |_| {
+ gather_bolt_profiles(env, "rustc", rustc_benchmarks(env), rustc_profile_dir)
+ })
+ })?;
+ print_free_disk_space()?;
+
+ // Now optimize the library with BOLT.
+ bolt_optimize(&rustc_lib, &rustc_profile)
+ .context("Could not optimize rustc with BOLT")?;
// LLVM is not being cleared here, we want to use the BOLT-optimized LLVM
- Ok(Some(profile))
+ Ok(vec![llvm_profile, rustc_profile])
})?
} else {
- None
+ vec![]
};
let mut dist = Bootstrap::dist(env, &dist_args)
@@ -292,13 +318,13 @@ fn execute_pipeline(
.rustc_pgo_optimize(&rustc_pgo_profile)
.avoid_rustc_rebuild();
- if let Some(llvm_bolt_profile) = llvm_bolt_profile {
- dist = dist.with_bolt_profile(llvm_bolt_profile);
+ for bolt_profile in bolt_profiles {
+ dist = dist.with_bolt_profile(bolt_profile);
}
// Final stage: Assemble the dist artifacts
// The previous PGO optimized rustc build and PGO optimized LLVM builds should be reused.
- timer.section("Stage 4 (final build)", |stage| dist.run(stage))?;
+ timer.section("Stage 5 (final build)", |stage| dist.run(stage))?;
// After dist has finished, run a subset of the test suite on the optimized artifacts to discover
// possible regressions.
@@ -359,6 +385,10 @@ fn main() -> anyhow::Result<()> {
let result = execute_pipeline(&env, &mut timer, build_args);
log::info!("Timer results\n{}", timer.format_stats());
+ if let Ok(summary_path) = std::env::var("GITHUB_STEP_SUMMARY") {
+ write_timer_to_summary(&summary_path, &timer)?;
+ }
+
print_free_disk_space()?;
result.context("Optimized build pipeline has failed")?;
print_binary_sizes(&env)?;
@@ -378,8 +408,8 @@ fn download_rustc_perf(env: &Environment) -> anyhow::Result<()> {
// FIXME: add some mechanism for synchronization of this commit SHA with
// Linux (which builds rustc-perf in a Dockerfile)
- // rustc-perf version from 2023-05-30
- const PERF_COMMIT: &str = "8b2ac3042e1ff2c0074455a0a3618adef97156b1";
+ // rustc-perf version from 2023-10-22
+ const PERF_COMMIT: &str = "4f313add609f43e928e98132358e8426ed3969ae";
let url = format!("https://ci-mirrors.rust-lang.org/rustc/rustc-perf-{PERF_COMMIT}.zip");
let client = reqwest::blocking::Client::builder()
diff --git a/src/tools/opt-dist/src/tests.rs b/src/tools/opt-dist/src/tests.rs
index 31aabca09..8a8f98a5e 100644
--- a/src/tools/opt-dist/src/tests.rs
+++ b/src/tools/opt-dist/src/tests.rs
@@ -61,7 +61,7 @@ pub fn run_tests(env: &Environment) -> anyhow::Result<()> {
let config_content = format!(
r#"profile = "user"
-changelog-seen = 2
+change-id = 115898
[build]
rustc = "{rustc}"
@@ -84,6 +84,8 @@ llvm-config = "{llvm_config}"
env.python_binary(),
x_py.as_str(),
"test",
+ "--build",
+ env.host_triple(),
"--stage",
"0",
"tests/assembly",
diff --git a/src/tools/opt-dist/src/training.rs b/src/tools/opt-dist/src/training.rs
index 274f4cea0..46040e32a 100644
--- a/src/tools/opt-dist/src/training.rs
+++ b/src/tools/opt-dist/src/training.rs
@@ -27,8 +27,6 @@ const RUSTC_PGO_CRATES: &[&str] = &[
"bitmaps-3.1.0",
];
-const LLVM_BOLT_CRATES: &[&str] = LLVM_PGO_CRATES;
-
fn init_compiler_benchmarks(
env: &Environment,
profiles: &[&str],
@@ -113,6 +111,14 @@ fn log_profile_stats(
Ok(())
}
+pub fn llvm_benchmarks(env: &Environment) -> CmdBuilder {
+ init_compiler_benchmarks(env, &["Debug", "Opt"], &["Full"], LLVM_PGO_CRATES)
+}
+
+pub fn rustc_benchmarks(env: &Environment) -> CmdBuilder {
+ init_compiler_benchmarks(env, &["Check", "Debug", "Opt"], &["All"], RUSTC_PGO_CRATES)
+}
+
pub struct LlvmPGOProfile(pub Utf8PathBuf);
pub fn gather_llvm_profiles(
@@ -122,9 +128,7 @@ pub fn gather_llvm_profiles(
log::info!("Running benchmarks with PGO instrumented LLVM");
with_log_group("Running benchmarks", || {
- init_compiler_benchmarks(env, &["Debug", "Opt"], &["Full"], LLVM_PGO_CRATES)
- .run()
- .context("Cannot gather LLVM PGO profiles")
+ llvm_benchmarks(env).run().context("Cannot gather LLVM PGO profiles")
})?;
let merged_profile = env.artifact_dir().join("llvm-pgo.profdata");
@@ -157,7 +161,7 @@ pub fn gather_rustc_profiles(
// Here we're profiling the `rustc` frontend, so we also include `Check`.
// The benchmark set includes various stress tests that put the frontend under pressure.
with_log_group("Running benchmarks", || {
- init_compiler_benchmarks(env, &["Check", "Debug", "Opt"], &["All"], RUSTC_PGO_CRATES)
+ rustc_benchmarks(env)
.env("LLVM_PROFILE_FILE", profile_template.as_str())
.run()
.context("Cannot gather rustc PGO profiles")
@@ -176,23 +180,25 @@ pub fn gather_rustc_profiles(
Ok(RustcPGOProfile(merged_profile))
}
-pub struct LlvmBoltProfile(pub Utf8PathBuf);
+pub struct BoltProfile(pub Utf8PathBuf);
-pub fn gather_llvm_bolt_profiles(env: &Environment) -> anyhow::Result<LlvmBoltProfile> {
- log::info!("Running benchmarks with BOLT instrumented LLVM");
+pub fn gather_bolt_profiles(
+ env: &Environment,
+ name: &str,
+ benchmarks: CmdBuilder,
+ profile_prefix: &Utf8Path,
+) -> anyhow::Result<BoltProfile> {
+ log::info!("Running benchmarks with BOLT instrumented {name}");
with_log_group("Running benchmarks", || {
- init_compiler_benchmarks(env, &["Check", "Debug", "Opt"], &["Full"], LLVM_BOLT_CRATES)
- .run()
- .context("Cannot gather LLVM BOLT profiles")
+ benchmarks.run().with_context(|| "Cannot gather {name} BOLT profiles")
})?;
- let merged_profile = env.artifact_dir().join("llvm-bolt.profdata");
- let profile_root = Utf8PathBuf::from("/tmp/prof.fdata");
- log::info!("Merging LLVM BOLT profiles to {merged_profile}");
+ let merged_profile = env.artifact_dir().join(format!("{name}-bolt.profdata"));
+ log::info!("Merging {name} BOLT profiles from {profile_prefix} to {merged_profile}");
let profiles: Vec<_> =
- glob::glob(&format!("{profile_root}*"))?.collect::<Result<Vec<_>, _>>()?;
+ glob::glob(&format!("{profile_prefix}*"))?.collect::<Result<Vec<_>, _>>()?;
let mut merge_args = vec!["merge-fdata"];
merge_args.extend(profiles.iter().map(|p| p.to_str().unwrap()));
@@ -204,7 +210,7 @@ pub fn gather_llvm_bolt_profiles(env: &Environment) -> anyhow::Result<LlvmBoltPr
.context("Cannot merge BOLT profiles")
})?;
- log::info!("LLVM BOLT statistics");
+ log::info!("{name} BOLT statistics");
log::info!(
"{merged_profile}: {}",
humansize::format_size(std::fs::metadata(merged_profile.as_std_path())?.len(), BINARY)
@@ -216,8 +222,17 @@ pub fn gather_llvm_bolt_profiles(env: &Environment) -> anyhow::Result<LlvmBoltPr
.collect::<Result<Vec<_>, _>>()?
.into_iter()
.sum::<u64>();
- log::info!("{profile_root}: {}", humansize::format_size(size, BINARY));
+ log::info!("{profile_prefix}: {}", humansize::format_size(size, BINARY));
log::info!("Profile file count: {}", profiles.len());
- Ok(LlvmBoltProfile(merged_profile))
+ // Delete the gathered profiles
+ for profile in glob::glob(&format!("{profile_prefix}*"))?.into_iter() {
+ if let Ok(profile) = profile {
+ if let Err(error) = std::fs::remove_file(&profile) {
+ log::error!("Cannot delete BOLT profile {}: {error:?}", profile.display());
+ }
+ }
+ }
+
+ Ok(BoltProfile(merged_profile))
}
diff --git a/src/tools/opt-dist/src/utils/artifact_size.rs b/src/tools/opt-dist/src/utils/artifact_size.rs
new file mode 100644
index 000000000..4dc8952b6
--- /dev/null
+++ b/src/tools/opt-dist/src/utils/artifact_size.rs
@@ -0,0 +1,61 @@
+use std::io::Write;
+
+use tabled::builder::Builder;
+use tabled::settings::object::Columns;
+use tabled::settings::style::{BorderChar, Offset};
+use tabled::settings::{Modify, Style};
+
+use crate::environment::Environment;
+use crate::utils::io::get_files_from_dir;
+
+pub fn print_binary_sizes(env: &Environment) -> anyhow::Result<()> {
+ use humansize::format_size;
+ use humansize::BINARY;
+ use std::fmt::Write;
+
+ let root = env.build_artifacts().join("stage2");
+
+ let mut files = get_files_from_dir(&root.join("bin"), None)?;
+ files.extend(get_files_from_dir(&root.join("lib"), Some(".so"))?);
+ files.sort_unstable();
+
+ let items: Vec<_> = files
+ .into_iter()
+ .map(|file| {
+ let size = std::fs::metadata(file.as_std_path()).map(|m| m.len()).unwrap_or(0);
+ let size_formatted = format_size(size, BINARY);
+ let name = file.file_name().unwrap().to_string();
+ (name, size_formatted)
+ })
+ .collect();
+
+ // Write to log
+ let mut output = String::new();
+ for (name, size_formatted) in items.iter() {
+ let name = format!("{}:", name);
+ writeln!(output, "{name:<50}{size_formatted:>10}")?;
+ }
+ log::info!("Rustc artifact size\n{output}");
+
+ // Write to GitHub summary
+ if let Ok(summary_path) = std::env::var("GITHUB_STEP_SUMMARY") {
+ let mut builder = Builder::default();
+ for (name, size_formatted) in items {
+ builder.push_record(vec![name, size_formatted]);
+ }
+
+ builder.set_header(vec!["Artifact", "Size"]);
+ let mut table = builder.build();
+
+ let mut file = std::fs::File::options().append(true).create(true).open(summary_path)?;
+ writeln!(
+ file,
+ "# Artifact size\n{}\n",
+ table.with(Style::markdown()).with(
+ Modify::new(Columns::single(1)).with(BorderChar::horizontal(':', Offset::End(0))),
+ )
+ )?;
+ }
+
+ Ok(())
+}
diff --git a/src/tools/opt-dist/src/utils/mod.rs b/src/tools/opt-dist/src/utils/mod.rs
index 6fc96592a..ca1292dd5 100644
--- a/src/tools/opt-dist/src/utils/mod.rs
+++ b/src/tools/opt-dist/src/utils/mod.rs
@@ -1,10 +1,13 @@
-pub mod io;
+use sysinfo::{DiskExt, RefreshKind, System, SystemExt};
use crate::environment::Environment;
-use crate::utils::io::{delete_directory, get_files_from_dir};
-use humansize::{format_size, BINARY};
+use crate::timer::Timer;
+use crate::utils::io::delete_directory;
+use humansize::BINARY;
use std::time::Duration;
-use sysinfo::{DiskExt, RefreshKind, System, SystemExt};
+
+pub mod artifact_size;
+pub mod io;
pub fn format_env_variables() -> String {
let vars = std::env::vars().map(|(key, value)| format!("{key}={value}")).collect::<Vec<_>>();
@@ -26,28 +29,6 @@ pub fn print_free_disk_space() -> anyhow::Result<()> {
Ok(())
}
-pub fn print_binary_sizes(env: &Environment) -> anyhow::Result<()> {
- use std::fmt::Write;
-
- let root = env.build_artifacts().join("stage2");
-
- let mut files = get_files_from_dir(&root.join("bin"), None)?;
- files.extend(get_files_from_dir(&root.join("lib"), Some(".so"))?);
- files.sort_unstable();
-
- let mut output = String::new();
- for file in files {
- let size = std::fs::metadata(file.as_std_path())?.len();
- let size_formatted = format_size(size, BINARY);
- let name = format!("{}:", file.file_name().unwrap());
- writeln!(output, "{name:<50}{size_formatted:>10}")?;
- }
-
- log::info!("Rustc artifact size\n{output}");
-
- Ok(())
-}
-
pub fn clear_llvm_files(env: &Environment) -> anyhow::Result<()> {
// Bootstrap currently doesn't support rebuilding LLVM when PGO options
// change (or any other llvm-related options); so just clear out the relevant
@@ -58,6 +39,24 @@ pub fn clear_llvm_files(env: &Environment) -> anyhow::Result<()> {
Ok(())
}
+/// Write the formatted statistics of the timer to a Github Actions summary.
+pub fn write_timer_to_summary(path: &str, timer: &Timer) -> anyhow::Result<()> {
+ use std::io::Write;
+
+ let mut file = std::fs::File::options().append(true).create(true).open(path)?;
+ writeln!(
+ file,
+ r#"# Step durations
+
+```
+{}
+```
+"#,
+ timer.format_stats()
+ )?;
+ Ok(())
+}
+
/// Wraps all output produced within the `func` closure in a CI output group, if we're running in
/// CI.
pub fn with_log_group<F: FnOnce() -> R, R>(group: &str, func: F) -> R {