Skip to content

Commit 60014f4

Browse files
feat(criterion): add benchmark filtering for instrumentation mode
1 parent f806f2b commit 60014f4

File tree

7 files changed

+115
-8
lines changed

7 files changed

+115
-8
lines changed

Cargo.lock

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

crates/cargo-codspeed/tests/simple-criterion.rs

Lines changed: 31 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,12 @@
11
use assert_cmd::assert::OutputAssertExt;
2-
use predicates::str::contains;
2+
use predicates::{prelude::PredicateBooleanExt, str::contains};
33

44
mod helpers;
55
use helpers::*;
66

77
const DIR: &str = "tests/simple-criterion.in";
8+
const FIB_BENCH_NAME: &str = "fib 20";
9+
const BUBBLE_SORT_BENCH_NAME: &str = "bubble sort";
810

911
#[test]
1012
fn test_criterion_run_without_build() {
@@ -66,8 +68,8 @@ fn test_criterion_build_and_run_single() {
6668
.args(["--bench", "another_criterion_example"])
6769
.assert()
6870
.success()
69-
.stderr(contains("Finished running 1 benchmark suite(s)"))
70-
.stderr(contains("another_criterion_example"));
71+
.stdout(contains("another_criterion_example"))
72+
.stderr(contains("Finished running 1 benchmark suite(s)"));
7173
teardown(dir);
7274
}
7375

@@ -80,20 +82,43 @@ fn test_criterion_build_and_run_filtered_by_name() {
8082
.arg("fib 20")
8183
.assert()
8284
.success()
85+
.stdout(contains(FIB_BENCH_NAME))
86+
.stdout(contains(BUBBLE_SORT_BENCH_NAME).not())
87+
.stderr(contains("Finished running 2 benchmark suite(s)"));
88+
cargo_codspeed(&dir)
89+
.arg("run")
90+
.arg("bu.*le_sort")
91+
.assert()
92+
.success()
93+
.stdout(contains(FIB_BENCH_NAME).not())
94+
.stdout(contains(BUBBLE_SORT_BENCH_NAME))
8395
.stderr(contains("Finished running 2 benchmark suite(s)"));
8496
teardown(dir);
8597
}
8698

8799
#[test]
88-
fn test_criterion_build_and_run_filtered_by_partial_name() {
100+
fn test_criterion_build_and_run_filtered_by_name_single() {
89101
let dir = setup(DIR, Project::Simple);
90102
cargo_codspeed(&dir).arg("build").assert().success();
91103
cargo_codspeed(&dir)
92104
.arg("run")
93-
.arg("bubble")
105+
.arg("bu.*le")
106+
.args(["--bench", "criterion_example"])
94107
.assert()
95108
.success()
96-
.stderr(contains("Finished running 2 benchmark suite(s)"));
109+
.stdout(contains(BUBBLE_SORT_BENCH_NAME).not()) // We are filtering with a name that is not in the selected benchmark
110+
.stdout(contains(FIB_BENCH_NAME).not())
111+
.stderr(contains("Finished running 1 benchmark suite(s)"));
112+
113+
cargo_codspeed(&dir)
114+
.arg("run")
115+
.arg("fib")
116+
.args(["--bench", "criterion_example"])
117+
.assert()
118+
.success()
119+
.stdout(contains(FIB_BENCH_NAME))
120+
.stdout(contains(BUBBLE_SORT_BENCH_NAME).not())
121+
.stderr(contains("Finished running 1 benchmark suite(s)"));
97122
teardown(dir);
98123
}
99124

crates/criterion_compat/Cargo.toml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,8 @@ keywords = ["codspeed", "benchmark", "criterion"]
2020
criterion = { package = "codspeed-criterion-compat-walltime", path = "./criterion_fork", version = "=4.0.0", default-features = false }
2121
codspeed = { path = "../codspeed", version = "=4.0.0" }
2222
colored = "2.1.0"
23+
clap = { version = "4", default-features = false, features = ["std"] }
24+
regex = { version = "1.5", default-features = false, features = ["std"] }
2325

2426
futures = { version = "0.3", default-features = false, optional = true }
2527
smol = { version = "2.0", default-features = false, optional = true }

crates/criterion_compat/src/compat/criterion.rs

Lines changed: 43 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,13 +6,15 @@ use criterion::{
66
profiler::Profiler,
77
PlottingBackend,
88
};
9+
use regex::Regex;
910

10-
use crate::{Bencher, BenchmarkGroup, BenchmarkId};
11+
use crate::{Bencher, BenchmarkFilter, BenchmarkGroup, BenchmarkId};
1112

1213
pub struct Criterion<M: Measurement = WallTime> {
1314
pub codspeed: Option<Rc<RefCell<CodSpeed>>>,
1415
pub current_file: String,
1516
pub macro_group: String,
17+
pub filter: BenchmarkFilter,
1618
phantom: PhantomData<*const M>,
1719
}
1820

@@ -23,19 +25,57 @@ impl Criterion {
2325
"Harness: codspeed-criterion-compat v{}",
2426
env!("CARGO_PKG_VERSION"),
2527
);
28+
29+
// Parse CLI arguments to extract filter
30+
let filter = Self::parse_filter();
31+
2632
Criterion {
2733
codspeed: Some(Rc::new(RefCell::new(CodSpeed::new()))),
2834
current_file: String::new(),
2935
macro_group: String::new(),
36+
filter,
3037
phantom: PhantomData,
3138
}
3239
}
3340

41+
fn parse_filter() -> BenchmarkFilter {
42+
use clap::{Arg, Command};
43+
44+
let matches = Command::new("Criterion Benchmark")
45+
.arg(
46+
Arg::new("FILTER")
47+
.help("Skip benchmarks whose names do not contain FILTER.")
48+
.index(1),
49+
)
50+
.arg(
51+
Arg::new("exact")
52+
.long("exact")
53+
.num_args(0)
54+
.help("Run benchmarks that exactly match the provided filter"),
55+
)
56+
.get_matches();
57+
58+
if let Some(filter) = matches.get_one::<String>("FILTER") {
59+
if matches.get_flag("exact") {
60+
BenchmarkFilter::Exact(filter.to_owned())
61+
} else {
62+
let regex = Regex::new(filter).unwrap_or_else(|err| {
63+
eprintln!("Unable to parse '{filter}' as a regular expression: {err}");
64+
std::process::exit(1);
65+
});
66+
BenchmarkFilter::Regex(regex)
67+
}
68+
} else {
69+
BenchmarkFilter::AcceptAll
70+
}
71+
}
72+
3473
pub fn with_patched_measurement<M: Measurement>(&mut self, _: Criterion<M>) -> Criterion<M> {
3574
Criterion {
3675
codspeed: self.codspeed.clone(),
3776
current_file: self.current_file.clone(),
3877
macro_group: self.macro_group.clone(),
78+
filter: self.filter.clone(),
3979
phantom: PhantomData,
4080
}
4181
}
@@ -92,6 +132,7 @@ impl Default for Criterion {
92132
codspeed: None,
93133
current_file: String::new(),
94134
macro_group: String::new(),
135+
filter: BenchmarkFilter::AcceptAll,
95136
phantom: PhantomData,
96137
}
97138
}
@@ -104,6 +145,7 @@ impl<M: Measurement> Criterion<M> {
104145
codspeed: self.codspeed,
105146
current_file: self.current_file,
106147
macro_group: self.macro_group,
148+
filter: self.filter,
107149
phantom: PhantomData::<*const M2>,
108150
}
109151
}
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
use regex::Regex;
2+
3+
/// Benchmark filtering support - re-exported from criterion fork.
4+
#[derive(Clone, Debug)]
5+
pub enum BenchmarkFilter {
6+
/// Run all benchmarks.
7+
AcceptAll,
8+
/// Run benchmarks matching this regex.
9+
Regex(Regex),
10+
/// Run the benchmark matching this string exactly.
11+
Exact(String),
12+
/// Do not run any benchmarks.
13+
RejectAll,
14+
}
15+
16+
impl BenchmarkFilter {
17+
/// Returns true if a string matches this filter.
18+
pub fn is_match(&self, id: &str) -> bool {
19+
match self {
20+
Self::AcceptAll => true,
21+
Self::Regex(r) => r.is_match(id),
22+
Self::Exact(e) => e == id,
23+
Self::RejectAll => false,
24+
}
25+
}
26+
}

