Skip to content

Commit 6c589ef

Browse files
authored
Merge pull request #19582 from Kobzol/ci-pgo
Distribute x64 and aarch64 Linux builds with PGO optimizations
2 parents dc70a78 + 0052f6a commit 6c589ef

File tree

3 files changed

+104
-9
lines changed

3 files changed

+104
-9
lines changed

.github/workflows/release.yaml

+6-3
Original file line numberDiff line numberDiff line change
@@ -38,10 +38,12 @@ jobs:
3838
target: x86_64-unknown-linux-gnu
3939
zig_target: x86_64-unknown-linux-gnu.2.28
4040
code-target: linux-x64
41+
pgo: true
4142
- os: ubuntu-latest
4243
target: aarch64-unknown-linux-gnu
4344
zig_target: aarch64-unknown-linux-gnu.2.28
4445
code-target: linux-arm64
46+
pgo: true
4547
- os: ubuntu-latest
4648
target: arm-unknown-linux-gnueabihf
4749
zig_target: arm-unknown-linux-gnueabihf.2.28
@@ -74,7 +76,8 @@ jobs:
7476
- name: Install Rust toolchain
7577
run: |
7678
rustup update --no-self-update stable
77-
rustup component add rust-src
79+
# llvm-tools contain the llvm-profdata tool which is needed for PGO
80+
rustup component add rust-src ${{ matrix.pgo && 'llvm-tools' || '' }}
7881
rustup target add ${{ matrix.target }}
7982
8083
- name: Install Zig toolchain
@@ -87,11 +90,11 @@ jobs:
8790
8891
- name: Dist (plain)
8992
if: ${{ !matrix.zig_target }}
90-
run: cargo xtask dist --client-patch-version ${{ github.run_number }}
93+
run: cargo xtask dist --client-patch-version ${{ github.run_number }} ${{ matrix.pgo && '--pgo' || ''}}
9194

9295
- name: Dist (using zigbuild)
9396
if: ${{ matrix.zig_target }}
94-
run: RA_TARGET=${{ matrix.zig_target}} cargo xtask dist --client-patch-version ${{ github.run_number }} --zig
97+
run: RA_TARGET=${{ matrix.zig_target}} cargo xtask dist --client-patch-version ${{ github.run_number }} --zig ${{ matrix.pgo && '--pgo' || ''}}
9598

9699
- run: npm ci
97100
working-directory: editors/code

xtask/src/dist.rs

+95-6
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,15 @@
1+
use anyhow::Context;
2+
use flate2::{Compression, write::GzEncoder};
3+
use std::env::consts::EXE_EXTENSION;
4+
use std::ffi::OsStr;
15
use std::{
26
env,
37
fs::File,
48
io::{self, BufWriter},
59
path::{Path, PathBuf},
610
};
7-
8-
use flate2::{Compression, write::GzEncoder};
911
use time::OffsetDateTime;
10-
use xshell::{Shell, cmd};
12+
use xshell::{Cmd, Shell, cmd};
1113
use zip::{DateTime, ZipWriter, write::SimpleFileOptions};
1214

