nodedb_vector/distance/simd/
bbq.rs1pub(super) fn recon_scale(residual_norm: f32, dim: usize) -> f32 {
19 if dim > 0 {
20 residual_norm / (dim as f32).sqrt()
21 } else {
22 0.0
23 }
24}
25
26#[inline]
28pub(super) fn centered_at(centered: &[u8], i: usize) -> f32 {
29 let b = ¢ered[i * 4..i * 4 + 4];
30 f32::from_le_bytes([b[0], b[1], b[2], b[3]])
31}
32
33pub(super) fn assert_payload_shapes(centered: &[u8], packed: &[u8], dim: usize) {
38 let need_centered = dim.saturating_mul(4);
41 let need_packed = dim.div_ceil(8);
42 assert!(
43 centered.len() >= need_centered,
44 "bbq l2: centered payload too short: {} bytes, need {need_centered} for dim {dim}",
45 centered.len()
46 );
47 assert!(
48 packed.len() >= need_packed,
49 "bbq l2: packed bits too short: {} bytes, need {need_packed} for dim {dim}",
50 packed.len()
51 );
52}
53
54pub fn l2_bbq(centered: &[u8], packed: &[u8], residual_norm: f32, dim: usize) -> f32 {
56 assert_payload_shapes(centered, packed, dim);
57 let scale = recon_scale(residual_norm, dim);
58 l2_scalar_from_bytes(centered, packed, scale, 0, dim).sqrt()
59}
60
61#[inline]
64pub(super) fn l2_scalar_from_bytes(
65 centered: &[u8],
66 packed: &[u8],
67 scale: f32,
68 from: usize,
69 dim: usize,
70) -> f32 {
71 let mut acc = 0.0f32;
72 for i in from..dim {
73 let bit = (packed[i / 8] >> (7 - (i % 8))) & 1;
74 let recon = if bit != 0 { scale } else { -scale };
75 let d = centered_at(centered, i) - recon;
76 acc += d * d;
77 }
78 acc
79}
80
81#[cfg(test)]
82mod tests {
83 use super::*;
84
85 const PARITY_REL: f64 = 1e-4;
88 const PARITY_ABS: f64 = 1e-6;
89
90 const DIMS: [usize; 30] = [
94 0, 1, 3, 7, 8, 9, 15, 16, 17, 31, 32, 33, 63, 64, 65, 100, 127, 128, 129, 191, 255, 256,
95 257, 383, 384, 511, 512, 513, 767, 768,
96 ];
97
98 fn reference_l2(centered: &[u8], packed: &[u8], residual_norm: f32, dim: usize) -> f64 {
101 let scale = recon_scale(residual_norm, dim) as f64;
102 let mut acc = 0.0f64;
103 for i in 0..dim {
104 let bit = (packed[i / 8] >> (7 - (i % 8))) & 1;
105 let recon = if bit != 0 { scale } else { -scale };
106 let d = centered_at(centered, i) as f64 - recon;
107 acc += d * d;
108 }
109 acc.sqrt()
110 }
111
112 fn within_parity(got: f32, expected: f64) -> bool {
113 ((got as f64) - expected).abs() <= PARITY_ABS.max(PARITY_REL * expected.abs())
114 }
115
116 fn sample(dim: usize, seed: u64) -> (Vec<u8>, Vec<u8>, f32) {
118 let mut state = seed.wrapping_add(0x9E37_79B9_7F4A_7C15);
119 let mut next = || {
120 state ^= state << 13;
121 state ^= state >> 7;
122 state ^= state << 17;
123 state
124 };
125 let lanes: Vec<f32> = (0..dim.max(1))
126 .map(|_| ((next() % 2000) as f32 / 1000.0) - 1.0)
127 .collect();
128 let mut centered = Vec::with_capacity(dim * 4);
129 for x in &lanes[..dim] {
130 centered.extend_from_slice(&x.to_le_bytes());
131 }
132 let packed: Vec<u8> = (0..dim.max(1).div_ceil(8))
133 .map(|_| (next() % 256) as u8)
134 .collect();
135 let residual_norm = ((next() % 1000) as f32 / 100.0) + 1.0;
136 (centered, packed, residual_norm)
137 }
138
139 #[test]
140 fn the_scalar_kernel_always_matches_the_reference() {
141 for dim in DIMS {
142 let (centered, packed, residual_norm) = sample(dim, 7 + dim as u64);
143 let expected = reference_l2(¢ered, &packed, residual_norm, dim);
144 let got = l2_bbq(¢ered, &packed, residual_norm, dim);
145 assert!(
146 within_parity(got, expected),
147 "scalar dim {dim}: {got} != reference {expected}"
148 );
149 }
150 }
151
152 #[test]
155 #[should_panic(expected = "centered payload too short")]
156 fn short_centered_payload_is_rejected() {
157 let _ = l2_bbq(&[], &[0u8; 2], 1.0, 16);
158 }
159
160 #[test]
161 #[should_panic(expected = "packed bits too short")]
162 fn short_packed_payload_is_rejected() {
163 let centered = vec![0u8; 16 * 4];
164 let _ = l2_bbq(¢ered, &[], 1.0, 16);
165 }
166
167 #[test]
168 fn dispatch_selects_a_tier_that_matches_the_reference() {
169 let (centered, packed, residual_norm) = sample(768, 99);
170 let expected = reference_l2(¢ered, &packed, residual_norm, 768);
171 let kernel = crate::distance::simd::runtime::runtime();
172 let got = (kernel.l2_bbq)(¢ered, &packed, residual_norm, 768);
173 assert!(
174 within_parity(got, expected),
175 "{}: {got} != reference {expected}",
176 kernel.name
177 );
178 }
179
180 #[cfg(all(target_arch = "x86_64", target_endian = "little"))]
184 mod x86 {
185 use super::*;
186
187 fn check_tier(name: &str, f: fn(&[u8], &[u8], f32, usize) -> f32) {
188 for dim in DIMS {
189 let (centered, packed, residual_norm) = sample(dim, 1234 + dim as u64);
190 let expected = reference_l2(¢ered, &packed, residual_norm, dim);
191 let got = f(¢ered, &packed, residual_norm, dim);
192 assert!(
193 within_parity(got, expected),
194 "{name} dim {dim}: {got} != reference {expected}"
195 );
196 let scalar = l2_bbq(¢ered, &packed, residual_norm, dim);
198 assert!(
199 within_parity(got, scalar as f64),
200 "{name} dim {dim}: {got} != scalar {scalar}"
201 );
202 }
203 }
204
205 #[test]
206 fn avx512_tier_matches_the_reference() {
207 if !std::is_x86_feature_detected!("avx512f") {
208 eprintln!("avx512 tier: skipped (no avx512f; run under sde64 -spr)");
209 return;
210 }
211 check_tier("avx512", crate::distance::simd::avx512::l2_bbq);
214 }
215
216 #[test]
217 fn avx2_tier_matches_the_reference() {
218 if !(std::is_x86_feature_detected!("avx2") && std::is_x86_feature_detected!("fma")) {
219 eprintln!("avx2 tier: skipped (no avx2+fma)");
220 return;
221 }
222 check_tier("avx2", crate::distance::simd::avx2::l2_bbq);
225 }
226
227 #[test]
231 #[should_panic(expected = "centered payload too short")]
232 fn avx512_rejects_empty_slices() {
233 let _ = crate::distance::simd::avx512::l2_bbq(&[], &[], 1.0, 16);
234 }
235
236 #[test]
237 #[should_panic(expected = "centered payload too short")]
238 fn avx2_rejects_empty_slices() {
239 let _ = crate::distance::simd::avx2::l2_bbq(&[], &[], 1.0, 16);
240 }
241 }
242
243 #[cfg(all(target_arch = "aarch64", target_endian = "little"))]
245 #[test]
246 fn neon_tier_matches_the_reference() {
247 let f = crate::distance::simd::neon::l2_bbq;
249 for dim in DIMS {
250 let (centered, packed, residual_norm) = sample(dim, 55 + dim as u64);
251 let expected = reference_l2(¢ered, &packed, residual_norm, dim);
252 let got = f(¢ered, &packed, residual_norm, dim);
253 assert!(
254 within_parity(got, expected),
255 "neon dim {dim}: {got} != reference {expected}"
256 );
257 }
258 }
259
260 #[cfg(all(target_arch = "aarch64", target_endian = "little"))]
261 #[test]
262 #[should_panic(expected = "centered payload too short")]
263 fn neon_rejects_empty_slices() {
264 let _ = crate::distance::simd::neon::l2_bbq(&[], &[], 1.0, 16);
265 }
266
267 #[cfg(all(target_arch = "wasm32", target_feature = "simd128"))]
268 #[test]
269 fn wasm_simd128_tier_matches_the_reference() {
270 for dim in DIMS {
271 let (centered, packed, residual_norm) = sample(dim, 55 + dim as u64);
272 let expected = reference_l2(¢ered, &packed, residual_norm, dim);
273 let got =
274 crate::distance::simd::wasm_simd128::l2_bbq(¢ered, &packed, residual_norm, dim);
275 assert!(
276 within_parity(got, expected),
277 "wasm dim {dim}: {got} != reference {expected}"
278 );
279 }
280 }
281}