use criterion::{black_box, criterion_group, criterion_main, BatchSize, Criterion, Throughput};
use embedvec::persistence::{create_backend, BackendConfig, BackendType, PersistenceBackend};
use rand::Rng;
use tempfile::TempDir;
const VALUE_LEN: usize = 200;
fn bench_n() -> usize {
std::env::var("EMBEDVEC_BENCH_N")
.ok()
.and_then(|s| s.parse().ok())
.unwrap_or(10_000)
}
fn enabled_backends() -> Vec<(&'static str, BackendType)> {
#[allow(unused_mut)]
let mut backends: Vec<(&'static str, BackendType)> = Vec::new();
#[cfg(feature = "persistence-fjall")]
backends.push(("fjall", BackendType::Fjall));
#[cfg(feature = "persistence-sled")]
backends.push(("sled", BackendType::Sled));
#[cfg(feature = "persistence-rocksdb")]
backends.push(("rocksdb", BackendType::RocksDb));
backends
}
fn make_pairs(n: usize) -> Vec<(Vec<u8>, Vec<u8>)> {
let mut rng = rand::thread_rng();
(0..n)
.map(|i| {
let key = format!("vec:{:08}", i).into_bytes();
let value: Vec<u8> = (0..VALUE_LEN).map(|_| rng.gen::<u8>()).collect();
(key, value)
})
.collect()
}
fn open_backend(backend_type: BackendType, dir: &TempDir) -> Box<dyn PersistenceBackend> {
let config =
BackendConfig::new(dir.path().to_string_lossy().to_string()).backend(backend_type);
create_backend(&config).expect("failed to open backend")
}
fn warm_store(
backend_type: BackendType,
pairs: &[(Vec<u8>, Vec<u8>)],
) -> (TempDir, Box<dyn PersistenceBackend>) {
let dir = TempDir::new().unwrap();
let backend = open_backend(backend_type, &dir);
backend.set_batch(pairs).unwrap();
backend.flush().unwrap();
(dir, backend)
}
fn bench_insert_single(c: &mut Criterion) {
let n = bench_n();
let pairs = make_pairs(n);
let mut group = c.benchmark_group("backend_insert_single");
group.sample_size(10);
group.throughput(Throughput::Elements(n as u64));
for (label, backend_type) in enabled_backends() {
group.bench_function(label, |b| {
b.iter_batched(
|| {
let dir = TempDir::new().unwrap();
let backend = open_backend(backend_type, &dir);
(dir, backend)
},
|(dir, backend)| {
for (k, v) in &pairs {
backend.set(k, v).unwrap();
}
backend.flush().unwrap();
(dir, backend) },
BatchSize::PerIteration,
);
});
}
group.finish();
}
fn bench_insert_batch(c: &mut Criterion) {
let n = bench_n();
let pairs = make_pairs(n);
let mut group = c.benchmark_group("backend_insert_batch");
group.sample_size(10);
group.throughput(Throughput::Elements(n as u64));
for (label, backend_type) in enabled_backends() {
group.bench_function(label, |b| {
b.iter_batched(
|| {
let dir = TempDir::new().unwrap();
let backend = open_backend(backend_type, &dir);
(dir, backend)
},
|(dir, backend)| {
backend.set_batch(&pairs).unwrap();
backend.flush().unwrap();
(dir, backend)
},
BatchSize::PerIteration,
);
});
}
group.finish();
}
fn bench_get(c: &mut Criterion) {
let n = bench_n();
let pairs = make_pairs(n);
let mut group = c.benchmark_group("backend_get");
for (label, backend_type) in enabled_backends() {
let (_dir, backend) = warm_store(backend_type, &pairs);
let mut rng = rand::thread_rng();
group.bench_function(label, |b| {
b.iter(|| {
let idx = rng.gen_range(0..n);
black_box(backend.get(&pairs[idx].0).unwrap());
});
});
}
group.finish();
}
fn bench_scan_prefix(c: &mut Criterion) {
let n = bench_n();
let pairs = make_pairs(n);
let mut group = c.benchmark_group("backend_scan_prefix");
group.sample_size(20);
group.throughput(Throughput::Elements(n as u64));
for (label, backend_type) in enabled_backends() {
let (_dir, backend) = warm_store(backend_type, &pairs);
group.bench_function(label, |b| {
b.iter(|| {
black_box(backend.scan_prefix(b"vec:").unwrap().len());
});
});
}
group.finish();
}
criterion_group!(
benches,
bench_insert_single,
bench_insert_batch,
bench_get,
bench_scan_prefix
);
criterion_main!(benches);