diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-06-07 05:48:48 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-06-07 05:48:48 +0000 |
commit | ef24de24a82fe681581cc130f342363c47c0969a (patch) | |
tree | 0d494f7e1a38b95c92426f58fe6eaa877303a86c /src/tools/opt-dist | |
parent | Releasing progress-linux version 1.74.1+dfsg1-1~progress7.99u1. (diff) | |
download | rustc-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.toml | 1 | ||||
-rw-r--r-- | src/tools/opt-dist/src/bolt.rs | 14 | ||||
-rw-r--r-- | src/tools/opt-dist/src/exec.rs | 9 | ||||
-rw-r--r-- | src/tools/opt-dist/src/main.rs | 76 | ||||
-rw-r--r-- | src/tools/opt-dist/src/tests.rs | 4 | ||||
-rw-r--r-- | src/tools/opt-dist/src/training.rs | 53 | ||||
-rw-r--r-- | src/tools/opt-dist/src/utils/artifact_size.rs | 61 | ||||
-rw-r--r-- | src/tools/opt-dist/src/utils/mod.rs | 51 |
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 { |