mirror of
https://github.com/qdrant/qdrant.git
synced 2026-08-01 07:30:54 -05:00
* Benches: use SmallRng instead of ChaCha12-based generators All benchmarks used StdRng or rand::rng() (ThreadRng), both backed by the ChaCha12 block cipher in rand 0.10. Benchmarks do not need crypto-strength randomness, and several draw random values inside the timed closure, so cipher work was included in the measurement itself. Switch every bench target to SmallRng (Xoshiro256++), and key the HNSW graph cache and sparse index cache by RNG algorithm so stale caches built from the old generator are not reused against newly generated vectors. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * Benches: replace free-function rand::random with local SmallRng Addresses review: rand::random draws from the thread RNG (ChaCha12), including inside the timed loop of the pq score benchmark. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> --------- Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
132 lines
4.3 KiB
Rust
132 lines
4.3 KiB
Rust
use std::hint::black_box;
|
|
|
|
use criterion::{BenchmarkId, Criterion, criterion_group, criterion_main};
|
|
use quantization::DistanceType;
|
|
use quantization::encoded_vectors::VectorParameters;
|
|
use quantization::encoded_vectors_tq::{Metadata, new_turbo_quantizer_from_metadata};
|
|
use quantization::turboquant::quantization::TurboQuantizer;
|
|
use quantization::turboquant::{TQBits, TQMode, TQRotation};
|
|
use rand::prelude::SmallRng;
|
|
use rand::{RngExt, SeedableRng};
|
|
|
|
const DIMS: &[usize] = &[128, 384, 768, 1024, 1536, 4096];
|
|
|
|
fn make_tq(dim: usize, bits: TQBits) -> TurboQuantizer {
|
|
let metadata = Metadata {
|
|
vector_parameters: VectorParameters {
|
|
dim,
|
|
distance_type: DistanceType::Dot,
|
|
invert: false,
|
|
deprecated_count: None,
|
|
},
|
|
bits,
|
|
mode: TQMode::Normal,
|
|
error_correction: None,
|
|
rotation: TQRotation::Padded,
|
|
};
|
|
new_turbo_quantizer_from_metadata(&metadata).expect("metadata is hand-constructed")
|
|
}
|
|
|
|
fn random_vector(dim: usize, rng: &mut SmallRng) -> Vec<f32> {
|
|
(0..dim).map(|_| rng.random_range(-1.0..1.0)).collect()
|
|
}
|
|
|
|
fn bench_quantize(c: &mut Criterion) {
|
|
let bit_widths: &[(TQBits, &str)] = &[
|
|
(TQBits::Bits1, "1bit"),
|
|
(TQBits::Bits2, "2bit"),
|
|
(TQBits::Bits4, "4bit"),
|
|
];
|
|
|
|
for &(bits, bits_name) in bit_widths {
|
|
let mut group = c.benchmark_group(format!("turboquant_{bits_name}"));
|
|
|
|
for &dim in DIMS {
|
|
let tq = make_tq(dim, bits);
|
|
let mut rng = SmallRng::seed_from_u64(42);
|
|
|
|
group.bench_with_input(BenchmarkId::from_parameter(dim), &dim, |b, _| {
|
|
b.iter_batched(
|
|
|| (random_vector(dim, &mut rng), vec![0.0f64; dim]),
|
|
|(vec, mut buf)| tq.quantize(black_box(&vec), &mut buf),
|
|
criterion::BatchSize::SmallInput,
|
|
);
|
|
});
|
|
}
|
|
|
|
group.finish();
|
|
}
|
|
}
|
|
|
|
fn bench_dot(c: &mut Criterion) {
|
|
let bit_widths: &[(TQBits, &str)] = &[
|
|
(TQBits::Bits1, "1bit"),
|
|
(TQBits::Bits2, "2bit"),
|
|
(TQBits::Bits4, "4bit"),
|
|
];
|
|
|
|
for &(bits, bits_name) in bit_widths {
|
|
let mut group = c.benchmark_group(format!("turboquant_dot_{bits_name}"));
|
|
|
|
for &dim in DIMS {
|
|
let tq = make_tq(dim, bits);
|
|
let mut rng = SmallRng::seed_from_u64(42);
|
|
|
|
// Pre-quantize a vector so the bench measures only the dot path.
|
|
let mut buf = vec![0.0f64; dim];
|
|
let vec = random_vector(dim, &mut rng);
|
|
let packed = tq.quantize(&vec, &mut buf);
|
|
|
|
group.bench_with_input(BenchmarkId::from_parameter(dim), &dim, |b, _| {
|
|
b.iter_batched(
|
|
|| random_vector(dim, &mut rng),
|
|
|query| {
|
|
tq.score_precomputed(
|
|
black_box(&tq.precompute_query(&query)),
|
|
black_box(&packed),
|
|
)
|
|
},
|
|
criterion::BatchSize::SmallInput,
|
|
);
|
|
});
|
|
}
|
|
|
|
group.finish();
|
|
}
|
|
}
|
|
|
|
fn bench_dot_precomputed(c: &mut Criterion) {
|
|
let bit_widths: &[(TQBits, &str)] = &[
|
|
(TQBits::Bits1, "1bit"),
|
|
(TQBits::Bits2, "2bit"),
|
|
(TQBits::Bits4, "4bit"),
|
|
];
|
|
|
|
for &(bits, bits_name) in bit_widths {
|
|
let mut group = c.benchmark_group(format!("turboquant_dot_precomputed_{bits_name}"));
|
|
|
|
for &dim in DIMS {
|
|
let tq = make_tq(dim, bits);
|
|
let mut rng = SmallRng::seed_from_u64(42);
|
|
|
|
// Pre-quantize a vector so the bench measures only the dot path.
|
|
let mut buf = vec![0.0f64; dim];
|
|
let vec = random_vector(dim, &mut rng);
|
|
let packed = tq.quantize(&vec, &mut buf);
|
|
|
|
group.bench_with_input(BenchmarkId::from_parameter(dim), &dim, |b, _| {
|
|
b.iter_batched(
|
|
|| tq.precompute_query(&random_vector(dim, &mut rng)),
|
|
|query| tq.score_precomputed(black_box(&query), black_box(&packed)),
|
|
criterion::BatchSize::SmallInput,
|
|
);
|
|
});
|
|
}
|
|
|
|
group.finish();
|
|
}
|
|
}
|
|
|
|
criterion_group!(benches, bench_quantize, bench_dot, bench_dot_precomputed);
|
|
criterion_main!(benches);
|