From 7e1fb23ca7a97d4da87158530e8e14d5c0938c25 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Ber=C3=A1nek?= Date: Wed, 12 Jul 2023 17:11:19 +0200 Subject: [PATCH 1/3] Use BOLT to optimize `librustc_driver` --- src/bootstrap/compile.rs | 5 ++++ src/tools/opt-dist/src/bolt.rs | 4 +-- src/tools/opt-dist/src/exec.rs | 4 +-- src/tools/opt-dist/src/main.rs | 42 ++++++++++++++++++++++++-- src/tools/opt-dist/src/training.rs | 47 ++++++++++++++++++++---------- 5 files changed, 79 insertions(+), 23 deletions(-) diff --git a/src/bootstrap/compile.rs b/src/bootstrap/compile.rs index 4f19ffa83dbe8..7eac0d49d9da6 100644 --- a/src/bootstrap/compile.rs +++ b/src/bootstrap/compile.rs @@ -887,6 +887,11 @@ impl Step for Rustc { cargo.arg("-p").arg(krate); } + if compiler.stage == 1 { + // Relocations are required for BOLT to work. + cargo.rustflag(&format!("-Clink-args=-Wl,-q")); + } + let _guard = builder.msg_sysroot_tool( Kind::Build, compiler.stage, diff --git a/src/tools/opt-dist/src/bolt.rs b/src/tools/opt-dist/src/bolt.rs index cf9f4fabcec12..ecd28935f9725 100644 --- a/src/tools/opt-dist/src/bolt.rs +++ b/src/tools/opt-dist/src/bolt.rs @@ -1,7 +1,7 @@ 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; @@ -40,7 +40,7 @@ pub fn with_bolt_instrumented anyhow::Result, R>( } /// 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 04e0184528a5f..f07bd5f9fce06 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,7 @@ impl Bootstrap { self } - pub fn with_bolt_profile(mut self, profile: LlvmBoltProfile) -> 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 978e2dfa4e864..baf5ae0995496 100644 --- a/src/tools/opt-dist/src/main.rs +++ b/src/tools/opt-dist/src/main.rs @@ -12,7 +12,7 @@ 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}; 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, @@ -267,9 +267,13 @@ fn execute_pipeline( ".so", )?; + log::info!("Optimizing {llvm_lib} with BOLT"); + // Instrument it and gather profiles let profile = with_bolt_instrumented(&llvm_lib, || { - stage.section("Gather profiles", |_| gather_llvm_bolt_profiles(env)) + stage.section("Gather profiles", |_| { + gather_bolt_profiles(env, "LLVM", llvm_benchmarks(env)) + }) })?; print_free_disk_space()?; @@ -287,6 +291,35 @@ fn execute_pipeline( None }; + let rustc_bolt_profile = if env.supports_bolt() { + // Stage 4: Build BOLT instrumented rustc + timer.section("Stage 4 (Rustc BOLT)", |stage| { + // Find the path to the `librustc_driver.so` file + let rustc_lib = io::find_file_in_dir( + &env.build_artifacts().join("stage2").join("lib"), + "librustc_driver", + ".so", + )?; + + log::info!("Optimizing {rustc_lib} with BOLT"); + + // Instrument it and gather profiles + let profile = with_bolt_instrumented(&rustc_lib, || { + stage.section("Gather profiles", |_| { + gather_bolt_profiles(env, "rustc", rustc_benchmarks(env)) + }) + })?; + print_free_disk_space()?; + + // Now optimize the library with BOLT. + bolt_optimize(&rustc_lib, &profile).context("Could not optimize rustc with BOLT")?; + + Ok(Some(profile)) + })? + } else { + None + }; + let mut dist = Bootstrap::dist(env, &dist_args) .llvm_pgo_optimize(&llvm_pgo_profile) .rustc_pgo_optimize(&rustc_pgo_profile) @@ -295,10 +328,13 @@ fn execute_pipeline( if let Some(llvm_bolt_profile) = llvm_bolt_profile { dist = dist.with_bolt_profile(llvm_bolt_profile); } + if let Some(rustc_bolt_profile) = rustc_bolt_profile { + dist = dist.with_bolt_profile(rustc_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. diff --git a/src/tools/opt-dist/src/training.rs b/src/tools/opt-dist/src/training.rs index 274f4cea0ab5f..3ebb969d1a957 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: &dyn Environment) -> CmdBuilder { + init_compiler_benchmarks(env, &["Debug", "Opt"], &["Full"], LLVM_PGO_CRATES) +} + +pub fn rustc_benchmarks(env: &dyn 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,20 +180,22 @@ 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 { - log::info!("Running benchmarks with BOLT instrumented LLVM"); +pub fn gather_bolt_profiles( + env: &Environment, + name: &str, + benchmarks: CmdBuilder, +) -> anyhow::Result { + 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 merged_profile = env.artifact_dir().join(format!("{name}-bolt.profdata")); let profile_root = Utf8PathBuf::from("/tmp/prof.fdata"); - log::info!("Merging LLVM BOLT profiles to {merged_profile}"); + log::info!("Merging {name} BOLT profiles to {merged_profile}"); let profiles: Vec<_> = glob::glob(&format!("{profile_root}*"))?.collect::, _>>()?; @@ -204,7 +210,7 @@ pub fn gather_llvm_bolt_profiles(env: &Environment) -> anyhow::Result anyhow::Result Date: Mon, 31 Jul 2023 19:56:35 +0200 Subject: [PATCH 2/3] Build GCC with support for BOLT --- src/ci/docker/host-x86_64/dist-x86_64-linux/build-gcc.sh | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/ci/docker/host-x86_64/dist-x86_64-linux/build-gcc.sh b/src/ci/docker/host-x86_64/dist-x86_64-linux/build-gcc.sh index 6da3f89220e23..1476f12684c01 100755 --- a/src/ci/docker/host-x86_64/dist-x86_64-linux/build-gcc.sh +++ b/src/ci/docker/host-x86_64/dist-x86_64-linux/build-gcc.sh @@ -30,7 +30,8 @@ cd ../gcc-build hide_output ../gcc-$GCC/configure \ --prefix=/rustroot \ --enable-languages=c,c++ \ - --disable-gnu-unique-object + --disable-gnu-unique-object \ + --enable-cxx-flags='-fno-reorder-blocks-and-partition' hide_output make -j$(nproc) hide_output make install ln -s gcc /rustroot/bin/cc From bcff0556c3b253f96e2fe9d438dfca4cbdc88828 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Ber=C3=A1nek?= Date: Sun, 24 Sep 2023 14:33:39 +0200 Subject: [PATCH 3/3] Specify directory for BOLT profiles --- src/tools/opt-dist/src/bolt.rs | 10 ++++- src/tools/opt-dist/src/main.rs | 70 +++++++++++++++--------------- src/tools/opt-dist/src/training.rs | 14 +++--- 3 files changed, 51 insertions(+), 43 deletions(-) diff --git a/src/tools/opt-dist/src/bolt.rs b/src/tools/opt-dist/src/bolt.rs index ecd28935f9725..f694c08f9b937 100644 --- a/src/tools/opt-dist/src/bolt.rs +++ b/src/tools/opt-dist/src/bolt.rs @@ -8,7 +8,7 @@ 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 anyhow::Result, R>( +pub fn with_bolt_instrumented anyhow::Result, R>( path: &Utf8Path, func: F, ) -> anyhow::Result { @@ -20,10 +20,16 @@ pub fn with_bolt_instrumented anyhow::Result, 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,7 +42,7 @@ pub fn with_bolt_instrumented anyhow::Result, 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`. diff --git a/src/tools/opt-dist/src/main.rs b/src/tools/opt-dist/src/main.rs index baf5ae0995496..ad1a65548f1fe 100644 --- a/src/tools/opt-dist/src/main.rs +++ b/src/tools/opt-dist/src/main.rs @@ -12,7 +12,9 @@ use crate::environment::{Environment, EnvironmentBuilder}; use crate::exec::{cmd, Bootstrap}; use crate::tests::run_tests; use crate::timer::Timer; -use crate::training::{gather_bolt_profiles, gather_llvm_profiles, gather_rustc_profiles}; +use crate::training::{ + gather_bolt_profiles, gather_llvm_profiles, gather_rustc_profiles, llvm_benchmarks, +}; 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, @@ -270,9 +272,9 @@ fn execute_pipeline( log::info!("Optimizing {llvm_lib} with BOLT"); // Instrument it and gather profiles - let profile = with_bolt_instrumented(&llvm_lib, || { + let profile = with_bolt_instrumented(&llvm_lib, |llvm_profile_dir| { stage.section("Gather profiles", |_| { - gather_bolt_profiles(env, "LLVM", llvm_benchmarks(env)) + gather_bolt_profiles(env, "LLVM", llvm_benchmarks(env), llvm_profile_dir) }) })?; print_free_disk_space()?; @@ -291,34 +293,34 @@ fn execute_pipeline( None }; - let rustc_bolt_profile = if env.supports_bolt() { - // Stage 4: Build BOLT instrumented rustc - timer.section("Stage 4 (Rustc BOLT)", |stage| { - // Find the path to the `librustc_driver.so` file - let rustc_lib = io::find_file_in_dir( - &env.build_artifacts().join("stage2").join("lib"), - "librustc_driver", - ".so", - )?; - - log::info!("Optimizing {rustc_lib} with BOLT"); - - // Instrument it and gather profiles - let profile = with_bolt_instrumented(&rustc_lib, || { - stage.section("Gather profiles", |_| { - gather_bolt_profiles(env, "rustc", rustc_benchmarks(env)) - }) - })?; - print_free_disk_space()?; - - // Now optimize the library with BOLT. - bolt_optimize(&rustc_lib, &profile).context("Could not optimize rustc with BOLT")?; - - Ok(Some(profile)) - })? - } else { - None - }; + // let rustc_bolt_profile = if env.use_bolt() { + // // Stage 4: Build BOLT instrumented rustc + // timer.section("Stage 4 (Rustc BOLT)", |stage| { + // // Find the path to the `librustc_driver.so` file + // let rustc_lib = io::find_file_in_dir( + // &env.build_artifacts().join("stage2").join("lib"), + // "librustc_driver", + // ".so", + // )?; + // + // log::info!("Optimizing {rustc_lib} with BOLT"); + // + // // Instrument it and gather profiles + // let profile = with_bolt_instrumented(&rustc_lib, || { + // stage.section("Gather profiles", |_| { + // gather_bolt_profiles(env, "rustc", rustc_benchmarks(env)) + // }) + // })?; + // print_free_disk_space()?; + // + // // Now optimize the library with BOLT. + // bolt_optimize(&rustc_lib, &profile).context("Could not optimize rustc with BOLT")?; + // + // Ok(Some(profile)) + // })? + // } else { + // None + // }; let mut dist = Bootstrap::dist(env, &dist_args) .llvm_pgo_optimize(&llvm_pgo_profile) @@ -328,9 +330,9 @@ fn execute_pipeline( if let Some(llvm_bolt_profile) = llvm_bolt_profile { dist = dist.with_bolt_profile(llvm_bolt_profile); } - if let Some(rustc_bolt_profile) = rustc_bolt_profile { - dist = dist.with_bolt_profile(rustc_bolt_profile); - } + // if let Some(rustc_bolt_profile) = rustc_bolt_profile { + // dist = dist.with_bolt_profile(rustc_bolt_profile); + // } // Final stage: Assemble the dist artifacts // The previous PGO optimized rustc build and PGO optimized LLVM builds should be reused. diff --git a/src/tools/opt-dist/src/training.rs b/src/tools/opt-dist/src/training.rs index 3ebb969d1a957..46040e32a0399 100644 --- a/src/tools/opt-dist/src/training.rs +++ b/src/tools/opt-dist/src/training.rs @@ -111,11 +111,11 @@ fn log_profile_stats( Ok(()) } -pub fn llvm_benchmarks(env: &dyn Environment) -> CmdBuilder { +pub fn llvm_benchmarks(env: &Environment) -> CmdBuilder { init_compiler_benchmarks(env, &["Debug", "Opt"], &["Full"], LLVM_PGO_CRATES) } -pub fn rustc_benchmarks(env: &dyn Environment) -> CmdBuilder { +pub fn rustc_benchmarks(env: &Environment) -> CmdBuilder { init_compiler_benchmarks(env, &["Check", "Debug", "Opt"], &["All"], RUSTC_PGO_CRATES) } @@ -186,6 +186,7 @@ pub fn gather_bolt_profiles( env: &Environment, name: &str, benchmarks: CmdBuilder, + profile_prefix: &Utf8Path, ) -> anyhow::Result { log::info!("Running benchmarks with BOLT instrumented {name}"); @@ -194,11 +195,10 @@ pub fn gather_bolt_profiles( })?; let merged_profile = env.artifact_dir().join(format!("{name}-bolt.profdata")); - let profile_root = Utf8PathBuf::from("/tmp/prof.fdata"); - log::info!("Merging {name} BOLT profiles to {merged_profile}"); + log::info!("Merging {name} BOLT profiles from {profile_prefix} to {merged_profile}"); let profiles: Vec<_> = - glob::glob(&format!("{profile_root}*"))?.collect::, _>>()?; + glob::glob(&format!("{profile_prefix}*"))?.collect::, _>>()?; let mut merge_args = vec!["merge-fdata"]; merge_args.extend(profiles.iter().map(|p| p.to_str().unwrap())); @@ -222,11 +222,11 @@ pub fn gather_bolt_profiles( .collect::, _>>()? .into_iter() .sum::(); - 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()); // Delete the gathered profiles - for profile in glob::glob(&format!("{profile_root}*"))?.into_iter() { + 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());