diff --git a/benches/common.rs b/benches/common.rs new file mode 100644 index 0000000..f5d93e7 --- /dev/null +++ b/benches/common.rs @@ -0,0 +1,103 @@ +use criterion::{ + measurement::WallTime, + BenchmarkGroup, + BenchmarkId, + Throughput, +}; +use norm::CaseSensitivity; + +pub trait Metric { + type Query<'a>: Copy + FromStr<'a>; + + fn dist(&self, query: Self::Query<'_>, candidate: &str); + fn with_case_sensitivity(self, case_sensitivity: CaseSensitivity) -> Self; + fn with_matched_ranges(self, matched_ranges: bool) -> Self; +} + +pub trait FromStr<'a> { + fn from_str(s: &'a str) -> Self; +} + +#[inline] +fn bench<'a, M, C>( + group: &mut BenchmarkGroup, + id: BenchmarkId, + metric: &M, + query: &str, + candidates: C, +) where + M: Metric, + C: IntoIterator, + C::IntoIter: ExactSizeIterator + Clone, +{ + let candidates = candidates.into_iter(); + + group.throughput(Throughput::Elements(candidates.len() as u64)); + + group.bench_function(id, |b| { + let query = M::Query::from_str(query); + + b.iter(|| { + for candidate in candidates.clone() { + metric.dist(query, candidate); + } + }) + }); +} + +fn param( + case: CaseSensitivity, + with_ranges: bool, + suffix: Option<&str>, +) -> String { + let mut s = String::new(); + + let case = match case { + CaseSensitivity::Sensitive => "case_sensitive", + CaseSensitivity::Insensitive => "case_insensitive", + CaseSensitivity::Smart => "case_smart", + }; + + s.push_str(case); + + let ranges = if with_ranges { "_with_ranges" } else { "" }; + + s.push_str(ranges); + + if let Some(suffix) = suffix { + s.push('_'); + s.push_str(suffix); + } + + s +} + +#[inline] +pub fn short( + mut metric: M, + suffix: Option<&str>, + mut group: BenchmarkGroup, +) where + M: Metric, +{ + for case in [ + CaseSensitivity::Sensitive, + CaseSensitivity::Insensitive, + CaseSensitivity::Smart, + ] { + for ranges in [true, false] { + let id = BenchmarkId::new("short", param(case, ranges, suffix)); + + metric = + metric.with_case_sensitivity(case).with_matched_ranges(ranges); + + bench( + &mut group, + id, + &metric, + "jelly", + core::iter::once("jellyfish"), + ); + } + } +} diff --git a/benches/fzf_v1.rs b/benches/fzf_v1.rs index 220df81..9ec01b7 100644 --- a/benches/fzf_v1.rs +++ b/benches/fzf_v1.rs @@ -1,8 +1,10 @@ +mod common; + +use common as bench; use criterion::{ criterion_group, criterion_main, measurement::WallTime, - Bencher, BenchmarkGroup, Criterion, }; @@ -12,28 +14,34 @@ use norm::{ Metric, }; -fn short(fzf: &FzfV1, b: &mut Bencher) { - let jelly = FzfQuery::new("jelly"); - b.iter(|| fzf.distance(jelly, "jellyfish")) -} - -fn sensitive_with_ranges() -> FzfV1 { - FzfV1::new() - .with_case_sensitivity(CaseSensitivity::Sensitive) - .with_matched_ranges(true) +impl<'a> bench::FromStr<'a> for FzfQuery<'a> { + fn from_str(s: &'a str) -> Self { + FzfQuery::new(s) + } } -fn short_case_sensitive_with_ranges(c: &mut Criterion) { - let fzf = sensitive_with_ranges(); +impl bench::Metric for FzfV1 { + type Query<'a> = FzfQuery<'a>; - group(c).bench_function("short_case_sensitive_with_ranges", |b| { - short(&fzf, b) - }); + #[inline] + fn dist(&self, query: FzfQuery, candidate: &str) { + self.distance(query, candidate); + } + fn with_case_sensitivity(self, case_sensitivity: CaseSensitivity) -> Self { + self.with_case_sensitivity(case_sensitivity) + } + fn with_matched_ranges(self, matched_ranges: bool) -> Self { + self.with_matched_ranges(matched_ranges) + } } fn group(c: &mut Criterion) -> BenchmarkGroup { c.benchmark_group("fzf_v1") } +fn short_case_sensitive_with_ranges(c: &mut Criterion) { + bench::short(FzfV1::new(), None, group(c)); +} + criterion_group!(benches, short_case_sensitive_with_ranges); criterion_main!(benches);