Skip to content

Commit f0c3b78

Browse files
committed
refactor: convert benches back to criterion
codspeed compat with divan wouldn't let me use byte counters for measuring throughput
1 parent c6d3f88 commit f0c3b78

File tree

5 files changed

+72
-73
lines changed

5 files changed

+72
-73
lines changed

Cargo.lock

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

crates/biome_js_analyze/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ biome_plugin_loader = { workspace = true }
5555
biome_service = { workspace = true }
5656
biome_test_utils = { path = "../biome_test_utils" }
5757
criterion = { package = "codspeed-criterion-compat", version = "=3.0.5" }
58-
divan = { package = "codspeed-divan-compat", version = "4.0.4" }
58+
5959
insta = { workspace = true, features = ["glob"] }
6060
tests_macros = { path = "../tests_macros" }
6161

Lines changed: 26 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,5 @@
1-
//! Benchmark for the Tailwind CSS class parser in `use_sorted_classes` rule.
2-
//!
3-
//! Useful for comparing performance to the newer (and hopefully better) `biome_tailwind_parser` crate.
4-
51
use biome_js_analyze::lint::nursery::use_sorted_classes::class_lexer::tokenize_class;
6-
use divan::{Bencher, counter::BytesCount};
2+
use criterion::{BenchmarkId, Criterion, Throughput, black_box, criterion_group, criterion_main};
73

84
#[cfg(target_os = "windows")]
95
#[global_allocator]
@@ -21,11 +17,8 @@ static GLOBAL: tikv_jemallocator::Jemalloc = tikv_jemallocator::Jemalloc;
2117
#[global_allocator]
2218
static GLOBAL: std::alloc::System = std::alloc::System;
2319

