Skip to content

Commit 1ac6fb5

Browse files
authored
Merge pull request #1762 from Kobzol/codegen-backend-clif
Add support for benchmarking Cranelift codegen backend
2 parents 9bc9940 + 32fb790 commit 1ac6fb5

File tree

11 files changed

+115
-80
lines changed

11 files changed

+115
-80
lines changed

collector/src/bin/collector.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -667,6 +667,7 @@ fn main_result() -> anyhow::Result<i32> {
667667
let get_suite = |rustc: &str, id: &str| {
668668
let toolchain = get_local_toolchain(
669669
&[Profile::Opt],
670+
&[CodegenBackend::Llvm],
670671
rustc,
671672
ToolchainConfig::default(),
672673
id,
@@ -723,6 +724,7 @@ fn main_result() -> anyhow::Result<i32> {
723724
let get_toolchain = |rustc: &str, id: &str| {
724725
let toolchain = get_local_toolchain(
725726
&[Profile::Opt],
727+
&[CodegenBackend::Llvm],
726728
rustc,
727729
ToolchainConfig::default(),
728730
id,
@@ -761,6 +763,7 @@ fn main_result() -> anyhow::Result<i32> {
761763

762764
let toolchain = get_local_toolchain(
763765
&profiles,
766+
&backends,
764767
&local.rustc,
765768
*ToolchainConfig::default()
766769
.rustdoc(opts.rustdoc.as_deref())
@@ -960,6 +963,7 @@ fn main_result() -> anyhow::Result<i32> {
960963
|rustc: &str, suffix: &str| -> anyhow::Result<String> {
961964
let toolchain = get_local_toolchain(
962965
profiles,
966+
&[CodegenBackend::Llvm],
963967
rustc,
964968
*ToolchainConfig::default()
965969
.rustdoc(opts.rustdoc.as_deref())
@@ -1085,6 +1089,7 @@ fn get_local_toolchain_for_runtime_benchmarks(
10851089
) -> anyhow::Result<Toolchain> {
10861090
get_local_toolchain(
10871091
&[Profile::Opt],
1092+
&[CodegenBackend::Llvm],
10881093
&local.rustc,
10891094
*ToolchainConfig::default()
10901095
.cargo(local.cargo.as_deref())

collector/src/compile/benchmark/codegen_backend.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,11 @@
22
#[value(rename_all = "PascalCase")]
33
pub enum CodegenBackend {
44
Llvm,
5+
Cranelift,
56
}
67

78
impl CodegenBackend {
89
pub fn all() -> Vec<CodegenBackend> {
9-
vec![CodegenBackend::Llvm]
10+
vec![CodegenBackend::Llvm, CodegenBackend::Cranelift]
1011
}
1112
}

collector/src/compile/benchmark/mod.rs

Lines changed: 73 additions & 77 deletions
Original file line numberDiff line numberDiff line change
@@ -263,10 +263,12 @@ impl Benchmark {
263263
}
264264

265265
eprintln!("Preparing {}", self.name);
266-
let profile_dirs = profiles
267-
.iter()
268-
.map(|profile| Ok((*profile, self.make_temp_dir(&self.path)?)))
269-
.collect::<anyhow::Result<Vec<_>>>()?;
266+
let mut target_dirs: Vec<((CodegenBackend, Profile), TempDir)> = vec![];
267+
for backend in backends {
268+
for profile in &profiles {
269+
target_dirs.push(((*backend, *profile), self.make_temp_dir(&self.path)?));
270+
}
271+
}
270272

271273
// In parallel (but with a limit to the number of CPUs), prepare all
272274
// profiles. This is done in parallel vs. sequentially because:
@@ -290,22 +292,22 @@ impl Benchmark {
290292
// to do this in Cargo today. We would also ideally build in the same
291293
// target directory, but that's also not possible, as Cargo takes a
292294
// target-directory global lock during compilation.
295+
//
296+
// To avoid potential problems with recompilations, artifacts compiled by
297+
// different codegen backends are stored in separate directories.
293298
let preparation_start = std::time::Instant::now();
294299
std::thread::scope::<_, anyhow::Result<()>>(|s| {
295300
let server = jobserver::Client::new(num_cpus::get()).context("jobserver::new")?;
296-
let mut threads = Vec::with_capacity(profile_dirs.len());
297-
for (profile, prep_dir) in &profile_dirs {
301+
let mut threads = Vec::with_capacity(target_dirs.len());
302+
for ((backend, profile), prep_dir) in &target_dirs {
298303
let server = server.clone();
299304
let thread = s.spawn::<_, anyhow::Result<()>>(move || {
300305
wait_for_future(async move {
301-
// Prepare all backend artifacts into the same target directory
302-
for backend in backends {
303-
let server = server.clone();
304-
self.mk_cargo_process(toolchain, prep_dir.path(), *profile, *backend)
305-
.jobserver(server)
306-
.run_rustc(false)
307-
.await?;
308-
}
306+
let server = server.clone();
307+
self.mk_cargo_process(toolchain, prep_dir.path(), *profile, *backend)
308+
.jobserver(server)
309+
.run_rustc(false)
310+
.await?;
309311
Ok::<(), anyhow::Error>(())
310312
})?;
311313
Ok(())
@@ -331,88 +333,82 @@ impl Benchmark {
331333
);
332334

333335
let benchmark_start = std::time::Instant::now();
334-
for &backend in backends {
335-
for (profile, prep_dir) in &profile_dirs {
336-
let profile = *profile;
337-
eprintln!(
338-
"Running {}: {:?} + {:?} + {:?}",
339-
self.name, profile, scenarios, backend
340-
);
336+
for ((backend, profile), prep_dir) in &target_dirs {
337+
let backend = *backend;
338+
let profile = *profile;
339+
eprintln!(
340+
"Running {}: {:?} + {:?} + {:?}",
341+
self.name, profile, scenarios, backend
342+
);
343+
344+
// We want at least two runs for all benchmarks (since we run
345+
// self-profile separately).
346+
processor.start_first_collection();
347+
for i in 0..std::cmp::max(iterations, 2) {
348+
if i == 1 {
349+
let different = processor.finished_first_collection();
350+
if iterations == 1 && !different {
351+
// Don't run twice if this processor doesn't need it and
352+
// we've only been asked to run once.
353+
break;
354+
}
355+
}
356+
log::debug!("Benchmark iteration {}/{}", i + 1, iterations);
357+
// Don't delete the directory on error.
358+
let timing_dir = ManuallyDrop::new(self.make_temp_dir(prep_dir.path())?);
359+
let cwd = timing_dir.path();
360+
361+
// A full non-incremental build.
362+
if scenarios.contains(&Scenario::Full) {
363+
self.mk_cargo_process(toolchain, cwd, profile, backend)
364+
.processor(processor, Scenario::Full, "Full", None)
365+
.run_rustc(true)
366+
.await?;
367+
}
341368

342-
// We want at least two runs for all benchmarks (since we run
343-
// self-profile separately).
344-
processor.start_first_collection();
345-
for i in 0..std::cmp::max(iterations, 2) {
346-
if i == 1 {
347-
let different = processor.finished_first_collection();
348-
if iterations == 1 && !different {
349-
// Don't run twice if this processor doesn't need it and
350-
// we've only been asked to run once.
351-
break;
352-
}
369+
// Rustdoc does not support incremental compilation
370+
if profile != Profile::Doc {
371+
// An incremental from scratch (slowest incremental case).
372+
// This is required for any subsequent incremental builds.
373+
if scenarios.iter().any(|s| s.is_incr()) {
374+
self.mk_cargo_process(toolchain, cwd, profile, backend)
375+
.incremental(true)
376+
.processor(processor, Scenario::IncrFull, "IncrFull", None)
377+
.run_rustc(true)
378+
.await?;
353379
}
354-
log::debug!("Benchmark iteration {}/{}", i + 1, iterations);
355-
// Don't delete the directory on error.
356-
let timing_dir = ManuallyDrop::new(self.make_temp_dir(prep_dir.path())?);
357-
let cwd = timing_dir.path();
358380

359-
// A full non-incremental build.
360-
if scenarios.contains(&Scenario::Full) {
381+
// An incremental build with no changes (fastest incremental case).
382+
if scenarios.contains(&Scenario::IncrUnchanged) {
361383
self.mk_cargo_process(toolchain, cwd, profile, backend)
362-
.processor(processor, Scenario::Full, "Full", None)
384+
.incremental(true)
385+
.processor(processor, Scenario::IncrUnchanged, "IncrUnchanged", None)
363386
.run_rustc(true)
364387
.await?;
365388
}
366389

367-
// Rustdoc does not support incremental compilation
368-
if profile != Profile::Doc {
369-
// An incremental from scratch (slowest incremental case).
370-
// This is required for any subsequent incremental builds.
371-
if scenarios.iter().any(|s| s.is_incr()) {
372-
self.mk_cargo_process(toolchain, cwd, profile, backend)
373-
.incremental(true)
374-
.processor(processor, Scenario::IncrFull, "IncrFull", None)
375-
.run_rustc(true)
376-
.await?;
377-
}
390+
if scenarios.contains(&Scenario::IncrPatched) {
391+
for (i, patch) in self.patches.iter().enumerate() {
392+
log::debug!("applying patch {}", patch.name);
393+
patch.apply(cwd).map_err(|s| anyhow::anyhow!("{}", s))?;
378394

379-
// An incremental build with no changes (fastest incremental case).
380-
if scenarios.contains(&Scenario::IncrUnchanged) {
395+
// An incremental build with some changes (realistic
396+
// incremental case).
397+
let scenario_str = format!("IncrPatched{}", i);
381398
self.mk_cargo_process(toolchain, cwd, profile, backend)
382399
.incremental(true)
383400
.processor(
384401
processor,
385-
Scenario::IncrUnchanged,
386-
"IncrUnchanged",
387-
None,
402+
Scenario::IncrPatched,
403+
&scenario_str,
404+
Some(patch),
388405
)
389406
.run_rustc(true)
390407
.await?;
391408
}
392-
393-
if scenarios.contains(&Scenario::IncrPatched) {
394-
for (i, patch) in self.patches.iter().enumerate() {
395-
log::debug!("applying patch {}", patch.name);
396-
patch.apply(cwd).map_err(|s| anyhow::anyhow!("{}", s))?;
397-
398-
// An incremental build with some changes (realistic
399-
// incremental case).
400-
let scenario_str = format!("IncrPatched{}", i);
401-
self.mk_cargo_process(toolchain, cwd, profile, backend)
402-
.incremental(true)
403-
.processor(
404-
processor,
405-
Scenario::IncrPatched,
406-
&scenario_str,
407-
Some(patch),
408-
)
409-
.run_rustc(true)
410-
.await?;
411-
}
412-
}
413409
}
414-
drop(ManuallyDrop::into_inner(timing_dir));
415410
}
411+
drop(ManuallyDrop::into_inner(timing_dir));
416412
}
417413
}
418414
log::trace!(

collector/src/compile/execute/bencher.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,7 @@ impl<'a> BenchProcessor<'a> {
9494

9595
let backend = match backend {
9696
CodegenBackend::Llvm => database::CodegenBackend::Llvm,
97+
CodegenBackend::Cranelift => database::CodegenBackend::Cranelift,
9798
};
9899

99100
if let Some(files) = stats.2 {

collector/src/compile/execute/mod.rs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -267,6 +267,14 @@ impl<'a> CargoProcess<'a> {
267267
cmd.arg("-Ztimings");
268268
}
269269
cmd.arg("--");
270+
271+
match self.backend {
272+
CodegenBackend::Llvm => {}
273+
CodegenBackend::Cranelift => {
274+
cmd.arg("-Zcodegen-backend=cranelift");
275+
}
276+
}
277+
270278
// --wrap-rustc-with is not a valid rustc flag. But rustc-fake
271279
// recognizes it, strips it (and its argument) out, and uses it as an
272280
// indicator that the rustc invocation should be profiled. This works

collector/src/toolchain.rs

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
use crate::compile::benchmark::codegen_backend::CodegenBackend;
12
use crate::compile::benchmark::profile::Profile;
23
use anyhow::{anyhow, Context};
34
use log::debug;
@@ -330,6 +331,7 @@ impl<'a> ToolchainConfig<'a> {
330331
/// for the nightly Cargo via `rustup`.
331332
pub fn get_local_toolchain(
332333
profiles: &[Profile],
334+
codegen_backends: &[CodegenBackend],
333335
rustc: &str,
334336
toolchain_config: ToolchainConfig<'_>,
335337
id_suffix: &str,
@@ -357,8 +359,18 @@ pub fn get_local_toolchain(
357359
anyhow::bail!("rustup-toolchain-install-master is not installed but must be");
358360
}
359361

360-
if !Command::new("rustup-toolchain-install-master")
361-
.arg(toolchain)
362+
let mut additional_components = vec![];
363+
if codegen_backends.contains(&CodegenBackend::Cranelift) {
364+
additional_components.push("rustc-codegen-cranelift");
365+
}
366+
367+
let mut cmd = Command::new("rustup-toolchain-install-master");
368+
cmd.arg(toolchain);
369+
for component in additional_components {
370+
cmd.arg("-c").arg(component);
371+
}
372+
373+
if !cmd
362374
.status()
363375
.context("failed to run `rustup-toolchain-install-master`")?
364376
.success()

database/src/lib.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -367,12 +367,15 @@ impl PartialOrd for Scenario {
367367
pub enum CodegenBackend {
368368
/// The default LLVM backend
369369
Llvm,
370+
/// Cranelift codegen backend
371+
Cranelift,
370372
}
371373

372374
impl CodegenBackend {
373375
pub fn as_str(self) -> &'static str {
374376
match self {
375377
CodegenBackend::Llvm => "llvm",
378+
CodegenBackend::Cranelift => "cranelift",
376379
}
377380
}
378381
}
@@ -382,6 +385,7 @@ impl FromStr for CodegenBackend {
382385
fn from_str(s: &str) -> Result<Self, Self::Err> {
383386
Ok(match s.to_ascii_lowercase().as_str() {
384387
"llvm" => CodegenBackend::Llvm,
388+
"cranelift" => CodegenBackend::Cranelift,
385389
_ => return Err(format!("{} is not a codegen backend", s)),
386390
})
387391
}

site/frontend/src/pages/compare/compile/common.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@ export const defaultCompileFilter: CompileBenchmarkFilter = {
5151
};
5252

5353
export type Profile = "check" | "debug" | "opt" | "doc";
54+
export type CodegenBackend = "llvm" | "cranelift";
5455
export type Category = "primary" | "secondary";
5556

5657
export type CompileBenchmarkMap = Dict<CompileBenchmarkMetadata>;
@@ -74,13 +75,15 @@ export interface CompileBenchmarkComparison {
7475
benchmark: string;
7576
profile: Profile;
7677
scenario: string;
78+
backend: CodegenBackend;
7779
comparison: StatComparison;
7880
}
7981

8082
export interface CompileTestCase {
8183
benchmark: string;
8284
profile: Profile;
8385
scenario: string;
86+
backend: CodegenBackend;
8487
category: Category;
8588
}
8689

@@ -156,6 +159,7 @@ export function computeCompileComparisonsWithNonRelevant(
156159
benchmark: c.benchmark,
157160
profile: c.profile,
158161
scenario: c.scenario,
162+
backend: c.backend,
159163
category: (benchmarkMap[c.benchmark] || {}).category || "secondary",
160164
};
161165
return calculateComparison(c.comparison, testCase);

site/frontend/src/pages/compare/compile/table/comparisons-table.vue

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@ const {toggleExpanded, isExpanded} = useExpandedStore();
5555
<th>Benchmark</th>
5656
<th>Profile</th>
5757
<th>Scenario</th>
58+
<th>Backend</th>
5859
<th>% Change</th>
5960
<th class="narrow">
6061
Significance Threshold
@@ -98,6 +99,7 @@ const {toggleExpanded, isExpanded} = useExpandedStore();
9899
{{ comparison.testCase.profile }}
99100
</td>
100101
<td>{{ comparison.testCase.scenario }}</td>
102+
<td>{{ comparison.testCase.backend }}</td>
101103
<td>
102104
<div class="numeric-aligned">
103105
<span v-bind:class="percentClass(comparison.percent)">

site/src/api.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -271,6 +271,7 @@ pub mod comparison {
271271
pub benchmark: String,
272272
pub profile: String,
273273
pub scenario: String,
274+
pub backend: String,
274275
pub comparison: StatComparison,
275276
}
276277

site/src/comparison.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -152,6 +152,7 @@ pub async fn handle_compare(
152152
benchmark: comparison.benchmark.to_string(),
153153
profile: comparison.profile.to_string(),
154154
scenario: comparison.scenario.to_string(),
155+
backend: comparison.backend.to_string(),
155156
comparison: comparison.comparison.into(),
156157
})
157158
.collect();

0 commit comments

Comments
 (0)