Skip to content

Commit b046174

Browse files
committed
refactor(pipnn): make kernels architecture-neutral
1 parent 15b8220 commit b046174

4 files changed

Lines changed: 28 additions & 112 deletions

File tree

diskann-pipnn/src/leaf_kernel.rs

Lines changed: 12 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -6,20 +6,14 @@
66
//! Fused nearest-neighbor kernel for a leaf's lower dot-product matrix.
77
88
use diskann_vector::distance::Metric;
9-
#[cfg(target_arch = "x86_64")]
10-
use diskann_wide::{SIMDFloat, SIMDMask, SIMDSelect, SIMDVector};
9+
use diskann_wide::{Architecture, SIMDFloat, SIMDMask, SIMDSelect, SIMDVector};
1110

1211
/// Widest f32 SIMD lane count DiskANN dispatches to, used to size lane scratch.
13-
#[cfg(target_arch = "x86_64")]
1412
const MAX_LANES: usize = 16;
1513

16-
#[cfg(target_arch = "x86_64")]
1714
const L2: u8 = 0;
18-
#[cfg(target_arch = "x86_64")]
1915
const COSINE_NORMALIZED: u8 = 1;
20-
#[cfg(target_arch = "x86_64")]
2116
const INNER_PRODUCT: u8 = 2;
22-
#[cfg(target_arch = "x86_64")]
2317
const COSINE: u8 = 3;
2418

2519
/// One leaf-local neighbor and its metric distance.
@@ -238,11 +232,6 @@ struct LeafKernel<'a, 'o, 'w> {
238232
}
239233

240234
impl LeafKernel<'_, '_, '_> {
241-
fn run_scalar(self) {
242-
process_pairs_scalar(self.input, self.k, self.output, self.norms, self.worst);
243-
}
244-
245-
#[cfg(target_arch = "x86_64")]
246235
fn run_simd<F>(self, arch: F::Arch)
247236
where
248237
F: SIMDVector<Scalar = f32> + SIMDFloat + std::ops::Div<Output = F>,
@@ -268,7 +257,6 @@ impl LeafKernel<'_, '_, '_> {
268257
}
269258
}
270259

271-
#[cfg(target_arch = "x86_64")]
272260
fn run_fused<F, const SLOTS: usize>(self, arch: F::Arch)
273261
where
274262
F: SIMDVector<Scalar = f32> + SIMDFloat + std::ops::Div<Output = F>,
@@ -308,40 +296,20 @@ impl LeafKernel<'_, '_, '_> {
308296
}
309297
}
310298