24-
fn main() {
25-
// Run registered benchmarks.
26-
divan::main();
27-
}
28-
20+
/// Benchmark for the Tailwind CSS class parser in `use_sorted_classes` rule,
21+
/// ported from divan to criterion.
2922
const CLASS_STRING_FIXTURES: &[(&str, &str)] = &[
3023
(
3124
"simple_classes",
@@ -46,23 +39,28 @@ const CLASS_STRING_FIXTURES: &[(&str, &str)] = &[
4639
),
4740
];
4841

49-
fn class_string_cases() -> impl Iterator<Item = &'static str> {
50-
CLASS_STRING_FIXTURES.iter().map(|(name, _)| *name)
51-
}
42+
fn bench_use_sorted_classes_parser(c: &mut Criterion) {
43+
let mut group = c.benchmark_group("use_sorted_classes_parser");
5244

53-
#[divan::bench(name = "class_strings", args = class_string_cases(), sample_size=10)]
54-
fn bench_class_strings(bencher: Bencher, name: &str) {
55-
bencher
56-
.with_inputs(|| {
57-
CLASS_STRING_FIXTURES
58-
.iter()
59-
.find_map(|(case_name, content)| (*case_name == name).then_some(*content))
60-
.expect("cannot find test case")
61-
})
62-
.input_counter(BytesCount::of_str)
63-
.bench_local_values(|content| {
64-
for class in content.split_whitespace() {
65-
divan::black_box(tokenize_class(divan::black_box(class)));
66-
}
67-
});
45+
for (name, content) in CLASS_STRING_FIXTURES {
46+
let len = content.len() as u64;
47+
group.throughput(Throughput::Bytes(len));
48+
49+
group.bench_with_input(
50+
BenchmarkId::new("class_strings", name),
51+
content,
52+
|b, input| {
53+
b.iter(|| {
54+
for class in input.split_whitespace() {
55+
black_box(tokenize_class(black_box(class)));
56+
}
57+
});
58+
},
59+
);
60+
}
61+
62+
group.finish();
6863
}
64+
65+
criterion_group!(use_sorted_classes_parser, bench_use_sorted_classes_parser);
66+
criterion_main!(use_sorted_classes_parser);

crates/biome_tailwind_parser/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ tracing = { workspace = true }
2828
[dev-dependencies]
2929
biome_test_utils = { workspace = true }
3030
camino = { workspace = true }
31-
divan = { package = "codspeed-divan-compat", version = "4.0.4" }
31+
criterion = { package = "codspeed-criterion-compat", version = "=3.0.5" }
3232
insta = { workspace = true }
3333
quickcheck = { workspace = true }
3434
quickcheck_macros = { workspace = true }
Lines changed: 43 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
1+
use std::fs;
2+
use std::path::Path;
3+
14
use biome_rowan::NodeCache;
25
use biome_tailwind_parser::{parse_tailwind, parse_tailwind_with_cache};
3-
use divan::{Bencher, counter::BytesCount};
4-
use std::path::Path;
6+
use criterion::{BenchmarkId, Criterion, Throughput, black_box, criterion_group, criterion_main};
57

68
#[cfg(target_os = "windows")]
79
#[global_allocator]
@@ -19,57 +21,57 @@ static GLOBAL: tikv_jemallocator::Jemalloc = tikv_jemallocator::Jemalloc;
1921
#[global_allocator]
2022
static GLOBAL: std::alloc::System = std::alloc::System;
2123

22-
fn main() {
23-
divan::main();
24-
}
24+
/// Load fixture files from `benches/fixtures` returning (file_name, content).
25+
fn load_fixtures() -> Vec<(String, String)> {
26+
let fixtures_dir = Path::new(env!("CARGO_MANIFEST_DIR")).join("benches/fixtures");
27+
let mut fixtures = Vec::new();
2528

26-
fn fixture_names() -> impl Iterator<Item = String> {
27-
let fixtures_dir = Path::new("benches/fixtures");
28-
let mut names = Vec::new();
29-
30-
if let Ok(entries) = std::fs::read_dir(fixtures_dir) {
29+
if let Ok(entries) = fs::read_dir(&fixtures_dir) {
3130
for entry in entries.flatten() {
3231
let path = entry.path();
3332
if path.is_file()
34-
&& let Some(file_name) = path.file_name().and_then(|n| n.to_str()) {
35-
names.push(file_name.to_string());
36-
}
33+
&& let Some(file_name) = path.file_name().and_then(|n| n.to_str())
34+
{
35+
let content = fs::read_to_string(&path).expect("Failed to read fixture file");
36+
fixtures.push((file_name.to_string(), content));
37+
}
3738
}
3839
}
3940

40-
names.into_iter()
41+
fixtures
4142
}
4243

43-
#[divan::bench(name = "uncached", args = fixture_names(), sample_size=10)]
44-
fn bench_uncached(bencher: Bencher, filename: &str) {
45-
let fixtures_dir = Path::new("benches/fixtures");
46-
let path = fixtures_dir.join(filename);
47-
let code = std::fs::read_to_string(&path).unwrap_or_default();
44+
fn bench_tailwind(c: &mut Criterion) {
45+
let fixtures = load_fixtures();
4846

49-
bencher
50-
.with_inputs(|| code.clone())
51-
.input_counter(BytesCount::of_str)
52-
.bench_local_values(|code| {
53-
let result = parse_tailwind(&code);
54-
divan::black_box(result);
55-
});
56-
}
47+
let mut group = c.benchmark_group("tailwind_parser");
5748

58-
#[divan::bench(name = "cached", args = fixture_names(), sample_size=10)]
59-
fn bench_cached(bencher: Bencher, filename: &str) {
60-
let fixtures_dir = Path::new("benches/fixtures");
61-
let path = fixtures_dir.join(filename);
62-
let code = std::fs::read_to_string(&path).unwrap_or_default();
49+
for (name, content) in &fixtures {
50+
let len = content.len() as u64;
6351

64-
bencher
65-
.with_inputs(|| {
52+
group.throughput(Throughput::Bytes(len));
53+
group.bench_with_input(BenchmarkId::new("uncached", name), content, |b, code| {
54+
b.iter(|| {
55+
let result = parse_tailwind(black_box(code));
56+
black_box(result);
57+
});
58+
});
59+
60+
group.throughput(Throughput::Bytes(len));
61+
group.bench_with_input(BenchmarkId::new("cached", name), content, |b, code| {
6662
let mut cache = NodeCache::default();
67-
// Warm-up parse to populate cache.
68-
let _ = parse_tailwind_with_cache(&code, &mut cache);
69-
(cache, code.clone())
70-
})
71-
.input_counter(|(_cache, code)| BytesCount::of_str(code))
72-
.bench_local_values(|(mut cache, code)| {
73-
divan::black_box(parse_tailwind_with_cache(&code, &mut cache));
63+
// Warm-up parse to populate cache (excluded from measurement).
64+
let _ = parse_tailwind_with_cache(code, &mut cache);
65+
66+
b.iter(|| {
67+
let result = parse_tailwind_with_cache(black_box(code), &mut cache);
68+
black_box(result);
69+
});
7470
});
71+
}
72+
73+
group.finish();
7574
}
75+
76+
criterion_group!(tailwind_parser, bench_tailwind);
77+
criterion_main!(tailwind_parser);

0 commit comments

Comments
 (0)