Skip to content

Commit fb05da8

Browse files
committed
Add command for listing runtime benchmarks
1 parent a8fb050 commit fb05da8

File tree

4 files changed

+89
-17
lines changed

4 files changed

+89
-17
lines changed

collector/benchlib/src/benchmark.rs

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -59,12 +59,20 @@ impl BenchmarkSuite {
5959
Args::Benchmark(args) => {
6060
run_benchmark(args, self.benchmarks)?;
6161
}
62+
Args::ListBenchmarks => list_benchmarks(self.benchmarks)?,
6263
}
6364

6465
Ok(())
6566
}
6667
}
6768

69+
fn list_benchmarks(benchmarks: BenchmarkMap) -> anyhow::Result<()> {
70+
let benchmark_list: Vec<&str> = benchmarks.into_keys().collect();
71+
serde_json::to_writer(std::io::stdout(), &benchmark_list)?;
72+
73+
Ok(())
74+
}
75+
6876
fn run_benchmark(args: BenchmarkArgs, benchmarks: BenchmarkMap) -> anyhow::Result<()> {
6977
let mut items: Vec<(&'static str, BenchmarkWrapper)> = benchmarks
7078
.into_iter()
@@ -104,7 +112,7 @@ macro_rules! define_benchmark {
104112
pub use define_benchmark;
105113

106114
/// Tests if the name of the benchmark passes through the include and exclude filter flags.
107-
fn passes_filter(name: &str, exclude: Option<&str>, include: Option<&str>) -> bool {
115+
pub fn passes_filter(name: &str, exclude: Option<&str>, include: Option<&str>) -> bool {
108116
match (exclude, include) {
109117
(Some(exclude), Some(include)) => name.starts_with(include) && !name.starts_with(exclude),
110118
(None, Some(include)) => name.starts_with(include),

collector/benchlib/src/cli.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@ use clap::{FromArgMatches, IntoApp};
44
pub enum Args {
55
/// Benchmark all benchmarks in this benchmark suite and print the results as JSON.
66
Benchmark(BenchmarkArgs),
7+
/// List benchmarks that are defined in the current suite as a JSON array.
8+
ListBenchmarks,
79
}
810

911
#[derive(clap::Parser, Debug)]

collector/src/bin/collector.rs

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ use tokio::runtime::Runtime;
2626

2727
use collector::execute::bencher::BenchProcessor;
2828
use collector::execute::profiler::{ProfileProcessor, Profiler};
29-
use collector::runtime::bench_runtime;
29+
use collector::runtime::{bench_runtime, BenchmarkFilter};
3030
use collector::toolchain::{get_local_toolchain, Compiler, Sysroot};
3131

3232
fn n_normal_benchmarks_remaining(n: usize) -> String {
@@ -710,8 +710,7 @@ fn main_result() -> anyhow::Result<i32> {
710710
bench_runtime(
711711
&rustc,
712712
id.as_deref(),
713-
exclude,
714-
include,
713+
BenchmarkFilter::new(exclude, include),
715714
runtime_benchmark_dir,
716715
)?;
717716
Ok(0)

collector/src/runtime.rs

Lines changed: 76 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
use crate::benchmark::profile::Profile;
22
use crate::toolchain::{get_local_toolchain, LocalToolchain};
3+
use benchlib::benchmark::passes_filter;
34
use benchlib::messages::BenchmarkResult;
45
use cargo_metadata::Message;
56
use std::path::{Path, PathBuf};
@@ -8,6 +9,46 @@ use std::process::Command;
89
#[derive(Debug)]
910
struct BenchmarkBinary {
1011
path: PathBuf,
12+
benchmark_names: Vec<String>,
13+
}
14+
15+
#[derive(Debug)]
16+
struct BenchmarkDatabase {
17+
binaries: Vec<BenchmarkBinary>,
18+
}
19+
20+
impl BenchmarkDatabase {
21+
fn benchmark_names(&self) -> impl Iterator<Item = &str> {
22+
self.binaries
23+
.iter()
24+
.flat_map(|binary| binary.benchmark_names.iter().map(|n| n.as_ref()))
25+
}
26+
27+
fn total_benchmark_count(&self) -> u64 {
28+
self.benchmark_names().count() as u64
29+
}
30+
fn filtered_benchmark_count(&self, filter: &BenchmarkFilter) -> u64 {
31+
self.benchmark_names()
32+
.filter(|benchmark| {
33+
passes_filter(
34+
&benchmark,
35+
filter.exclude.as_deref(),
36+
filter.include.as_deref(),
37+
)
38+
})
39+
.count() as u64
40+
}
41+
}
42+
43+
pub struct BenchmarkFilter {
44+
exclude: Option<String>,
45+
include: Option<String>,
46+
}
47+
48+
impl BenchmarkFilter {
49+
pub fn new(exclude: Option<String>, include: Option<String>) -> BenchmarkFilter {
50+
Self { exclude, include }
51+
}
1152
}
1253

1354
/// Perform a series of runtime benchmarks using the provided `rustc` compiler.
@@ -17,19 +58,25 @@ struct BenchmarkBinary {
1758
pub fn bench_runtime(
1859
rustc: &str,
1960
id: Option<&str>,
20-
exclude: Option<String>,
21-
include: Option<String>,
61+
filter: BenchmarkFilter,
2262
benchmark_dir: PathBuf,
2363
) -> anyhow::Result<()> {
2464
let toolchain = get_local_toolchain(&[Profile::Opt], rustc, None, None, id, "")?;
2565
let output = compile_runtime_benchmarks(&toolchain, &benchmark_dir)?;
26-
let binaries = gather_binaries(&output)?;
66+
let benchmark_db = discover_benchmarks(&output)?;
67+
68+
let total_benchmark_count = benchmark_db.total_benchmark_count();
69+
let filtered = benchmark_db.filtered_benchmark_count(&filter);
70+
println!(
71+
"Executing {} benchmarks ({} filtered out)",
72+
filtered,
73+
total_benchmark_count - filtered
74+
);
2775

28-
for binary in binaries {
76+
for binary in benchmark_db.binaries {
2977
let name = binary.path.file_name().and_then(|s| s.to_str()).unwrap();
3078

31-
let data: Vec<BenchmarkResult> =
32-
execute_runtime_binary(&binary.path, name, exclude.as_deref(), include.as_deref())?;
79+
let data: Vec<BenchmarkResult> = execute_runtime_binary(&binary.path, name, &filter)?;
3380
// TODO: do something with the result
3481
println!("{name}: {:?}", data);
3582
}
@@ -43,8 +90,7 @@ pub fn bench_runtime(
4390
fn execute_runtime_binary(
4491
binary: &Path,
4592
name: &str,
46-
exclude: Option<&str>,
47-
include: Option<&str>,
93+
filter: &BenchmarkFilter,
4894
) -> anyhow::Result<Vec<BenchmarkResult>> {
4995
// Turn off ASLR
5096
let mut command = Command::new("setarch");
@@ -53,10 +99,10 @@ fn execute_runtime_binary(
5399
command.arg(binary);
54100
command.arg("benchmark");
55101

56-
if let Some(exclude) = exclude {
102+
if let Some(ref exclude) = filter.exclude {
57103
command.args(&["--exclude", exclude]);
58104
}
59-
if let Some(include) = include {
105+
if let Some(ref include) = filter.include {
60106
command.args(&["--include", include]);
61107
}
62108

@@ -99,7 +145,8 @@ fn compile_runtime_benchmarks(toolchain: &LocalToolchain, dir: &Path) -> anyhow:
99145
}
100146

101147
/// Parse Cargo JSON output and find all compiled binaries.
102-
fn gather_binaries(cargo_stdout: &[u8]) -> anyhow::Result<Vec<BenchmarkBinary>> {
148+
/// Then execute each benchmark with the `list-benchmarks` command to find out its benchmark names.
149+
fn discover_benchmarks(cargo_stdout: &[u8]) -> anyhow::Result<BenchmarkDatabase> {
103150
let mut binaries = vec![];
104151

105152
for message in Message::parse_stream(cargo_stdout) {
@@ -109,7 +156,16 @@ fn gather_binaries(cargo_stdout: &[u8]) -> anyhow::Result<Vec<BenchmarkBinary>>
109156
if let Some(ref executable) = artifact.executable {
110157
if artifact.target.kind.iter().any(|k| k == "bin") {
111158
let path = executable.as_std_path().to_path_buf();
112-
binaries.push(BenchmarkBinary { path });
159+
let benchmarks = gather_benchmarks(&path).map_err(|err| {
160+
anyhow::anyhow!(
161+
"Cannot gather benchmarks from `{}`: {err:?}",
162+
path.display()
163+
)
164+
})?;
165+
binaries.push(BenchmarkBinary {
166+
path,
167+
benchmark_names: benchmarks,
168+
});
113169
}
114170
}
115171
}
@@ -119,5 +175,12 @@ fn gather_binaries(cargo_stdout: &[u8]) -> anyhow::Result<Vec<BenchmarkBinary>>
119175

120176
log::debug!("Found binaries: {:?}", binaries);
121177

122-
Ok(binaries)
178+
Ok(BenchmarkDatabase { binaries })
179+
}
180+
181+
/// Uses the `list-benchmarks` command from `benchlib` to find the benchmark names from the given
182+
/// benchmark binary.
183+
fn gather_benchmarks(binary: &Path) -> anyhow::Result<Vec<String>> {
184+
let output = Command::new(binary).arg("list-benchmarks").output()?;
185+
Ok(serde_json::from_slice(&output.stdout)?)
123186
}

0 commit comments

Comments
 (0)