311-
impl diskann_wide::arch::Target<diskann_wide::arch::Scalar, ()> for LeafKernel<'_, '_, '_> {
312-
#[inline(always)]
313-
fn run(self, _: diskann_wide::arch::Scalar) {
314-
self.run_scalar();
315-
}
316-
}
317-
318-
#[cfg(target_arch = "x86_64")]
319-
impl diskann_wide::arch::Target<diskann_wide::arch::x86_64::V3, ()> for LeafKernel<'_, '_, '_> {
320-
#[inline(always)]
321-
fn run(self, arch: diskann_wide::arch::x86_64::V3) {
322-
diskann_wide::alias!(F32x8 = <diskann_wide::arch::x86_64::V3>::f32x8);
323-
self.run_simd::<F32x8>(arch);
324-
}
325-
}
326-
327-
#[cfg(target_arch = "x86_64")]
328-
impl diskann_wide::arch::Target<diskann_wide::arch::x86_64::V4, ()> for LeafKernel<'_, '_, '_> {
329-
#[inline(always)]
330-
fn run(self, arch: diskann_wide::arch::x86_64::V4) {
331-
diskann_wide::alias!(F32x16 = <diskann_wide::arch::x86_64::V4>::f32x16);
332-
self.run_simd::<F32x16>(arch);
333-
}
334-
}
335-
336-
#[cfg(target_arch = "aarch64")]
337-
impl diskann_wide::arch::Target<diskann_wide::arch::aarch64::Neon, ()> for LeafKernel<'_, '_, '_> {
299+
impl<A> diskann_wide::arch::Target<A, ()> for LeafKernel<'_, '_, '_>
300+
where
301+
A: Architecture,
302+
A::f32x16: std::ops::Div<Output = A::f32x16>,
303+
<A::f32x16 as SIMDVector>::Mask: SIMDSelect<A::f32x16>,
304+
u64: From<<<<A::f32x16 as SIMDVector>::Mask as SIMDMask>::BitMask as SIMDMask>::Underlying>,
305+
{
338306
#[inline(always)]
339-
fn run(self, arch: diskann_wide::arch::aarch64::Neon) {
340-
let _scalar = arch.retarget();
341-
self.run_scalar();
307+
fn run(self, arch: A) {
308+
self.run_simd::<A::f32x16>(arch);
342309
}
343310
}
344311

312+
#[cfg(test)]
345313
fn process_pairs_scalar(
346314
input: LeafTopK<'_>,
347315
k: usize,
@@ -359,7 +327,6 @@ fn process_pairs_scalar(
359327
}
360328
}
361329

362-
#[cfg(target_arch = "x86_64")]
363330
/// Fused dual-endpoint scan for row widths without a specialized arm.
364331
///
365332
/// Identical structure to [`process_pairs_simd_fused`], with the slot count
@@ -462,7 +429,6 @@ fn process_pairs_simd_dynamic<F>(
462429
/// a chunk where neither endpoint can accept costs one branch. `SLOTS` is the
463430
/// per-row neighbor count, threaded as a const so the insert arm is selected at
464431
/// compile time.
465-
#[cfg(target_arch = "x86_64")]
466432
#[inline(never)]
467433
fn process_pairs_simd_fused<F, const METRIC: u8, const SLOTS: usize>(
468434
arch: F::Arch,
@@ -567,7 +533,6 @@ fn process_pairs_simd_fused<F, const METRIC: u8, const SLOTS: usize>(
567533
}
568534
}
569535

570-
#[cfg(target_arch = "x86_64")]
571536
const fn metric<const METRIC: u8>() -> Metric {
572537
match METRIC {
573538
L2 => Metric::L2,
@@ -589,7 +554,6 @@ const fn metric<const METRIC: u8>() -> Metric {
589554
/// # Safety
590555
///
591556
/// `base + slots` must be within the allocation behind `output`.
592-
#[cfg(target_arch = "x86_64")]
593557
#[inline(always)]
594558
unsafe fn insert_slots(
595559
output: *mut LeafNeighbor,
@@ -669,7 +633,6 @@ unsafe fn insert_slots(
669633
}
670634
}
671635

672-
#[cfg(target_arch = "x86_64")]
673636
#[inline(always)]
674637
fn pair_distances<F>(arch: F::Arch, metric: Metric, dot: F, row_norm: F, column_norm: F) -> F
675638
where
@@ -741,6 +704,7 @@ fn pair_distance(metric: Metric, dot: f32, row_norm: f32, column_norm: f32) -> f
741704
}
742705

743706
#[inline(always)]
707+
#[cfg(test)]
744708
fn insert_row(
745709
output: &mut [LeafNeighbor],
746710
worst: &mut [f32],

diskann-pipnn/src/leaf_kernel/tests.rs

Lines changed: 2 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@
44
*/
55

66
use super::*;
7-
use diskann_wide::arch::{Scalar, Target};
87

98
fn dots(metric: Metric, points: usize) -> Vec<f32> {
109
let mut dots = vec![f32::NAN; points * points];
@@ -39,7 +38,7 @@ fn norms(input: LeafTopK<'_>) -> Vec<f32> {
3938
}
4039

4140
#[test]
42-
fn scalar_target_matches_runtime_dispatch() {
41+
fn scalar_reference_matches_runtime_dispatch() {
4342
for metric in [
4443
Metric::L2,
4544
Metric::Cosine,
@@ -61,16 +60,7 @@ fn scalar_target_matches_runtime_dispatch() {
6160
let mut actual = vec![LeafNeighbor::default(); points * k];
6261
let mut worst = vec![f32::INFINITY; points];
6362
let norms = norms(input);
64-
<LeafKernel<'_, '_, '_> as Target<Scalar, ()>>::run(
65-
LeafKernel {
66-
input,
67-
k,
68-
output: &mut actual,
69-
norms: &norms,
70-
worst: &mut worst,
71-
},
72-
Scalar::new(),
73-
);
63+
process_pairs_scalar(input, k, &mut actual, &norms, &mut worst);
7464

7565
assert_eq!(actual, expected, "{metric:?}, n={points}, k={k}");
7666
}

diskann-pipnn/src/partition_kernel.rs

Lines changed: 12 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,7 @@
1010
//! positions; partition recursion and cluster ownership stay with the caller.
1111
1212
use diskann_vector::distance::Metric;
13-
#[cfg(target_arch = "x86_64")]
14-
use diskann_wide::{SIMDFloat, SIMDMask, SIMDPartialOrd, SIMDSelect, SIMDVector};
13+
use diskann_wide::{Architecture, SIMDFloat, SIMDMask, SIMDPartialOrd, SIMDSelect, SIMDVector};
1514

1615
/// Maximum number of leaders retained for one point.
1716
pub const MAX_PARTITION_FANOUT: usize = 16;
@@ -175,11 +174,6 @@ struct PartitionKernel<'a, 'o> {
175174
}
176175

177176
impl PartitionKernel<'_, '_> {
178-
fn run_scalar(self) {
179-
process_rows_scalar(self.input, self.fanout, self.output);
180-
}
181-
182-
#[cfg(target_arch = "x86_64")]
183177
fn run_simd<F>(self, arch: F::Arch)
184178
where
185179
F: SIMDVector<Scalar = f32> + SIMDFloat + std::ops::Div<Output = F>,
@@ -190,40 +184,20 @@ impl PartitionKernel<'_, '_> {
190184
}
191185
}
192186

193-
impl diskann_wide::arch::Target<diskann_wide::arch::Scalar, ()> for PartitionKernel<'_, '_> {
194-
#[inline(always)]
195-
fn run(self, _: diskann_wide::arch::Scalar) {
196-
self.run_scalar();
197-
}
198-
}
199-
200-
#[cfg(target_arch = "x86_64")]
201-
impl diskann_wide::arch::Target<diskann_wide::arch::x86_64::V3, ()> for PartitionKernel<'_, '_> {
202-
#[inline(always)]
203-
fn run(self, arch: diskann_wide::arch::x86_64::V3) {
204-
diskann_wide::alias!(F32x8 = <diskann_wide::arch::x86_64::V3>::f32x8);
205-
self.run_simd::<F32x8>(arch);
206-
}
207-
}
208-
209-
#[cfg(target_arch = "x86_64")]
210-
impl diskann_wide::arch::Target<diskann_wide::arch::x86_64::V4, ()> for PartitionKernel<'_, '_> {
211-
#[inline(always)]
212-
fn run(self, arch: diskann_wide::arch::x86_64::V4) {
213-
diskann_wide::alias!(F32x16 = <diskann_wide::arch::x86_64::V4>::f32x16);
214-
self.run_simd::<F32x16>(arch);
215-
}
216-
}
217-
218-
#[cfg(target_arch = "aarch64")]
219-
impl diskann_wide::arch::Target<diskann_wide::arch::aarch64::Neon, ()> for PartitionKernel<'_, '_> {
187+
impl<A> diskann_wide::arch::Target<A, ()> for PartitionKernel<'_, '_>
188+
where
189+
A: Architecture,
190+
A::f32x16: std::ops::Div<Output = A::f32x16>,
191+
<A::f32x16 as SIMDVector>::Mask: SIMDSelect<A::f32x16>,
192+
u64: From<<<<A::f32x16 as SIMDVector>::Mask as SIMDMask>::BitMask as SIMDMask>::Underlying>,
193+
{
220194
#[inline(always)]
221-
fn run(self, arch: diskann_wide::arch::aarch64::Neon) {
222-
let _scalar = arch.retarget();
223-
self.run_scalar();
195+
fn run(self, arch: A) {
196+
self.run_simd::<A::f32x16>(arch);
224197
}
225198
}
226199

200+
#[cfg(test)]
227201
fn process_rows_scalar(input: PartitionTopK<'_>, fanout: usize, output: &mut [u32]) {
228202
for (row_index, (dot_row, output_row)) in input
229203
.dots
@@ -246,7 +220,6 @@ fn process_rows_scalar(input: PartitionTopK<'_>, fanout: usize, output: &mut [u3
246220
}
247221
}
248222

249-
#[cfg(target_arch = "x86_64")]
250223
fn process_rows_simd<F>(arch: F::Arch, input: PartitionTopK<'_>, fanout: usize, output: &mut [u32])
251224
where
252225
F: SIMDVector<Scalar = f32> + SIMDFloat + std::ops::Div<Output = F>,
@@ -290,7 +263,6 @@ where
290263
}
291264
}
292265

293-
#[cfg(target_arch = "x86_64")]
294266
fn process_cosine<F>(
295267
arch: F::Arch,
296268
dots: &[f32],
@@ -315,7 +287,6 @@ fn process_cosine<F>(
315287
});
316288
}
317289

318-
#[cfg(target_arch = "x86_64")]
319290
fn process_unary<F, Transform>(
320291
arch: F::Arch,
321292
dots: &[f32],
@@ -342,7 +313,6 @@ fn process_unary<F, Transform>(
342313
}
343314
}
344315

345-
#[cfg(target_arch = "x86_64")]
346316
fn process_binary<F, Transform>(
347317
arch: F::Arch,
348318
dots: &[f32],
@@ -375,7 +345,6 @@ fn process_binary<F, Transform>(
375345
}
376346
}
377347

378-
#[cfg(target_arch = "x86_64")]
379348
fn insert_lanes<F>(distances: F, base: usize, top: &mut TopK, fanout: usize)
380349
where
381350
F: SIMDVector<Scalar = f32> + SIMDPartialOrd,
@@ -399,6 +368,7 @@ where
399368
}
400369

401370
#[inline(always)]
371+
#[cfg(test)]
402372
fn distance(metric: Metric, dot: f32, row_scale: f32, leader_scale: f32) -> f32 {
403373
match metric {
404374
Metric::L2 => (-2.0f32).mul_add(dot, leader_scale),

diskann-pipnn/src/partition_kernel/tests.rs

Lines changed: 2 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@
44
*/
55

66
use super::*;
7-
use diskann_wide::arch::{Scalar, Target};
87

98
fn input(metric: Metric, leaders: usize) -> (Vec<f32>, Vec<f32>, Vec<f32>) {
109
let dots = (0..2 * leaders)
@@ -32,7 +31,7 @@ fn input(metric: Metric, leaders: usize) -> (Vec<f32>, Vec<f32>, Vec<f32>) {
3231
}
3332

3433
#[test]
35-
fn scalar_target_matches_runtime_dispatch() {
34+
fn scalar_reference_matches_runtime_dispatch() {
3635
for metric in [
3736
Metric::L2,
3837
Metric::Cosine,
@@ -54,14 +53,7 @@ fn scalar_target_matches_runtime_dispatch() {
5453
nearest_leaders(input, fanout, &mut expected).unwrap();
5554

5655
let mut actual = vec![u32::MAX; input.rows * fanout];
57-
<PartitionKernel<'_, '_> as Target<Scalar, ()>>::run(
58-
PartitionKernel {
59-
input,
60-
fanout,
61-
output: &mut actual,
62-
},
63-
Scalar::new(),
64-
);
56+
process_rows_scalar(input, fanout, &mut actual);
6557

6658
assert_eq!(
6759
actual, expected,

0 commit comments

Comments
 (0)