crates/criterion_compat/src/compat/group.rs

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ use codspeed::{codspeed::CodSpeed, utils::get_git_relative_path};
55
use criterion::measurement::WallTime;
66
use criterion::{measurement::Measurement, PlotConfiguration, SamplingMode, Throughput};
77

8-
use crate::{Bencher, Criterion};
8+
use crate::{Bencher, BenchmarkFilter, Criterion};
99

1010
/// Deprecated: using the default measurement will be removed in the next major version.
1111
/// Defaulting to WallTime differs from the original BenchmarkGroup implementation but avoids creating a breaking change
@@ -14,6 +14,7 @@ pub struct BenchmarkGroup<'a, M: Measurement = WallTime> {
1414
current_file: String,
1515
macro_group: String,
1616
group_name: String,
17+
filter: BenchmarkFilter,
1718
_marker: PhantomData<&'a M>,
1819
}
1920

@@ -29,6 +30,7 @@ impl<'a, M: Measurement> BenchmarkGroup<'a, M> {
2930
current_file: criterion.current_file.clone(),
3031
macro_group: criterion.macro_group.clone(),
3132
group_name,
33+
filter: criterion.filter.clone(),
3234
_marker: PhantomData,
3335
}
3436
}
@@ -73,6 +75,12 @@ impl<'a, M: Measurement> BenchmarkGroup<'a, M> {
7375
if let Some(parameter) = id.parameter {
7476
uri = format!("{uri}[{parameter}]");
7577
}
78+
79+
// Apply filter - skip benchmark if it doesn't match
80+
if !self.filter.is_match(&uri) {
81+
return;
82+
}
83+
7684
let mut codspeed = self.codspeed.borrow_mut();
7785
let mut b = Bencher::new(&mut codspeed, uri);
7886
f(&mut b, input);
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
11
mod bencher;
22
mod criterion;
3+
mod filter;
34
mod group;
45
mod macros;
56

67
pub use self::bencher::*;
78
pub use self::criterion::*;
9+
pub use self::filter::*;
810
pub use self::group::*;

0 commit comments

Comments
 (0)