Skip to content

Commit 74d70ed

Browse files
authored
Merge pull request #1298 from Kobzol/profile-local-parallel
Add option to parallelize profiling across benchmarks
2 parents b37e7ba + e2a4f7b commit 74d70ed

File tree

5 files changed

+140
-12
lines changed

5 files changed

+140
-12
lines changed

Cargo.lock

Lines changed: 69 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

collector/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ snap = "1"
3232
filetime = "0.2.14"
3333
walkdir = "2"
3434
flate2 = { version = "1.0.22", features = ["rust_backend"] }
35+
rayon = "1.5.2"
3536

3637
[target.'cfg(windows)'.dependencies]
3738
miow = "0.3"

collector/README.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -425,6 +425,8 @@ The following options alter the behaviour of the `profile_local` subcommand.
425425
diff files will also be produced.
426426
- `--rustdoc <RUSTDOC>` as for `bench_local`.
427427
- `--scenarios <SCENARIOS>`: as for `bench_local`.
428+
- `--jobs <JOB-COUNT>`: execute `<JOB-COUNT>` benchmarks in parallel. This is only allowed for certain
429+
profilers whose results are not affected by system noise (e.g. `callgrind` or `eprintln`).
428430

429431
`RUST_LOG=debug` can be specified to enable verbose logging, which is useful
430432
for debugging `collector` itself.

collector/src/execute.rs

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -177,6 +177,25 @@ pub enum Profiler {
177177
LlvmIr,
178178
}
179179

180+
impl Profiler {
181+
/// Returns true if this profiler can be executed
182+
/// in parallel without distorting the profile results.
183+
pub fn supports_parallel_execution(&self) -> bool {
184+
matches!(
185+
self,
186+
Profiler::Cachegrind
187+
| Profiler::Callgrind
188+
| Profiler::Dhat
189+
| Profiler::DhatCopy
190+
| Profiler::Eprintln
191+
| Profiler::LlvmLines
192+
| Profiler::LlvmIr
193+
| Profiler::MonoItems
194+
| Profiler::DepGraph
195+
)
196+
}
197+
}
198+
180199
#[derive(Clone, Copy, Debug, PartialEq)]
181200
pub enum PerfTool {
182201
BenchTool(Bencher),

collector/src/main.rs

Lines changed: 49 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ use clap::Parser;
55
use collector::category::Category;
66
use database::{ArtifactId, Commit};
77
use log::debug;
8+
use rayon::iter::{IndexedParallelIterator, IntoParallelRefIterator, ParallelIterator};
89
use std::collections::HashMap;
910
use std::fs;
1011
use std::fs::File;
@@ -134,6 +135,10 @@ impl BenchmarkErrors {
134135
self.0 += 1;
135136
}
136137

138+
fn add(&mut self, count: usize) {
139+
self.0 += count;
140+
}
141+
137142
fn fail_if_nonzero(self) -> anyhow::Result<()> {
138143
if self.0 > 0 {
139144
anyhow::bail!("{} benchmarks failed", self.0)
@@ -684,18 +689,30 @@ fn profile(
684689
if let Profiler::SelfProfile = profiler {
685690
check_measureme_installed().unwrap();
686691
}
687-
for (i, benchmark) in benchmarks.iter().enumerate() {
688-
eprintln!("{}", n_normal_benchmarks_remaining(benchmarks.len() - i));
689-
let mut processor = ProfileProcessor::new(profiler, out_dir, id);
690-
let result = benchmark.measure(&mut processor, &profiles, &scenarios, compiler, Some(1));
691-
if let Err(ref s) = result {
692-
errors.incr();
693-
eprintln!(
694-
"collector error: Failed to profile '{}' with {:?}, recorded: {:?}",
695-
benchmark.name, profiler, s
696-
);
697-
}
698-
}
692+
693+
let error_count: usize = benchmarks
694+
.par_iter()
695+
.enumerate()
696+
.map(|(i, benchmark)| {
697+
let benchmark_id = format!("{} ({}/{})", benchmark.name, i + 1, benchmarks.len());
698+
eprintln!("Executing benchmark {benchmark_id}");
699+
let mut processor = ProfileProcessor::new(profiler, out_dir, id);
700+
let result =
701+
benchmark.measure(&mut processor, &profiles, &scenarios, compiler, Some(1));
702+
eprintln!("Finished benchmark {benchmark_id}");
703+
704+
if let Err(ref s) = result {
705+
eprintln!(
706+
"collector error: Failed to profile '{}' with {:?}, recorded: {:?}",
707+
benchmark.name, profiler, s
708+
);
709+
1
710+
} else {
711+
0
712+
}
713+
})
714+
.sum();
715+
errors.add(error_count);
699716
}
700717

701718
fn main() {
@@ -859,6 +876,11 @@ enum Commands {
859876
// toolchain name, and `PathBuf` doesn't work well for the latter.
860877
#[clap(long)]
861878
rustc2: Option<String>,
879+
880+
/// How many benchmarks should be profiled in parallel.
881+
/// This flag is only supported for certain profilers
882+
#[clap(long, short = 'j', default_value = "1")]
883+
jobs: u64,
862884
},
863885

864886
/// Installs the next commit for perf.rust-lang.org
@@ -1089,7 +1111,16 @@ fn main_result() -> anyhow::Result<i32> {
10891111
local,
10901112
out_dir,
10911113
rustc2,
1114+
jobs,
10921115
} => {
1116+
let jobs = jobs.max(1);
1117+
if jobs > 1 && !profiler.supports_parallel_execution() {
1118+
anyhow::bail!(
1119+
"Profiler {:?} does not support parallel execution.",
1120+
profiler
1121+
);
1122+
}
1123+
10931124
let profiles = Profile::expand_all(&local.profiles);
10941125
let scenarios = Scenario::expand_all(&local.scenarios);
10951126

@@ -1102,6 +1133,12 @@ fn main_result() -> anyhow::Result<i32> {
11021133

11031134
let mut errors = BenchmarkErrors::new();
11041135

1136+
eprintln!("Running with {jobs} job(s)");
1137+
rayon::ThreadPoolBuilder::new()
1138+
.num_threads(jobs as usize)
1139+
.build_global()
1140+
.unwrap();
1141+
11051142
let mut get_toolchain_and_profile =
11061143
|rustc: &str, suffix: &str| -> anyhow::Result<String> {
11071144
let (rustc, rustdoc, cargo, id) = get_local_toolchain(

0 commit comments

Comments
 (0)