Skip to main content

velesdb_core/simd_native/dispatch/
euclidean.rs

1#[allow(unused_imports)] // simd_level used only on x86_64/aarch64 targets
2use super::{dot::dot_product_native, simd_level, SimdLevel};
3
4/// Squared L2 distance with runtime SIMD dispatch.
5///
6/// # Panics
7///
8/// Panics if `a.len() != b.len()`.
9#[allow(clippy::inline_always)]
10#[inline(always)]
11#[must_use]
12pub fn squared_l2_native(a: &[f32], b: &[f32]) -> f32 {
13    assert_eq!(a.len(), b.len(), "Vector dimensions must match");
14    match simd_level() {
15        #[cfg(target_arch = "x86_64")]
16        SimdLevel::Avx512 if a.len() >= 1024 => {
17            // SAFETY: AVX-512 8-acc squared-L2 kernel requires CPU feature + minimum dim.
18            // - Condition 1: `simd_level()` selected `Avx512` after runtime detection.
19            // SAFETY: 8-accumulator variant for very large dimensions (stride 128).
20            unsafe { crate::simd_native::squared_l2_avx512_8acc(a, b) }
21        }
22        #[cfg(target_arch = "x86_64")]
23        SimdLevel::Avx512 if a.len() >= 512 => {
24            // SAFETY: AVX-512 squared-L2 kernel requires CPU feature + minimum dim.
25            // - Condition 1: `simd_level()` selected `Avx512` after runtime detection.
26            // SAFETY: call specialized kernel for higher throughput.
27            unsafe { crate::simd_native::squared_l2_avx512_4acc(a, b) }
28        }
29        #[cfg(target_arch = "x86_64")]
30        SimdLevel::Avx512 => {
31            // SAFETY: AVX-512 squared-L2 kernel requires CPU feature.
32            // - Condition 1: `simd_level()` selected `Avx512` after runtime detection.
33            // SAFETY: call specialized kernel for higher throughput.
34            unsafe { crate::simd_native::squared_l2_avx512(a, b) }
35        }
36        #[cfg(target_arch = "x86_64")]
37        SimdLevel::Avx2 if a.len() >= 256 => {
38            // SAFETY: AVX2 squared-L2 kernel requires CPU feature + minimum dim.
39            // - Condition 1: `simd_level()` selected `Avx2` after runtime detection.
40            // SAFETY: call specialized kernel for higher throughput.
41            unsafe { crate::simd_native::squared_l2_avx2_4acc(a, b) }
42        }
43        #[cfg(target_arch = "x86_64")]
44        SimdLevel::Avx2 if a.len() >= 64 => {
45            // SAFETY: AVX2 squared-L2 kernel requires CPU feature + minimum dim.
46            // - Condition 1: `simd_level()` selected `Avx2` after runtime detection.
47            // SAFETY: call specialized kernel for higher throughput.
48            unsafe { crate::simd_native::squared_l2_avx2(a, b) }
49        }
50        #[cfg(target_arch = "x86_64")]
51        SimdLevel::Avx2 if a.len() >= 8 => {
52            // SAFETY: AVX2 squared-L2 kernel requires CPU feature + minimum dim.
53            // - Condition 1: `simd_level()` selected `Avx2` after runtime detection.
54            // SAFETY: call specialized kernel for higher throughput.
55            unsafe { crate::simd_native::squared_l2_avx2_1acc(a, b) }
56        }
57        #[cfg(target_arch = "aarch64")]
58        SimdLevel::Neon if a.len() >= 4 => crate::simd_native::squared_l2_neon(a, b),
59        _ => super::squared_l2_scalar(a, b),
60    }
61}
62
63/// Euclidean distance with runtime SIMD dispatch.
64#[allow(clippy::inline_always)]
65#[inline(always)]
66#[must_use]
67pub fn euclidean_native(a: &[f32], b: &[f32]) -> f32 {
68    squared_l2_native(a, b).sqrt()
69}
70
71/// L2 norm with runtime SIMD dispatch.
72#[allow(clippy::inline_always)]
73#[inline(always)]
74#[must_use]
75pub fn norm_native(v: &[f32]) -> f32 {
76    dot_product_native(v, v).sqrt()
77}
78
79/// In-place normalization with runtime SIMD dispatch.
80///
81/// F-07: The scaling phase now uses SIMD (AVX2/AVX-512) instead of a scalar loop.
82/// For 768D vectors, this is ~4-8x faster on the scaling phase.
83#[allow(clippy::inline_always)]
84#[inline(always)]
85pub fn normalize_inplace_native(v: &mut [f32]) {
86    let n = norm_native(v);
87    if n > 0.0 {
88        let inv_norm = 1.0 / n;
89        scale_inplace_native(v, inv_norm);
90    }
91}
92
93/// Scales all elements of a mutable slice by a constant factor using SIMD.
94///
95/// F-07: Replaces scalar `for x in v { *x *= factor }` with SIMD broadcast+mul.
96/// AVX-512 and AVX2 share the AVX2 kernel — sufficient throughput for `scale`
97/// because the operation is memory-bound on every microarchitecture we ship
98/// to. An AVX-512 variant becomes reachable now that `_mm512_storeu_ps` sits
99/// inside the workspace MSRV of 1.89, but is intentionally not written until
100/// a profile shows it would beat the AVX2 path.
101#[inline]
102fn scale_inplace_native(v: &mut [f32], factor: f32) {
103    match simd_level() {
104        #[cfg(target_arch = "x86_64")]
105        SimdLevel::Avx512 | SimdLevel::Avx2 if v.len() >= 8 => {
106            // SAFETY: AVX2 scale kernel requires CPU feature + minimum dim.
107            // - Condition 1: `simd_level()` confirmed AVX2+ after runtime detection.
108            // SAFETY: broadcast factor and multiply 8 floats per iteration.
109            unsafe { scale_inplace_avx2(v, factor) };
110        }
111        _ => {
112            for x in v.iter_mut() {
113                *x *= factor;
114            }
115        }
116    }
117}
118
119/// AVX2 in-place scale: `v[i] *= factor` for all elements.
120///
121/// # Safety
122///
123/// Caller must ensure CPU supports AVX2 (enforced by runtime detection).
124#[cfg(target_arch = "x86_64")]
125#[target_feature(enable = "avx2")]
126#[inline]
127unsafe fn scale_inplace_avx2(v: &mut [f32], factor: f32) {
128    use std::arch::x86_64::{_mm256_loadu_ps, _mm256_mul_ps, _mm256_set1_ps, _mm256_storeu_ps};
129
130    let len = v.len();
131    let simd_len = len / 8;
132    let ptr = v.as_mut_ptr();
133    let scale = _mm256_set1_ps(factor);
134
135    for i in 0..simd_len {
136        let offset = i * 8;
137        // SAFETY: offset + 8 <= simd_len * 8 <= len, so within bounds.
138        // `_mm256_loadu_ps` / `_mm256_storeu_ps` handle unaligned access.
139        let val = _mm256_loadu_ps(ptr.add(offset));
140        let scaled = _mm256_mul_ps(val, scale);
141        _mm256_storeu_ps(ptr.add(offset), scaled);
142    }
143
144    // Scalar remainder (0-7 elements)
145    let base = simd_len * 8;
146    for i in base..len {
147        // SAFETY: `i` is in range `base..len` where `base = simd_len * 8 <= len`,
148        // so `i < len` is guaranteed. Bounds check would be elided by LLVM anyway.
149        *v.get_unchecked_mut(i) *= factor;
150    }
151}
152
153// AVX-512 scale_inplace is intentionally not implemented — the AVX2 kernel
154// already saturates memory bandwidth on the typical embedding-size workload,
155// and AVX-512 CPUs hit it via the AVX2 dispatch arm above.
156
157/// Batch squared L2 distance with cross-platform multi-level prefetch hints.
158#[inline]
159#[must_use]
160pub fn batch_squared_l2_native(candidates: &[&[f32]], query: &[f32]) -> Vec<f32> {
161    super::batch_with_prefetch(candidates, query, squared_l2_native)
162}
163
164/// Batch euclidean distance with cross-platform multi-level prefetch hints.
165#[inline]
166#[must_use]
167pub fn batch_euclidean_native(candidates: &[&[f32]], query: &[f32]) -> Vec<f32> {
168    super::batch_with_prefetch(candidates, query, euclidean_native)
169}
170
171#[allow(unused_variables)] // dim used only on x86_64 for dimension-based dispatch
172pub(super) fn resolve_squared_l2(level: SimdLevel, dim: usize) -> fn(&[f32], &[f32]) -> f32 {
173    match level {
174        #[cfg(target_arch = "x86_64")]
175        SimdLevel::Avx512 if dim >= 1024 => {
176            |a, b| {
177                // SAFETY: Resolver emitted AVX-512 8-acc implementation for this dimension.
178                // - Condition 1: caller chose this function pointer via `resolve_squared_l2`.
179                // SAFETY: execute AVX-512 8-accumulator squared-L2 for very large dims.
180                unsafe { crate::simd_native::squared_l2_avx512_8acc(a, b) }
181            }
182        }
183        #[cfg(target_arch = "x86_64")]
184        SimdLevel::Avx512 if dim >= 512 => {
185            |a, b| {
186                // SAFETY: Resolver emitted AVX-512 implementation for this dimension.
187                // - Condition 1: caller chose this function pointer via `resolve_squared_l2`.
188                // SAFETY: execute AVX-512 specialized squared-L2 implementation.
189                unsafe { crate::simd_native::squared_l2_avx512_4acc(a, b) }
190            }
191        }
192        #[cfg(target_arch = "x86_64")]
193        SimdLevel::Avx512 => |a, b| {
194            // SAFETY: Resolver emitted AVX-512 implementation for this dimension.
195            // - Condition 1: caller chose this function pointer via `resolve_squared_l2`.
196            // SAFETY: execute AVX-512 specialized squared-L2 implementation.
197            unsafe { crate::simd_native::squared_l2_avx512(a, b) }
198        },
199        #[cfg(target_arch = "x86_64")]
200        SimdLevel::Avx2 if dim >= 256 => {
201            |a, b| {
202                // SAFETY: Resolver emitted AVX2 implementation for this dimension.
203                // - Condition 1: caller chose this function pointer via `resolve_squared_l2`.
204                // SAFETY: execute AVX2 specialized squared-L2 implementation.
205                unsafe { crate::simd_native::squared_l2_avx2_4acc(a, b) }
206            }
207        }
208        #[cfg(target_arch = "x86_64")]
209        SimdLevel::Avx2 if dim >= 64 => |a, b| {
210            // SAFETY: Resolver emitted AVX2 implementation for this dimension.
211            // - Condition 1: caller chose this function pointer via `resolve_squared_l2`.
212            // SAFETY: execute AVX2 specialized squared-L2 implementation.
213            unsafe { crate::simd_native::squared_l2_avx2(a, b) }
214        },
215        #[cfg(target_arch = "x86_64")]
216        SimdLevel::Avx2 if dim >= 8 => {
217            |a, b| {
218                // SAFETY: Resolver emitted AVX2 implementation for this dimension.
219                // - Condition 1: caller chose this function pointer via `resolve_squared_l2`.
220                // SAFETY: execute AVX2 specialized squared-L2 implementation.
221                unsafe { crate::simd_native::squared_l2_avx2_1acc(a, b) }
222            }
223        }
224        #[cfg(target_arch = "aarch64")]
225        SimdLevel::Neon if dim >= 4 => |a, b| crate::simd_native::squared_l2_neon(a, b),
226        _ => super::squared_l2_scalar,
227    }
228}