use criterion::{BenchmarkId, Criterion, criterion_group, criterion_main};
use ftui_text::{S3FifoWidthCache, TinyLfuWidthCache, WidthCache};
use std::hint::black_box;
fn zipfian_workload(size: usize) -> Vec<String> {
let mut work = Vec::with_capacity(size);
for i in 0..size {
let key = if i % 5 == 0 {
format!("cold_string_{i}")
} else {
format!("hot_key_{}", i % 20)
};
work.push(key);
}
work
}
fn scan_workload(size: usize) -> Vec<String> {
(0..size).map(|i| format!("scan_unique_{i}")).collect()
}
fn mixed_workload(size: usize) -> Vec<String> {
let mut work = Vec::with_capacity(size);
for i in 0..size {
if i % 3 == 0 {
work.push(format!("noise_{i}"));
} else {
work.push(format!("working_set_{}", i % 50));
}
}
work
}
fn bench_lru(c: &mut Criterion) {
let mut group = c.benchmark_group("width_cache/lru");
for (name, workload_fn) in [
("zipfian", zipfian_workload as fn(usize) -> Vec<String>),
("scan", scan_workload),
("mixed", mixed_workload),
] {
for &cap in &[1_000, 10_000, 100_000] {
let work = workload_fn(cap);
group.bench_with_input(BenchmarkId::new(name, cap), &work, |b, work| {
b.iter(|| {
let mut cache = WidthCache::new(cap / 10);
for s in work {
black_box(cache.get_or_compute(s));
}
black_box(cache.stats())
});
});
}
}
group.finish();
}
fn bench_tinylfu(c: &mut Criterion) {
let mut group = c.benchmark_group("width_cache/tinylfu");
for (name, workload_fn) in [
("zipfian", zipfian_workload as fn(usize) -> Vec<String>),
("scan", scan_workload),
("mixed", mixed_workload),
] {
for &cap in &[1_000, 10_000, 100_000] {
let work = workload_fn(cap);
group.bench_with_input(BenchmarkId::new(name, cap), &work, |b, work| {
b.iter(|| {
let mut cache = TinyLfuWidthCache::new(cap / 10);
for s in work {
black_box(cache.get_or_compute(s));
}
black_box(cache.stats())
});
});
}
}
group.finish();
}
fn bench_s3fifo(c: &mut Criterion) {
let mut group = c.benchmark_group("width_cache/s3fifo");
for (name, workload_fn) in [
("zipfian", zipfian_workload as fn(usize) -> Vec<String>),
("scan", scan_workload),
("mixed", mixed_workload),
] {
for &cap in &[1_000, 10_000, 100_000] {
let work = workload_fn(cap);
group.bench_with_input(BenchmarkId::new(name, cap), &work, |b, work| {
b.iter(|| {
let mut cache = S3FifoWidthCache::new(cap / 10);
for s in work {
black_box(cache.get_or_compute(s));
}
black_box(cache.stats())
});
});
}
}
group.finish();
}
fn bench_hit_rates(c: &mut Criterion) {
let mut group = c.benchmark_group("width_cache/hit_rate_comparison");
group.sample_size(10);
for &cap in &[1_000, 10_000] {
let zipf = zipfian_workload(cap);
let scan = scan_workload(cap);
let mixed = mixed_workload(cap);
for (name, work) in [("zipfian", &zipf), ("scan", &scan), ("mixed", &mixed)] {
group.bench_with_input(
BenchmarkId::new(format!("all/{name}"), cap),
work,
|b, work| {
b.iter(|| {
let cache_cap = cap / 10;
let mut lru = WidthCache::new(cache_cap);
let mut tlfu = TinyLfuWidthCache::new(cache_cap);
let mut s3 = S3FifoWidthCache::new(cache_cap);
for s in work.iter() {
lru.get_or_compute(s);
tlfu.get_or_compute(s);
s3.get_or_compute(s);
}
black_box((lru.stats(), tlfu.stats(), s3.stats()))
});
},
);
}
}
group.finish();
}
criterion_group!(
benches,
bench_lru,
bench_tinylfu,
bench_s3fifo,
bench_hit_rates
);
criterion_main!(benches);