use criterion::{criterion_group, criterion_main, Criterion, SamplingMode};
use edgevec::hnsw::{HnswConfig, HnswIndex, SearchContext};
use edgevec::quantization::binary::QuantizedVector;
use edgevec::quantization::ScalarQuantizer;
use edgevec::storage::VectorStorage;
use rand::{Rng, SeedableRng};
use rand_chacha::ChaCha8Rng;
use std::hint::black_box;
use std::time::Duration;
const SEED: u64 = 42;
const DIMS: usize = 768;
const INSERT_COUNT: usize = 1000;
const SEARCH_INDEX_SIZE: usize = 10_000;
const SEARCH_K: usize = 10;
fn generate_vectors(count: usize, dims: usize, seed: u64) -> Vec<Vec<f32>> {
let mut rng = ChaCha8Rng::seed_from_u64(seed);
(0..count)
.map(|_| (0..dims).map(|_| rng.gen_range(-1.0..1.0)).collect())
.collect()
}
fn generate_quantized_vector(seed: u64) -> QuantizedVector {
let mut rng = ChaCha8Rng::seed_from_u64(seed);
let mut data = [0u8; 96];
for byte in &mut data {
*byte = rng.gen();
}
QuantizedVector::from_bytes(data)
}
fn bench_insert_1k(c: &mut Criterion) {
let vectors = generate_vectors(INSERT_COUNT, DIMS, SEED);
let mut group = c.benchmark_group("validation");
group.sample_size(10); group.measurement_time(Duration::from_secs(10));
group.sampling_mode(SamplingMode::Flat);
group.bench_function("insert_1k", |b| {
b.iter(|| {
let config = HnswConfig::new(DIMS as u32);
let mut storage = VectorStorage::new(&config, None);
let mut index = HnswIndex::new(config, &storage).unwrap();
for v in &vectors {
index.insert(black_box(v), &mut storage).unwrap();
}
black_box(index)
});
});
group.finish();
}
fn bench_search_10k(c: &mut Criterion) {
let vectors = generate_vectors(SEARCH_INDEX_SIZE, DIMS, SEED);
let config = HnswConfig::new(DIMS as u32);
let mut storage = VectorStorage::new(&config, None);
let mut index = HnswIndex::new(config, &storage).unwrap();
for v in &vectors {
index.insert(v, &mut storage).unwrap();
}
let query = &vectors[0];
let mut group = c.benchmark_group("validation");
group.sample_size(100);
group.measurement_time(Duration::from_secs(5));
group.bench_function("search_10k", |b| {
let mut search_ctx = SearchContext::new();
b.iter(|| {
black_box(
index
.search_with_context(black_box(query), SEARCH_K, &storage, &mut search_ctx)
.unwrap(),
)
});
});
group.finish();
}
fn bench_quantization_encode(c: &mut Criterion) {
let mut rng = ChaCha8Rng::seed_from_u64(SEED);
let vector: Vec<f32> = (0..DIMS).map(|_| rng.gen_range(-10.0..10.0)).collect();
let batch = vec![vector.as_slice()];
let quantizer = ScalarQuantizer::train(&batch);
let mut group = c.benchmark_group("validation");
group.sample_size(1000);
group.measurement_time(Duration::from_secs(5));
group.bench_function("quantization_encode", |b| {
b.iter(|| black_box(quantizer.quantize(black_box(&vector))))
});
group.finish();
}
fn bench_hamming_distance(c: &mut Criterion) {
let q1 = generate_quantized_vector(SEED);
let q2 = generate_quantized_vector(SEED + 1);
let mut group = c.benchmark_group("validation");
group.sample_size(10000);
group.measurement_time(Duration::from_secs(5));
group.bench_function("hamming_distance", |b| {
b.iter(|| black_box(q1.hamming_distance(black_box(&q2))))
});
group.finish();
}
criterion_group! {
name = validation_benches;
config = Criterion::default()
.with_output_color(true)
.without_plots(); targets =
bench_insert_1k,
bench_search_10k,
bench_quantization_encode,
bench_hamming_distance
}
criterion_main!(validation_benches);