velesdb_core/simd_native/dispatch/hamming.rs
1#[cfg(target_arch = "x86_64")]
2use super::has_avx512vpopcntdq;
3use super::{simd_level, SimdLevel};
4
5/// Hamming distance with runtime SIMD dispatch.
6///
7/// # Panics
8///
9/// Panics if `a.len() != b.len()`.
10#[inline]
11#[must_use]
12pub fn hamming_distance_native(a: &[f32], b: &[f32]) -> f32 {
13 assert_eq!(
14 a.len(),
15 b.len(),
16 "Vector length mismatch: {} vs {}",
17 a.len(),
18 b.len()
19 );
20 hamming_simd(a, b)
21}
22
23/// Jaccard similarity with runtime SIMD dispatch.
24///
25/// # Panics
26///
27/// Panics if `a.len() != b.len()`.
28#[inline]
29#[must_use]
30pub fn jaccard_similarity_native(a: &[f32], b: &[f32]) -> f32 {
31 assert_eq!(
32 a.len(),
33 b.len(),
34 "Vector length mismatch: {} vs {}",
35 a.len(),
36 b.len()
37 );
38 jaccard_simd(a, b)
39}
40
41/// F-08: Use cached `simd_level()` (OnceLock) instead of per-call `is_x86_feature_detected!`
42/// for consistency with dot/cosine/euclidean dispatch paths.
43#[inline]
44fn hamming_simd(a: &[f32], b: &[f32]) -> f32 {
45 match simd_level() {
46 #[cfg(target_arch = "x86_64")]
47 SimdLevel::Avx512 if a.len() >= 512 => {
48 // SAFETY: AVX-512 4-acc hamming kernel requires CPU feature + minimum dim.
49 // - Condition 1: `simd_level()` selected `Avx512` after runtime detection.
50 // SAFETY: 4-accumulator kernel for large vectors.
51 unsafe { crate::simd_native::hamming_avx512_4acc(a, b) }
52 }
53 #[cfg(target_arch = "x86_64")]
54 SimdLevel::Avx512 if a.len() >= 16 => {
55 // SAFETY: AVX-512 hamming kernel requires CPU feature + minimum dim.
56 // - Condition 1: `simd_level()` selected `Avx512` after runtime detection.
57 // SAFETY: call specialized kernel for higher throughput.
58 unsafe { crate::simd_native::hamming_avx512(a, b) }
59 }
60 #[cfg(target_arch = "x86_64")]
61 SimdLevel::Avx512 | SimdLevel::Avx2 if a.len() >= 8 => {
62 // SAFETY: AVX2 hamming kernel requires CPU feature + minimum dim.
63 // - Condition 1: `simd_level()` confirmed AVX2+ (Avx512 implies Avx2 support).
64 // SAFETY: fallthrough for Avx512 with short vectors that don't meet 16-element minimum.
65 unsafe { crate::simd_native::hamming_avx2(a, b) }
66 }
67 #[cfg(target_arch = "aarch64")]
68 SimdLevel::Neon if a.len() >= 4 => crate::simd_native::hamming_neon(a, b),
69 _ => crate::simd_native::scalar::hamming_scalar(a, b),
70 }
71}
72
73/// F-08: Use cached `simd_level()` for consistency with other metrics.
74#[inline]
75fn jaccard_simd(a: &[f32], b: &[f32]) -> f32 {
76 match simd_level() {
77 #[cfg(target_arch = "x86_64")]
78 SimdLevel::Avx512 if a.len() >= 1024 => {
79 // SAFETY: AVX-512 8-acc jaccard kernel requires CPU feature + minimum dim.
80 // - Condition 1: `simd_level()` selected `Avx512` after runtime detection.
81 // SAFETY: 8-accumulator kernel for very large vectors (>= 1024 dims).
82 unsafe { crate::simd_native::jaccard_avx512_8acc(a, b) }
83 }
84 #[cfg(target_arch = "x86_64")]
85 SimdLevel::Avx512 if a.len() >= 512 => {
86 // SAFETY: AVX-512 4-acc jaccard kernel requires CPU feature + minimum dim.
87 // - Condition 1: `simd_level()` selected `Avx512` after runtime detection.
88 // SAFETY: 4-accumulator kernel for large vectors.
89 unsafe { crate::simd_native::jaccard_avx512_4acc(a, b) }
90 }
91 #[cfg(target_arch = "x86_64")]
92 SimdLevel::Avx512 if a.len() >= 16 => {
93 // SAFETY: AVX-512 jaccard kernel requires CPU feature + minimum dim.
94 // - Condition 1: `simd_level()` selected `Avx512` after runtime detection.
95 // SAFETY: call specialized kernel for higher throughput.
96 unsafe { crate::simd_native::jaccard_avx512(a, b) }
97 }
98 #[cfg(target_arch = "x86_64")]
99 SimdLevel::Avx512 | SimdLevel::Avx2 if a.len() >= 8 => {
100 // SAFETY: AVX2 jaccard kernel requires CPU feature + minimum dim.
101 // - Condition 1: `simd_level()` confirmed AVX2+ (Avx512 implies Avx2 support).
102 // SAFETY: fallthrough for Avx512 with short vectors that don't meet 16-element minimum.
103 unsafe { crate::simd_native::jaccard_avx2(a, b) }
104 }
105 #[cfg(target_arch = "aarch64")]
106 SimdLevel::Neon if a.len() >= 4 => crate::simd_native::jaccard_neon(a, b),
107 _ => crate::simd_native::scalar::jaccard_scalar(a, b),
108 }
109}
110
111#[allow(unused_variables)]
112pub(super) fn resolve_hamming(level: SimdLevel, dim: usize) -> fn(&[f32], &[f32]) -> f32 {
113 match level {
114 #[cfg(target_arch = "x86_64")]
115 SimdLevel::Avx512 if dim >= 512 => {
116 |a, b| {
117 // SAFETY: Resolver emitted AVX-512 4-acc implementation for this dimension.
118 // - Condition 1: caller chose this function pointer via `resolve_hamming`.
119 // SAFETY: execute AVX-512 4-accumulator hamming for large vectors.
120 unsafe { crate::simd_native::hamming_avx512_4acc(a, b) }
121 }
122 }
123 #[cfg(target_arch = "x86_64")]
124 SimdLevel::Avx512 if dim >= 16 => {
125 |a, b| {
126 // SAFETY: Resolver emitted AVX-512 implementation for this dimension.
127 // - Condition 1: caller chose this function pointer via `resolve_hamming`.
128 // SAFETY: execute AVX-512 specialized hamming implementation.
129 unsafe { crate::simd_native::hamming_avx512(a, b) }
130 }
131 }
132 #[cfg(target_arch = "x86_64")]
133 SimdLevel::Avx512 | SimdLevel::Avx2 if dim >= 8 => |a, b| {
134 // SAFETY: Resolver emitted AVX2 implementation for this dimension.
135 // - Condition 1: caller chose this function pointer via `resolve_hamming`.
136 // SAFETY: execute AVX2 specialized hamming implementation (Avx512 implies Avx2).
137 unsafe { crate::simd_native::hamming_avx2(a, b) }
138 },
139 #[cfg(target_arch = "aarch64")]
140 SimdLevel::Neon if dim >= 4 => |a, b| crate::simd_native::hamming_neon(a, b),
141 _ => crate::simd_native::scalar::hamming_scalar,
142 }
143}
144
145#[allow(unused_variables)]
146pub(super) fn resolve_jaccard(level: SimdLevel, dim: usize) -> fn(&[f32], &[f32]) -> f32 {
147 match level {
148 #[cfg(target_arch = "x86_64")]
149 SimdLevel::Avx512 if dim >= 1024 => {
150 |a, b| {
151 // SAFETY: Resolver emitted AVX-512 8-acc implementation for this dimension.
152 // - Condition 1: caller chose this function pointer via `resolve_jaccard`.
153 // SAFETY: execute AVX-512 8-accumulator jaccard for very large vectors.
154 unsafe { crate::simd_native::jaccard_avx512_8acc(a, b) }
155 }
156 }
157 #[cfg(target_arch = "x86_64")]
158 SimdLevel::Avx512 if dim >= 512 => {
159 |a, b| {
160 // SAFETY: Resolver emitted AVX-512 4-acc implementation for this dimension.
161 // - Condition 1: caller chose this function pointer via `resolve_jaccard`.
162 // SAFETY: execute AVX-512 4-accumulator jaccard for large vectors.
163 unsafe { crate::simd_native::jaccard_avx512_4acc(a, b) }
164 }
165 }
166 #[cfg(target_arch = "x86_64")]
167 SimdLevel::Avx512 if dim >= 16 => {
168 |a, b| {
169 // SAFETY: Resolver emitted AVX-512 implementation for this dimension.
170 // - Condition 1: caller chose this function pointer via `resolve_jaccard`.
171 // SAFETY: execute AVX-512 specialized jaccard implementation.
172 unsafe { crate::simd_native::jaccard_avx512(a, b) }
173 }
174 }
175 #[cfg(target_arch = "x86_64")]
176 SimdLevel::Avx512 | SimdLevel::Avx2 if dim >= 8 => |a, b| {
177 // SAFETY: Resolver emitted AVX2 implementation for this dimension.
178 // - Condition 1: caller chose this function pointer via `resolve_jaccard`.
179 // SAFETY: execute AVX2 specialized jaccard implementation (Avx512 implies Avx2).
180 unsafe { crate::simd_native::jaccard_avx2(a, b) }
181 },
182 #[cfg(target_arch = "aarch64")]
183 SimdLevel::Neon if dim >= 4 => |a, b| crate::simd_native::jaccard_neon(a, b),
184 _ => crate::simd_native::scalar::jaccard_scalar,
185 }
186}
187
188// =============================================================================
189// Binary Hamming distance (packed u64)
190// =============================================================================
191
192/// Binary Hamming distance with runtime SIMD dispatch.
193///
194/// Operates on packed u64 binary vectors where each bit is a dimension.
195/// XOR + popcount gives the number of differing bits.
196///
197/// # Panics
198///
199/// Panics if `a.len() != b.len()`.
200#[inline]
201#[must_use]
202#[allow(clippy::cast_possible_truncation)]
203// SAFETY: return type is u32; max bits = len * 64 which fits in u32 for any
204// practical binary vector dimension (up to ~67M words).
205pub fn hamming_binary_native(a: &[u64], b: &[u64]) -> u32 {
206 assert_eq!(
207 a.len(),
208 b.len(),
209 "Binary vector length mismatch: {} vs {}",
210 a.len(),
211 b.len()
212 );
213
214 match simd_level() {
215 #[cfg(target_arch = "x86_64")]
216 SimdLevel::Avx512 if a.len() >= 8 && has_avx512vpopcntdq() => {
217 // SAFETY: AVX-512 VPOPCNTDQ binary hamming uses native _mm512_popcnt_epi64.
218 // - Condition 1: `simd_level()` selected `Avx512` after runtime detection.
219 // - Condition 2: `has_avx512vpopcntdq()` confirms hardware popcount support.
220 // SAFETY: native 64-bit popcount avoids extract+scalar loop.
221 unsafe { crate::simd_native::hamming_binary_avx512_vpopcntdq(a, b) }
222 }
223 #[cfg(target_arch = "x86_64")]
224 SimdLevel::Avx512 if a.len() >= 8 => {
225 // SAFETY: AVX-512 binary hamming kernel requires CPU feature + minimum dim.
226 // - Condition 1: `simd_level()` selected `Avx512` after runtime detection.
227 // SAFETY: AVX-512 XOR on 8 packed u64 per iteration for higher throughput.
228 unsafe { crate::simd_native::hamming_binary_avx512(a, b) }
229 }
230 #[cfg(target_arch = "x86_64")]
231 SimdLevel::Avx512 | SimdLevel::Avx2 if a.len() >= 4 => {
232 // SAFETY: AVX2 binary hamming kernel requires CPU feature + minimum dim.
233 // - Condition 1: `simd_level()` confirmed AVX2+ (Avx512 implies Avx2 support).
234 // SAFETY: AVX2 XOR on 4 packed u64 per iteration for higher throughput.
235 unsafe { crate::simd_native::hamming_binary_avx2(a, b) }
236 }
237 #[cfg(target_arch = "aarch64")]
238 SimdLevel::Neon if a.len() >= 2 => {
239 // NEON binary hamming uses vcntq_u8 for byte-popcount on 2 u64 per iteration.
240 crate::simd_native::hamming_binary_neon(a, b)
241 }
242 _ => crate::simd_native::scalar::hamming_binary_scalar(a, b),
243 }
244}
245
246// =============================================================================
247// Batch operations with prefetch (Phase 4)
248// =============================================================================
249
250/// Batch Hamming distance with cross-platform multi-level prefetch hints.
251///
252/// Computes Hamming distance between each candidate and the query vector,
253/// using software prefetching for better cache utilization on large batches.
254#[inline]
255#[must_use]
256pub fn batch_hamming_native(candidates: &[&[f32]], query: &[f32]) -> Vec<f32> {
257 super::batch_with_prefetch(candidates, query, hamming_distance_native)
258}
259
260/// Batch Jaccard similarity with cross-platform multi-level prefetch hints.
261///
262/// Computes Jaccard similarity between each candidate and the query vector,
263/// using software prefetching for better cache utilization on large batches.
264#[inline]
265#[must_use]
266pub fn batch_jaccard_native(candidates: &[&[f32]], query: &[f32]) -> Vec<f32> {
267 super::batch_with_prefetch(candidates, query, jaccard_similarity_native)
268}