From 29d1ec1053694b1c81ec9ae8bb676fa9e084d891 Mon Sep 17 00:00:00 2001 From: John Suykerbuyk Date: Wed, 8 Apr 2026 17:28:59 -0600 Subject: [PATCH] perf: use vek32 SIMD for EuclideanDistance EuclideanDistance used a plain Go loop while CosineDistance already used vek32 with AVX2 SIMD acceleration. Replace the loop with vek32.Distance which provides the same SIMD optimization. Benchmark at 1536 dimensions (OpenAI default) on AMD Ryzen 7 7745HX: Before: EuclideanDistance 335 ns/op After: EuclideanDistance 47 ns/op (7.1x faster) Cosine: CosineDistance 80 ns/op (unchanged, already SIMD) Also adds BenchmarkEuclideanDistance to distance_test.go. --- distance.go | 9 +-------- distance_test.go | 9 +++++++++ 2 files changed, 10 insertions(+), 8 deletions(-) diff --git a/distance.go b/distance.go index 4ab7cad..86024dc 100644 --- a/distance.go +++ b/distance.go @@ -1,7 +1,6 @@ package hnsw import ( - "math" "reflect" "github.com/viterin/vek/vek32" @@ -17,13 +16,7 @@ func CosineDistance(a, b []float32) float32 { // EuclideanDistance computes the Euclidean distance between two vectors. func EuclideanDistance(a, b []float32) float32 { - // TODO: can we speedup with vek? - var sum float32 = 0 - for i := range a { - diff := a[i] - b[i] - sum += diff * diff - } - return float32(math.Sqrt(float64(sum))) + return vek32.Distance(a, b) } var distanceFuncs = map[string]DistanceFunc{ diff --git a/distance_test.go b/distance_test.go index e411ee5..11c2c94 100644 --- a/distance_test.go +++ b/distance_test.go @@ -30,6 +30,15 @@ func TestCosineSimilarity(t *testing.T) { require.InDelta(t, 0, CosineDistance(a, b), 0.000001) } +func BenchmarkEuclideanDistance(b *testing.B) { + v1 := randFloats(1536) + v2 := randFloats(1536) + b.ResetTimer() + for i := 0; i < b.N; i++ { + EuclideanDistance(v1, v2) + } +} + func BenchmarkCosineSimilarity(b *testing.B) { v1 := randFloats(1536) v2 := randFloats(1536)