Files
wehub-resource-sync 70cb81e982
CI / Test (macos-latest, stable) (push) Waiting to run
CI / Test (ubuntu-latest, stable) (push) Waiting to run
CI / Test (windows-latest, stable) (push) Waiting to run
CI / Lint (push) Waiting to run
chore: import upstream snapshot with attribution
2026-07-13 12:45:24 +08:00

102 lines
3.2 KiB
Rust

//! Benchmark comparing SIMD vs Scalar L2 distance calculations.
//!
//! Run with: `cargo run --example simd_benchmark --features simd --release`
use std::hint::black_box;
use std::time::Instant;
fn main() {
let num_vectors = 10_000;
let dims = 384; // Standard embedding dimension
let num_iterations = 100;
println!("SIMD vs Scalar L2 Distance Benchmark");
println!("=====================================");
println!("Vectors: {}", num_vectors);
println!("Dimensions: {}", dims);
println!("Iterations: {}", num_iterations);
println!();
// Generate random vectors
let query: Vec<f32> = (0..dims).map(|i| (i as f32 * 0.001) % 1.0).collect();
let vectors: Vec<Vec<f32>> = (0..num_vectors)
.map(|v| (0..dims).map(|i| ((v + i) as f32 * 0.0017) % 1.0).collect())
.collect();
// Benchmark SIMD version
let simd_start = Instant::now();
let mut simd_sum = 0.0f32;
for _ in 0..num_iterations {
for vec in &vectors {
simd_sum += black_box(l2_distance_simd(black_box(&query), black_box(vec)));
}
}
let simd_elapsed = simd_start.elapsed();
black_box(simd_sum);
// Benchmark Scalar version
let scalar_start = Instant::now();
let mut scalar_sum = 0.0f32;
for _ in 0..num_iterations {
for vec in &vectors {
scalar_sum += black_box(l2_distance_scalar(black_box(&query), black_box(vec)));
}
}
let scalar_elapsed = scalar_start.elapsed();
black_box(scalar_sum);
// Results
let total_ops = num_vectors * num_iterations;
let simd_per_op_ns = simd_elapsed.as_nanos() as f64 / total_ops as f64;
let scalar_per_op_ns = scalar_elapsed.as_nanos() as f64 / total_ops as f64;
let speedup = scalar_elapsed.as_nanos() as f64 / simd_elapsed.as_nanos() as f64;
println!("Results:");
println!("--------");
println!(
"SIMD: {:>8.2}ms total, {:>6.1}ns per distance",
simd_elapsed.as_secs_f64() * 1000.0,
simd_per_op_ns
);
println!(
"Scalar: {:>8.2}ms total, {:>6.1}ns per distance",
scalar_elapsed.as_secs_f64() * 1000.0,
scalar_per_op_ns
);
println!();
println!("Speedup: {:.2}x", speedup);
// Verify correctness
let simd_result = l2_distance_simd(&query, &vectors[0]);
let scalar_result = l2_distance_scalar(&query, &vectors[0]);
let diff = (simd_result - scalar_result).abs();
println!();
println!("Correctness check:");
println!(" SIMD result: {:.8}", simd_result);
println!(" Scalar result: {:.8}", scalar_result);
println!(" Difference: {:.2e} (should be < 1e-5)", diff);
assert!(diff < 1e-4, "Results differ too much!");
println!(" ✓ Results match!");
}
/// SIMD L2 distance using the wide crate
#[cfg(feature = "simd")]
fn l2_distance_simd(a: &[f32], b: &[f32]) -> f32 {
memvid_core::simd::l2_distance_simd(a, b)
}
#[cfg(not(feature = "simd"))]
fn l2_distance_simd(a: &[f32], b: &[f32]) -> f32 {
l2_distance_scalar(a, b)
}
/// Scalar L2 distance (the OLD implementation)
#[inline(never)]
fn l2_distance_scalar(a: &[f32], b: &[f32]) -> f32 {
a.iter()
.zip(b.iter())
.map(|(x, y)| (x - y).powi(2))
.sum::<f32>()
.sqrt()
}