Skip to content

Commit 9863312

Browse files
authored
Merge pull request #2139 from Kobzol/cli-match
Add `--exact-match` CLI argument to allow exact matching of benchmarks
2 parents 9316c1e + 25108f3 commit 9863312

File tree

5 files changed

+110
-52
lines changed

5 files changed

+110
-52
lines changed

collector/README.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -135,6 +135,9 @@ The following options alter the behaviour of the `bench_local` subcommand.
135135
dedicated to artifact sizes (ending with `-tiny`).
136136
- `--id <ID>` the identifier that will be used to identify the results in the
137137
database.
138+
- `--exact-match <BENCHMARKS>`: comma-separated list of benchmark names that should be
139+
executed. The names have to match exactly. Cannot be combined with
140+
`--include`/`--exclude`/`--exclude-suffix`.
138141
- `--include <INCLUDE>`: the inverse of `--exclude`. The argument is a
139142
comma-separated list of benchmark prefixes. When this option is specified, a
140143
benchmark is included in the run only if its name matches one of the given

collector/src/bin/collector.rs

Lines changed: 46 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -40,13 +40,14 @@ use collector::compile::benchmark::scenario::Scenario;
4040
use collector::compile::benchmark::target::Target;
4141
use collector::compile::benchmark::{
4242
compile_benchmark_dir, get_compile_benchmarks, ArtifactType, Benchmark, BenchmarkName,
43+
CompileBenchmarkFilter,
4344
};
4445
use collector::compile::execute::bencher::BenchProcessor;
4546
use collector::compile::execute::profiler::{ProfileProcessor, Profiler};
4647
use collector::runtime::{
4748
bench_runtime, get_runtime_benchmark_groups, prepare_runtime_benchmark_suite,
48-
runtime_benchmark_dir, BenchmarkFilter, BenchmarkSuite, BenchmarkSuiteCompilation,
49-
CargoIsolationMode, RuntimeProfiler, DEFAULT_RUNTIME_ITERATIONS,
49+
runtime_benchmark_dir, BenchmarkSuite, BenchmarkSuiteCompilation, CargoIsolationMode,
50+
RuntimeBenchmarkFilter, RuntimeProfiler, DEFAULT_RUNTIME_ITERATIONS,
5051
};
5152
use collector::runtime::{profile_runtime, RuntimeCompilationOpts};
5253
use collector::toolchain::{
@@ -105,12 +106,12 @@ struct CompileBenchmarkConfig {
105106

106107
struct RuntimeBenchmarkConfig {
107108
runtime_suite: BenchmarkSuite,
108-
filter: BenchmarkFilter,
109+
filter: RuntimeBenchmarkFilter,
109110
iterations: u32,
110111
}
111112

112113
impl RuntimeBenchmarkConfig {
113-
fn new(suite: BenchmarkSuite, filter: BenchmarkFilter, iterations: u32) -> Self {
114+
fn new(suite: BenchmarkSuite, filter: RuntimeBenchmarkFilter, iterations: u32) -> Self {
114115
Self {
115116
runtime_suite: suite.filter(&filter),
116117
filter,
@@ -337,6 +338,16 @@ struct LocalOptions {
337338
#[arg(long, value_delimiter = ',')]
338339
include: Vec<String>,
339340

341+
/// Include only benchmarks in this comma-separated list
342+
#[arg(
343+
long,
344+
value_delimiter = ',',
345+
conflicts_with("include"),
346+
conflicts_with("exclude"),
347+
conflicts_with("exclude_suffix")
348+
)]
349+
exact_match: Vec<String>,
350+
340351
/// Include only benchmarks belonging to the given categories.
341352
#[arg(long, value_parser = EnumArgParser::<Category>::default(), default_value = "Primary,Secondary")]
342353
category: MultiEnumValue<Category>,
@@ -688,6 +699,25 @@ enum DownloadSubcommand {
688699
},
689700
}
690701

702+
impl<'a> From<&'a LocalOptions> for CompileBenchmarkFilter<'a> {
703+
fn from(value: &'a LocalOptions) -> Self {
704+
if !value.exact_match.is_empty() {
705+
Self::Exact(&value.exact_match)
706+
} else if !value.include.is_empty()
707+
|| !value.exclude.is_empty()
708+
|| !value.exclude_suffix.is_empty()
709+
{
710+
Self::Fuzzy {
711+
include: &value.include,
712+
exclude: &value.exclude,
713+
exclude_suffix: &value.exclude_suffix,
714+
}
715+
} else {
716+
Self::All
717+
}
718+
}
719+
}
720+
691721
fn main_result() -> anyhow::Result<i32> {
692722
env_logger::init();
693723

@@ -761,7 +791,7 @@ fn main_result() -> anyhow::Result<i32> {
761791
};
762792
let config = RuntimeBenchmarkConfig::new(
763793
runtime_suite,
764-
BenchmarkFilter::new(local.exclude, local.include),
794+
RuntimeBenchmarkFilter::new(local.exclude, local.include),
765795
iterations,
766796
);
767797
run_benchmarks(&mut rt, conn, shared, None, Some(config))?;
@@ -884,12 +914,7 @@ fn main_result() -> anyhow::Result<i32> {
884914
target_triple,
885915
)?;
886916

887-
let mut benchmarks = get_compile_benchmarks(
888-
&compile_benchmark_dir,
889-
&local.include,
890-
&local.exclude,
891-
&local.exclude_suffix,
892-
)?;
917+
let mut benchmarks = get_compile_benchmarks(&compile_benchmark_dir, (&local).into())?;
893918
benchmarks.retain(|b| local.category.0.contains(&b.category()));
894919

895920
let artifact_id = ArtifactId::Commit(Commit {
@@ -1006,9 +1031,11 @@ fn main_result() -> anyhow::Result<i32> {
10061031

10071032
let mut benchmarks = get_compile_benchmarks(
10081033
&compile_benchmark_dir,
1009-
&split_args(include),
1010-
&split_args(exclude),
1011-
&[],
1034+
CompileBenchmarkFilter::Fuzzy {
1035+
include: &split_args(include),
1036+
exclude: &split_args(exclude),
1037+
exclude_suffix: &[],
1038+
},
10121039
)?;
10131040
benchmarks.retain(|b| b.category().is_primary_or_secondary());
10141041

@@ -1042,7 +1069,7 @@ fn main_result() -> anyhow::Result<i32> {
10421069

10431070
let runtime_config = RuntimeBenchmarkConfig {
10441071
runtime_suite,
1045-
filter: BenchmarkFilter::keep_all(),
1072+
filter: RuntimeBenchmarkFilter::keep_all(),
10461073
iterations: DEFAULT_RUNTIME_ITERATIONS,
10471074
};
10481075
let shared = SharedBenchmarkConfig {
@@ -1102,12 +1129,7 @@ fn main_result() -> anyhow::Result<i32> {
11021129
let scenarios = &opts.scenarios.0;
11031130
let backends = &opts.codegen_backends.0;
11041131

1105-
let mut benchmarks = get_compile_benchmarks(
1106-
&compile_benchmark_dir,
1107-
&local.include,
1108-
&local.exclude,
1109-
&local.exclude_suffix,
1110-
)?;
1132+
let mut benchmarks = get_compile_benchmarks(&compile_benchmark_dir, (&local).into())?;
11111133
benchmarks.retain(|b| local.category.0.contains(&b.category()));
11121134

11131135
let mut errors = BenchmarkErrors::new();
@@ -1315,12 +1337,7 @@ fn binary_stats_compile(
13151337
Profile::Opt => CargoProfile::Release,
13161338
_ => return Err(anyhow::anyhow!("Only Debug and Opt profiles are supported")),
13171339
};
1318-
let benchmarks = get_compile_benchmarks(
1319-
&compile_benchmark_dir(),
1320-
&local.include,
1321-
&local.exclude,
1322-
&local.exclude_suffix,
1323-
)?;
1340+
let benchmarks = get_compile_benchmarks(&compile_benchmark_dir(), (&local).into())?;
13241341
for benchmark in benchmarks {
13251342
println!("Stats for benchmark `{}`", benchmark.name);
13261343
println!("{}", "-".repeat(20));
@@ -1713,7 +1730,7 @@ fn bench_published_artifact(
17131730
};
17141731

17151732
// Exclude benchmarks that don't work with a stable compiler.
1716-
let mut compile_benchmarks = get_compile_benchmarks(dirs.compile, &[], &[], &[])?;
1733+
let mut compile_benchmarks = get_compile_benchmarks(dirs.compile, CompileBenchmarkFilter::All)?;
17171734
compile_benchmarks.retain(|b| b.category().is_stable());
17181735

17191736
let runtime_suite = rt.block_on(load_runtime_benchmarks(
@@ -1745,7 +1762,7 @@ fn bench_published_artifact(
17451762
}),
17461763
Some(RuntimeBenchmarkConfig::new(
17471764
runtime_suite,
1748-
BenchmarkFilter::keep_all(),
1765+
RuntimeBenchmarkFilter::keep_all(),
17491766
DEFAULT_RUNTIME_ITERATIONS,
17501767
)),
17511768
)

collector/src/compile/benchmark/mod.rs

Lines changed: 53 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -466,14 +466,22 @@ pub fn compile_benchmark_dir() -> PathBuf {
466466
PathBuf::from("collector/compile-benchmarks")
467467
}
468468

469+
pub enum CompileBenchmarkFilter<'a> {
470+
All,
471+
/// Select benchmarks exactly matching the given benchmark names.
472+
Exact(&'a [String]),
473+
/// Select benchmarks matching the given prefixes/suffixes.
474+
Fuzzy {
475+
include: &'a [String],
476+
exclude: &'a [String],
477+
exclude_suffix: &'a [String],
478+
},
479+
}
480+
469481
pub fn get_compile_benchmarks(
470482
benchmark_dir: &Path,
471-
include: &[String],
472-
exclude: &[String],
473-
exclude_suffix: &[String],
483+
filter: CompileBenchmarkFilter<'_>,
474484
) -> anyhow::Result<Vec<Benchmark>> {
475-
let mut benchmarks = Vec::new();
476-
477485
let mut paths = Vec::new();
478486
for entry in std::fs::read_dir(benchmark_dir)
479487
.with_context(|| format!("failed to list benchmark dir '{}'", benchmark_dir.display()))?
@@ -493,6 +501,40 @@ pub fn get_compile_benchmarks(
493501
paths.push((path, name));
494502
}
495503

504+
let mut benchmarks = match filter {
505+
CompileBenchmarkFilter::All => paths
506+
.into_iter()
507+
.map(|(path, name)| Benchmark::new(name, path))
508+
.collect::<anyhow::Result<Vec<Benchmark>>>()?,
509+
CompileBenchmarkFilter::Exact(names) => paths
510+
.into_iter()
511+
.filter(|(_, name)| names.contains(name))
512+
.map(|(path, name)| Benchmark::new(name, path))
513+
.collect::<anyhow::Result<Vec<Benchmark>>>()?,
514+
CompileBenchmarkFilter::Fuzzy {
515+
include,
516+
exclude,
517+
exclude_suffix,
518+
} => select_benchmarks_fuzzy(paths, include, exclude, exclude_suffix)?,
519+
};
520+
521+
benchmarks.sort_by_key(|benchmark| benchmark.name.clone());
522+
523+
if benchmarks.is_empty() {
524+
eprintln!("Warning: no benchmarks selected! Try less strict filters.");
525+
}
526+
527+
Ok(benchmarks)
528+
}
529+
530+
fn select_benchmarks_fuzzy(
531+
paths: Vec<(PathBuf, String)>,
532+
include: &[String],
533+
exclude: &[String],
534+
exclude_suffix: &[String],
535+
) -> anyhow::Result<Vec<Benchmark>> {
536+
let mut benchmarks = Vec::new();
537+
496538
// For each --include/--exclude entry, we count how many times it's used,
497539
// to enable `check_for_unused` below.
498540
fn to_hashmap(xyz: &[String]) -> HashMap<&str, usize> {
@@ -551,12 +593,6 @@ Expected zero or more entries or substrings from list: {:?}."#,
551593
check_for_unused("exclude", excludes)?;
552594
check_for_unused("exclude-suffix", exclude_suffixes)?;
553595

554-
benchmarks.sort_by_key(|benchmark| benchmark.name.clone());
555-
556-
if benchmarks.is_empty() {
557-
eprintln!("Warning: no benchmarks selected! Try less strict filters.");
558-
}
559-
560596
Ok(benchmarks)
561597
}
562598

@@ -578,17 +614,19 @@ fn substring_matches(
578614

579615
#[cfg(test)]
580616
mod tests {
581-
use crate::compile::benchmark::get_compile_benchmarks;
617+
use crate::compile::benchmark::{get_compile_benchmarks, CompileBenchmarkFilter};
582618
use std::path::Path;
583619

584620
#[test]
585621
fn check_compile_benchmarks() {
586622
// Check that we can deserialize all perf-config.json files in the compile benchmark
587623
// directory.
588624
let root = env!("CARGO_MANIFEST_DIR");
589-
let benchmarks =
590-
get_compile_benchmarks(&Path::new(root).join("compile-benchmarks"), &[], &[], &[])
591-
.unwrap();
625+
let benchmarks = get_compile_benchmarks(
626+
&Path::new(root).join("compile-benchmarks"),
627+
CompileBenchmarkFilter::All,
628+
)
629+
.unwrap();
592630
assert!(!benchmarks.is_empty());
593631
}
594632
}

collector/src/runtime/benchmark.rs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ pub struct BenchmarkSuite {
4242
impl BenchmarkSuite {
4343
/// Returns a new suite containing only groups that contains at least a single benchmark
4444
/// that matches the filter.
45-
pub fn filter(self, filter: &BenchmarkFilter) -> Self {
45+
pub fn filter(self, filter: &RuntimeBenchmarkFilter) -> Self {
4646
let BenchmarkSuite {
4747
toolchain,
4848
groups,
@@ -64,7 +64,7 @@ impl BenchmarkSuite {
6464
}
6565
}
6666

67-
pub fn filtered_benchmark_count(&self, filter: &BenchmarkFilter) -> u64 {
67+
pub fn filtered_benchmark_count(&self, filter: &RuntimeBenchmarkFilter) -> u64 {
6868
self.benchmark_names()
6969
.filter(|benchmark| passes_filter(benchmark, &filter.exclude, &filter.include))
7070
.count() as u64
@@ -86,12 +86,12 @@ impl BenchmarkSuite {
8686
}
8787
}
8888

89-
pub struct BenchmarkFilter {
89+
pub struct RuntimeBenchmarkFilter {
9090
pub exclude: Vec<String>,
9191
pub include: Vec<String>,
9292
}
9393

94-
impl BenchmarkFilter {
94+
impl RuntimeBenchmarkFilter {
9595
pub fn keep_all() -> Self {
9696
Self {
9797
exclude: vec![],

collector/src/runtime/mod.rs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,8 @@ use thousands::Separable;
99
use benchlib::comm::messages::{BenchmarkMessage, BenchmarkResult, BenchmarkStats};
1010
pub use benchmark::{
1111
get_runtime_benchmark_groups, prepare_runtime_benchmark_suite, runtime_benchmark_dir,
12-
BenchmarkFilter, BenchmarkGroup, BenchmarkGroupCrate, BenchmarkSuite,
13-
BenchmarkSuiteCompilation, CargoIsolationMode,
12+
BenchmarkGroup, BenchmarkGroupCrate, BenchmarkSuite, BenchmarkSuiteCompilation,
13+
CargoIsolationMode, RuntimeBenchmarkFilter,
1414
};
1515
use database::{ArtifactIdNumber, CollectionId, Connection};
1616

@@ -33,7 +33,7 @@ pub async fn bench_runtime(
3333
conn: &mut dyn Connection,
3434
suite: BenchmarkSuite,
3535
collector: &CollectorCtx,
36-
filter: BenchmarkFilter,
36+
filter: RuntimeBenchmarkFilter,
3737
iterations: u32,
3838
) -> anyhow::Result<()> {
3939
let filtered = suite.filtered_benchmark_count(&filter);
@@ -203,7 +203,7 @@ async fn record_stats(
203203
/// a set of runtime benchmarks and print `BenchmarkMessage`s encoded as JSON, one per line.
204204
fn execute_runtime_benchmark_binary(
205205
binary: &Path,
206-
filter: &BenchmarkFilter,
206+
filter: &RuntimeBenchmarkFilter,
207207
iterations: u32,
208208
) -> anyhow::Result<impl Iterator<Item = anyhow::Result<BenchmarkMessage>>> {
209209
let mut command = prepare_command(binary);

0 commit comments

Comments
 (0)