use cached::time::Duration;
use cached::{
Cached, CachedRead, Expires, ExpiringCache, ExpiringLruCache, LruCache, LruTtlCache, TtlCache,
TtlSortedCache, UnboundCache,
};
use criterion::{black_box, criterion_group, criterion_main, Criterion};
use parking_lot::RwLock;
use std::sync::Arc;
#[derive(Clone)]
#[allow(dead_code)]
struct ExpiringValue {
val: usize,
}
impl Expires for ExpiringValue {
fn is_expired(&self) -> bool {
false
}
}
fn bench_cache_hits(c: &mut Criterion) {
let mut group = c.benchmark_group("Cache Hits (O(1) Reads)");
let limit = 1000;
let query_key = 500;
let mut unbound = UnboundCache::new();
for i in 0..limit {
unbound.cache_set(i, i * 2);
}
group.bench_function("UnboundCache hit", |b| {
b.iter(|| {
let res = unbound.cache_get(black_box(&query_key));
black_box(res);
})
});
let mut lru = LruCache::with_size(limit);
for i in 0..limit {
lru.cache_set(i, i * 2);
}
group.bench_function("LruCache hit", |b| {
b.iter(|| {
let res = lru.cache_get(black_box(&query_key));
black_box(res);
})
});
let mut ttl_cache = TtlCache::builder().ttl(Duration::from_secs(3600)).build();
for i in 0..limit {
ttl_cache.cache_set(i, i * 2);
}
group.bench_function("TtlCache hit (O(1))", |b| {
b.iter(|| {
let res = ttl_cache.cache_get(black_box(&query_key));
black_box(res);
})
});
let mut lru_ttl_cache = LruTtlCache::builder()
.size(limit)
.ttl(Duration::from_secs(3600))
.build();
for i in 0..limit {
lru_ttl_cache.cache_set(i, i * 2);
}
group.bench_function("LruTtlCache hit (O(1))", |b| {
b.iter(|| {
let res = lru_ttl_cache.cache_get(black_box(&query_key));
black_box(res);
})
});
let mut expiring_lru_cache = ExpiringLruCache::builder().size(limit).build();
for i in 0..limit {
expiring_lru_cache.cache_set(i, ExpiringValue { val: i * 2 });
}
group.bench_function("ExpiringLruCache hit (O(1))", |b| {
b.iter(|| {
let res = expiring_lru_cache.cache_get(black_box(&query_key));
black_box(res);
})
});
let mut ttl_sorted_cache = TtlSortedCache::builder()
.ttl(Duration::from_secs(3600))
.build();
for i in 0..limit {
let _ = ttl_sorted_cache.cache_set(i, i * 2);
}
group.bench_function("TtlSortedCache hit", |b| {
b.iter(|| {
let res = ttl_sorted_cache.cache_get(black_box(&query_key));
black_box(res);
})
});
let mut expiring_cache = ExpiringCache::new();
for i in 0..limit {
expiring_cache.cache_set(i, ExpiringValue { val: i * 2 });
}
group.bench_function("ExpiringCache hit (O(1))", |b| {
b.iter(|| {
let res = expiring_cache.cache_get(black_box(&query_key));
black_box(res);
})
});
group.finish();
}
fn bench_cache_misses_and_inserts(c: &mut Criterion) {
let mut group = c.benchmark_group("Cache Misses & Inserts");
group.bench_function("UnboundCache insert", |b| {
let mut cache = UnboundCache::new();
let mut key = 0;
b.iter(|| {
cache.cache_set(key, key * 2);
key += 1;
})
});
group.bench_function("LruCache insert (no eviction)", |b| {
let mut cache = LruCache::with_size(100_000);
let mut key = 0;
b.iter(|| {
cache.cache_set(key, key * 2);
key += 1;
})
});
group.bench_function("TtlCache insert (no eviction)", |b| {
let mut cache = TtlCache::builder().ttl(Duration::from_secs(3600)).build();
let mut key = 0;
b.iter(|| {
cache.cache_set(key, key * 2);
key += 1;
})
});
group.bench_function("LruTtlCache insert (no eviction)", |b| {
let mut cache = LruTtlCache::builder()
.size(100_000)
.ttl(Duration::from_secs(3600))
.build();
let mut key = 0;
b.iter(|| {
cache.cache_set(key, key * 2);
key += 1;
})
});
group.bench_function("ExpiringCache insert", |b| {
let mut cache: ExpiringCache<usize, ExpiringValue> = ExpiringCache::new();
let mut key = 0;
b.iter(|| {
cache.cache_set(key, ExpiringValue { val: key * 2 });
key += 1;
})
});
group.finish();
}
fn bench_eviction_overhead(c: &mut Criterion) {
let mut group = c.benchmark_group("Eviction & Capacity Limits");
let capacity = 1000;
let mut lru = LruCache::with_size(capacity);
for i in 0..capacity {
lru.cache_set(i, i * 2);
}
let mut key = capacity;
group.bench_function("LruCache eviction overhead", |b| {
b.iter(|| {
lru.cache_set(key, key * 2);
key += 1;
})
});
let mut lru_ttl = LruTtlCache::builder()
.size(capacity)
.ttl(Duration::from_secs(3600))
.build();
for i in 0..capacity {
lru_ttl.cache_set(i, i * 2);
}
let mut key = capacity;
group.bench_function("LruTtlCache eviction overhead", |b| {
b.iter(|| {
lru_ttl.cache_set(key, key * 2);
key += 1;
})
});
group.finish();
}
fn bench_lock_synchronization(c: &mut Criterion) {
let mut group = c.benchmark_group("Lock Contention & Synchronization");
let limit = 1000;
let query_key = 500;
let unbound_lock = Arc::new(RwLock::new({
let mut cache = UnboundCache::new();
for i in 0..limit {
cache.cache_set(i, i * 2);
}
cache
}));
let unbound_lock_clone = unbound_lock.clone();
group.bench_function("RwLock UnboundCache write lock read", |b| {
b.iter(|| {
let mut cache = unbound_lock_clone.write();
let res = cache.cache_get(black_box(&query_key));
black_box(res);
})
});
let unbound_lock_clone = unbound_lock.clone();
group.bench_function("RwLock UnboundCache unsync read", |b| {
b.iter(|| {
let cache = unbound_lock_clone.read();
let res = cache.cache_get_read(black_box(&query_key));
black_box(res);
})
});
group.finish();
}
criterion_group!(
benches,
bench_cache_hits,
bench_cache_misses_and_inserts,
bench_eviction_overhead,
bench_lock_synchronization
);
criterion_main!(benches);