1315
use crate::{
@@ -38,11 +40,18 @@ impl flags::Dist {
3840
// A hack to make VS Code prefer nightly over stable.
3941
format!("{VERSION_NIGHTLY}.{patch_version}")
4042
};
41-
dist_server(sh, &format!("{version}-standalone"), &target, allocator, self.zig)?;
43+
dist_server(
44+
sh,
45+
&format!("{version}-standalone"),
46+
&target,
47+
allocator,
48+
self.zig,
49+
self.pgo,
50+
)?;
4251
let release_tag = if stable { date_iso(sh)? } else { "nightly".to_owned() };
4352
dist_client(sh, &version, &release_tag, &target)?;
4453
} else {
45-
dist_server(sh, "0.0.0-standalone", &target, allocator, self.zig)?;
54+
dist_server(sh, "0.0.0-standalone", &target, allocator, self.zig, self.pgo)?;
4655
}
4756
Ok(())
4857
}
@@ -84,6 +93,7 @@ fn dist_server(
8493
target: &Target,
8594
allocator: Malloc,
8695
zig: bool,
96+
pgo: bool,
8797
) -> anyhow::Result<()> {
8898
let _e = sh.push_env("CFG_RELEASE", release);
8999
let _e = sh.push_env("CARGO_PROFILE_RELEASE_LTO", "thin");
@@ -100,7 +110,22 @@ fn dist_server(
100110
};
101111
let features = allocator.to_features();
102112
let command = if linux_target && zig { "zigbuild" } else { "build" };
103-
cmd!(sh, "cargo {command} --manifest-path ./crates/rust-analyzer/Cargo.toml --bin rust-analyzer --target {target_name} {features...} --release").run()?;
113+
114+
let pgo_profile = if pgo {
115+
Some(gather_pgo_profile(
116+
sh,
117+
build_command(sh, command, &target_name, features),
118+
&target_name,
119+
)?)
120+
} else {
121+
None
122+
};
123+
124+
let mut cmd = build_command(sh, command, &target_name, features);
125+
if let Some(profile) = pgo_profile {
126+
cmd = cmd.env("RUSTFLAGS", format!("-Cprofile-use={}", profile.to_str().unwrap()));
127+
}
128+
cmd.run().context("cannot build Rust Analyzer")?;
104129

105130
let dst = Path::new("dist").join(&target.artifact_name);
106131
if target_name.contains("-windows-") {
@@ -112,6 +137,70 @@ fn dist_server(
112137
Ok(())
113138
}
114139

140+
fn build_command<'a>(
141+
sh: &'a Shell,
142+
command: &str,
143+
target_name: &str,
144+
features: &[&str],
145+
) -> Cmd<'a> {
146+
cmd!(
147+
sh,
148+
"cargo {command} --manifest-path ./crates/rust-analyzer/Cargo.toml --bin rust-analyzer --target {target_name} {features...} --release"
149+
)
150+
}
151+
152+
/// Decorates `ra_build_cmd` to add PGO instrumentation, and then runs the PGO instrumented
153+
/// Rust Analyzer on itself to gather a PGO profile.
154+
fn gather_pgo_profile<'a>(
155+
sh: &'a Shell,
156+
ra_build_cmd: Cmd<'a>,
157+
target: &str,
158+
) -> anyhow::Result<PathBuf> {
159+
let pgo_dir = std::path::absolute("ra-pgo-profiles")?;
160+
// Clear out any stale profiles
161+
if pgo_dir.is_dir() {
162+
std::fs::remove_dir_all(&pgo_dir)?;
163+
}
164+
std::fs::create_dir_all(&pgo_dir)?;
165+
166+
// Figure out a path to `llvm-profdata`
167+
let target_libdir = cmd!(sh, "rustc --print=target-libdir")
168+
.read()
169+
.context("cannot resolve target-libdir from rustc")?;
170+
let target_bindir = PathBuf::from(target_libdir).parent().unwrap().join("bin");
171+
let llvm_profdata = target_bindir.join(format!("llvm-profdata{}", EXE_EXTENSION));
172+
173+
// Build RA with PGO instrumentation
174+
let cmd_gather =
175+
ra_build_cmd.env("RUSTFLAGS", format!("-Cprofile-generate={}", pgo_dir.to_str().unwrap()));
176+
cmd_gather.run().context("cannot build rust-analyzer with PGO instrumentation")?;
177+
178+
// Run RA on itself to gather profiles
179+
let train_crate = ".";
180+
cmd!(
181+
sh,
182+
"target/{target}/release/rust-analyzer analysis-stats {train_crate} --run-all-ide-things"
183+
)
184+
.run()
185+
.context("cannot generate PGO profiles")?;
186+
187+
// Merge profiles into a single file
188+
let merged_profile = pgo_dir.join("merged.profdata");
189+
let profile_files = std::fs::read_dir(pgo_dir)?.filter_map(|entry| {
190+
let entry = entry.ok()?;
191+
if entry.path().extension() == Some(OsStr::new("profraw")) {
192+
Some(entry.path().to_str().unwrap().to_owned())
193+
} else {
194+
None
195+
}
196+
});
197+
cmd!(sh, "{llvm_profdata} merge {profile_files...} -o {merged_profile}").run().context(
198+
"cannot merge PGO profiles. Do you have the rustup `llvm-tools` component installed?",
199+
)?;
200+
201+
Ok(merged_profile)
202+
}
203+
115204
fn gzip(src_path: &Path, dest_path: &Path) -> anyhow::Result<()> {
116205
let mut encoder = GzEncoder::new(File::create(dest_path)?, Compression::best());
117206
let mut input = io::BufReader::new(File::open(src_path)?);

xtask/src/flags.rs

+3
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,8 @@ xflags::xflags! {
5959
optional --client-patch-version version: String
6060
/// Use cargo-zigbuild
6161
optional --zig
62+
/// Apply PGO optimizations
63+
optional --pgo
6264
}
6365
/// Read a changelog AsciiDoc file and update the GitHub Releases entry in Markdown.
6466
cmd publish-release-notes {
@@ -147,6 +149,7 @@ pub struct Dist {
147149
pub jemalloc: bool,
148150
pub client_patch_version: Option<String>,
149151
pub zig: bool,
152+
pub pgo: bool,
150153
}
151154

152155
#[derive(Debug)]

0 commit comments

Comments
 (0)