numkong 7.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +201 -0
- package/README.md +495 -0
- package/binding.gyp +540 -0
- package/c/dispatch.h +512 -0
- package/c/dispatch_bf16.c +389 -0
- package/c/dispatch_bf16c.c +52 -0
- package/c/dispatch_e2m3.c +263 -0
- package/c/dispatch_e3m2.c +243 -0
- package/c/dispatch_e4m3.c +276 -0
- package/c/dispatch_e5m2.c +272 -0
- package/c/dispatch_f16.c +376 -0
- package/c/dispatch_f16c.c +58 -0
- package/c/dispatch_f32.c +378 -0
- package/c/dispatch_f32c.c +99 -0
- package/c/dispatch_f64.c +296 -0
- package/c/dispatch_f64c.c +98 -0
- package/c/dispatch_i16.c +96 -0
- package/c/dispatch_i32.c +89 -0
- package/c/dispatch_i4.c +150 -0
- package/c/dispatch_i64.c +86 -0
- package/c/dispatch_i8.c +289 -0
- package/c/dispatch_other.c +330 -0
- package/c/dispatch_u1.c +148 -0
- package/c/dispatch_u16.c +124 -0
- package/c/dispatch_u32.c +118 -0
- package/c/dispatch_u4.c +150 -0
- package/c/dispatch_u64.c +102 -0
- package/c/dispatch_u8.c +303 -0
- package/c/numkong.c +950 -0
- package/include/README.md +573 -0
- package/include/module.modulemap +129 -0
- package/include/numkong/attention/sapphireamx.h +1361 -0
- package/include/numkong/attention/sme.h +2066 -0
- package/include/numkong/attention.h +49 -0
- package/include/numkong/capabilities.h +748 -0
- package/include/numkong/cast/README.md +262 -0
- package/include/numkong/cast/haswell.h +975 -0
- package/include/numkong/cast/icelake.h +470 -0
- package/include/numkong/cast/neon.h +1192 -0
- package/include/numkong/cast/rvv.h +1021 -0
- package/include/numkong/cast/sapphire.h +262 -0
- package/include/numkong/cast/serial.h +2262 -0
- package/include/numkong/cast/skylake.h +856 -0
- package/include/numkong/cast/v128relaxed.h +180 -0
- package/include/numkong/cast.h +230 -0
- package/include/numkong/curved/README.md +223 -0
- package/include/numkong/curved/genoa.h +182 -0
- package/include/numkong/curved/haswell.h +276 -0
- package/include/numkong/curved/neon.h +205 -0
- package/include/numkong/curved/neonbfdot.h +212 -0
- package/include/numkong/curved/neonhalf.h +212 -0
- package/include/numkong/curved/rvv.h +305 -0
- package/include/numkong/curved/serial.h +207 -0
- package/include/numkong/curved/skylake.h +457 -0
- package/include/numkong/curved/smef64.h +506 -0
- package/include/numkong/curved.h +517 -0
- package/include/numkong/curved.hpp +144 -0
- package/include/numkong/dot/README.md +425 -0
- package/include/numkong/dot/alder.h +563 -0
- package/include/numkong/dot/genoa.h +315 -0
- package/include/numkong/dot/haswell.h +1688 -0
- package/include/numkong/dot/icelake.h +883 -0
- package/include/numkong/dot/neon.h +818 -0
- package/include/numkong/dot/neonbfdot.h +244 -0
- package/include/numkong/dot/neonfhm.h +360 -0
- package/include/numkong/dot/neonhalf.h +198 -0
- package/include/numkong/dot/neonsdot.h +508 -0
- package/include/numkong/dot/rvv.h +714 -0
- package/include/numkong/dot/rvvbb.h +72 -0
- package/include/numkong/dot/rvvbf16.h +123 -0
- package/include/numkong/dot/rvvhalf.h +129 -0
- package/include/numkong/dot/sapphire.h +141 -0
- package/include/numkong/dot/serial.h +838 -0
- package/include/numkong/dot/sierra.h +405 -0
- package/include/numkong/dot/skylake.h +1084 -0
- package/include/numkong/dot/sve.h +379 -0
- package/include/numkong/dot/svebfdot.h +74 -0
- package/include/numkong/dot/svehalf.h +123 -0
- package/include/numkong/dot/v128relaxed.h +1258 -0
- package/include/numkong/dot.h +1070 -0
- package/include/numkong/dot.hpp +94 -0
- package/include/numkong/dots/README.md +496 -0
- package/include/numkong/dots/alder.h +114 -0
- package/include/numkong/dots/genoa.h +94 -0
- package/include/numkong/dots/haswell.h +295 -0
- package/include/numkong/dots/icelake.h +171 -0
- package/include/numkong/dots/neon.h +120 -0
- package/include/numkong/dots/neonbfdot.h +58 -0
- package/include/numkong/dots/neonfhm.h +94 -0
- package/include/numkong/dots/neonhalf.h +57 -0
- package/include/numkong/dots/neonsdot.h +108 -0
- package/include/numkong/dots/rvv.h +2486 -0
- package/include/numkong/dots/sapphireamx.h +3973 -0
- package/include/numkong/dots/serial.h +2844 -0
- package/include/numkong/dots/sierra.h +97 -0
- package/include/numkong/dots/skylake.h +196 -0
- package/include/numkong/dots/sme.h +5372 -0
- package/include/numkong/dots/smebi32.h +461 -0
- package/include/numkong/dots/smef64.h +1318 -0
- package/include/numkong/dots/smehalf.h +47 -0
- package/include/numkong/dots/v128relaxed.h +294 -0
- package/include/numkong/dots.h +2804 -0
- package/include/numkong/dots.hpp +639 -0
- package/include/numkong/each/README.md +469 -0
- package/include/numkong/each/haswell.h +1658 -0
- package/include/numkong/each/icelake.h +272 -0
- package/include/numkong/each/neon.h +1104 -0
- package/include/numkong/each/neonbfdot.h +212 -0
- package/include/numkong/each/neonhalf.h +410 -0
- package/include/numkong/each/rvv.h +1121 -0
- package/include/numkong/each/sapphire.h +477 -0
- package/include/numkong/each/serial.h +260 -0
- package/include/numkong/each/skylake.h +1562 -0
- package/include/numkong/each.h +2146 -0
- package/include/numkong/each.hpp +434 -0
- package/include/numkong/geospatial/README.md +147 -0
- package/include/numkong/geospatial/haswell.h +593 -0
- package/include/numkong/geospatial/neon.h +571 -0
- package/include/numkong/geospatial/rvv.h +701 -0
- package/include/numkong/geospatial/serial.h +309 -0
- package/include/numkong/geospatial/skylake.h +577 -0
- package/include/numkong/geospatial/v128relaxed.h +613 -0
- package/include/numkong/geospatial.h +453 -0
- package/include/numkong/geospatial.hpp +235 -0
- package/include/numkong/matrix.hpp +336 -0
- package/include/numkong/maxsim/README.md +187 -0
- package/include/numkong/maxsim/alder.h +511 -0
- package/include/numkong/maxsim/genoa.h +115 -0
- package/include/numkong/maxsim/haswell.h +553 -0
- package/include/numkong/maxsim/icelake.h +480 -0
- package/include/numkong/maxsim/neonsdot.h +394 -0
- package/include/numkong/maxsim/sapphireamx.h +877 -0
- package/include/numkong/maxsim/serial.h +490 -0
- package/include/numkong/maxsim/sme.h +929 -0
- package/include/numkong/maxsim/v128relaxed.h +280 -0
- package/include/numkong/maxsim.h +571 -0
- package/include/numkong/maxsim.hpp +133 -0
- package/include/numkong/mesh/README.md +227 -0
- package/include/numkong/mesh/haswell.h +2235 -0
- package/include/numkong/mesh/neon.h +1329 -0
- package/include/numkong/mesh/neonbfdot.h +842 -0
- package/include/numkong/mesh/neonhalf.h +616 -0
- package/include/numkong/mesh/rvv.h +916 -0
- package/include/numkong/mesh/serial.h +742 -0
- package/include/numkong/mesh/skylake.h +1135 -0
- package/include/numkong/mesh/v128relaxed.h +1052 -0
- package/include/numkong/mesh.h +652 -0
- package/include/numkong/mesh.hpp +762 -0
- package/include/numkong/numkong.h +78 -0
- package/include/numkong/numkong.hpp +57 -0
- package/include/numkong/probability/README.md +173 -0
- package/include/numkong/probability/haswell.h +267 -0
- package/include/numkong/probability/neon.h +225 -0
- package/include/numkong/probability/rvv.h +409 -0
- package/include/numkong/probability/serial.h +169 -0
- package/include/numkong/probability/skylake.h +324 -0
- package/include/numkong/probability.h +383 -0
- package/include/numkong/probability.hpp +120 -0
- package/include/numkong/random.h +50 -0
- package/include/numkong/random.hpp +285 -0
- package/include/numkong/reduce/README.md +547 -0
- package/include/numkong/reduce/alder.h +632 -0
- package/include/numkong/reduce/genoa.h +201 -0
- package/include/numkong/reduce/haswell.h +3783 -0
- package/include/numkong/reduce/icelake.h +549 -0
- package/include/numkong/reduce/neon.h +3841 -0
- package/include/numkong/reduce/neonbfdot.h +353 -0
- package/include/numkong/reduce/neonfhm.h +665 -0
- package/include/numkong/reduce/neonhalf.h +157 -0
- package/include/numkong/reduce/neonsdot.h +357 -0
- package/include/numkong/reduce/rvv.h +3407 -0
- package/include/numkong/reduce/serial.h +757 -0
- package/include/numkong/reduce/sierra.h +338 -0
- package/include/numkong/reduce/skylake.h +3792 -0
- package/include/numkong/reduce/v128relaxed.h +2302 -0
- package/include/numkong/reduce.h +1597 -0
- package/include/numkong/reduce.hpp +633 -0
- package/include/numkong/scalar/README.md +89 -0
- package/include/numkong/scalar/haswell.h +113 -0
- package/include/numkong/scalar/neon.h +122 -0
- package/include/numkong/scalar/neonhalf.h +70 -0
- package/include/numkong/scalar/rvv.h +211 -0
- package/include/numkong/scalar/sapphire.h +63 -0
- package/include/numkong/scalar/serial.h +332 -0
- package/include/numkong/scalar/v128relaxed.h +56 -0
- package/include/numkong/scalar.h +683 -0
- package/include/numkong/set/README.md +179 -0
- package/include/numkong/set/haswell.h +334 -0
- package/include/numkong/set/icelake.h +485 -0
- package/include/numkong/set/neon.h +364 -0
- package/include/numkong/set/rvv.h +226 -0
- package/include/numkong/set/rvvbb.h +117 -0
- package/include/numkong/set/serial.h +174 -0
- package/include/numkong/set/sve.h +185 -0
- package/include/numkong/set/v128relaxed.h +240 -0
- package/include/numkong/set.h +457 -0
- package/include/numkong/set.hpp +114 -0
- package/include/numkong/sets/README.md +149 -0
- package/include/numkong/sets/haswell.h +63 -0
- package/include/numkong/sets/icelake.h +66 -0
- package/include/numkong/sets/neon.h +61 -0
- package/include/numkong/sets/serial.h +43 -0
- package/include/numkong/sets/smebi32.h +1099 -0
- package/include/numkong/sets/v128relaxed.h +58 -0
- package/include/numkong/sets.h +339 -0
- package/include/numkong/sparse/README.md +156 -0
- package/include/numkong/sparse/icelake.h +463 -0
- package/include/numkong/sparse/neon.h +288 -0
- package/include/numkong/sparse/serial.h +117 -0
- package/include/numkong/sparse/sve2.h +507 -0
- package/include/numkong/sparse/turin.h +322 -0
- package/include/numkong/sparse.h +363 -0
- package/include/numkong/sparse.hpp +113 -0
- package/include/numkong/spatial/README.md +435 -0
- package/include/numkong/spatial/alder.h +607 -0
- package/include/numkong/spatial/genoa.h +290 -0
- package/include/numkong/spatial/haswell.h +960 -0
- package/include/numkong/spatial/icelake.h +586 -0
- package/include/numkong/spatial/neon.h +773 -0
- package/include/numkong/spatial/neonbfdot.h +165 -0
- package/include/numkong/spatial/neonhalf.h +118 -0
- package/include/numkong/spatial/neonsdot.h +261 -0
- package/include/numkong/spatial/rvv.h +984 -0
- package/include/numkong/spatial/rvvbf16.h +123 -0
- package/include/numkong/spatial/rvvhalf.h +117 -0
- package/include/numkong/spatial/sapphire.h +343 -0
- package/include/numkong/spatial/serial.h +346 -0
- package/include/numkong/spatial/sierra.h +323 -0
- package/include/numkong/spatial/skylake.h +606 -0
- package/include/numkong/spatial/sve.h +224 -0
- package/include/numkong/spatial/svebfdot.h +122 -0
- package/include/numkong/spatial/svehalf.h +109 -0
- package/include/numkong/spatial/v128relaxed.h +717 -0
- package/include/numkong/spatial.h +1425 -0
- package/include/numkong/spatial.hpp +183 -0
- package/include/numkong/spatials/README.md +580 -0
- package/include/numkong/spatials/alder.h +94 -0
- package/include/numkong/spatials/genoa.h +94 -0
- package/include/numkong/spatials/haswell.h +219 -0
- package/include/numkong/spatials/icelake.h +113 -0
- package/include/numkong/spatials/neon.h +109 -0
- package/include/numkong/spatials/neonbfdot.h +60 -0
- package/include/numkong/spatials/neonfhm.h +92 -0
- package/include/numkong/spatials/neonhalf.h +58 -0
- package/include/numkong/spatials/neonsdot.h +109 -0
- package/include/numkong/spatials/rvv.h +1960 -0
- package/include/numkong/spatials/sapphireamx.h +1149 -0
- package/include/numkong/spatials/serial.h +226 -0
- package/include/numkong/spatials/sierra.h +96 -0
- package/include/numkong/spatials/skylake.h +184 -0
- package/include/numkong/spatials/sme.h +1901 -0
- package/include/numkong/spatials/smef64.h +465 -0
- package/include/numkong/spatials/v128relaxed.h +240 -0
- package/include/numkong/spatials.h +3021 -0
- package/include/numkong/spatials.hpp +508 -0
- package/include/numkong/tensor.hpp +1592 -0
- package/include/numkong/trigonometry/README.md +184 -0
- package/include/numkong/trigonometry/haswell.h +652 -0
- package/include/numkong/trigonometry/neon.h +639 -0
- package/include/numkong/trigonometry/rvv.h +699 -0
- package/include/numkong/trigonometry/serial.h +703 -0
- package/include/numkong/trigonometry/skylake.h +721 -0
- package/include/numkong/trigonometry/v128relaxed.h +666 -0
- package/include/numkong/trigonometry.h +467 -0
- package/include/numkong/trigonometry.hpp +166 -0
- package/include/numkong/types.h +1384 -0
- package/include/numkong/types.hpp +5603 -0
- package/include/numkong/vector.hpp +698 -0
- package/javascript/README.md +246 -0
- package/javascript/dist/cjs/numkong-wasm.d.ts +166 -0
- package/javascript/dist/cjs/numkong-wasm.js +617 -0
- package/javascript/dist/cjs/numkong.d.ts +343 -0
- package/javascript/dist/cjs/numkong.js +523 -0
- package/javascript/dist/cjs/package.json +3 -0
- package/javascript/dist/cjs/types.d.ts +284 -0
- package/javascript/dist/cjs/types.js +653 -0
- package/javascript/dist/esm/numkong-wasm.d.ts +166 -0
- package/javascript/dist/esm/numkong-wasm.js +595 -0
- package/javascript/dist/esm/numkong.d.ts +343 -0
- package/javascript/dist/esm/numkong.js +452 -0
- package/javascript/dist/esm/package.json +3 -0
- package/javascript/dist/esm/types.d.ts +284 -0
- package/javascript/dist/esm/types.js +630 -0
- package/javascript/dist-package-cjs.json +3 -0
- package/javascript/dist-package-esm.json +3 -0
- package/javascript/node-gyp-build.d.ts +1 -0
- package/javascript/numkong-wasm.ts +756 -0
- package/javascript/numkong.c +689 -0
- package/javascript/numkong.ts +575 -0
- package/javascript/tsconfig-base.json +39 -0
- package/javascript/tsconfig-cjs.json +8 -0
- package/javascript/tsconfig-esm.json +8 -0
- package/javascript/types.ts +674 -0
- package/package.json +87 -0
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @brief Batched Set Operations for WASM Relaxed SIMD.
|
|
3
|
+
* @file include/numkong/sets/v128relaxed.h
|
|
4
|
+
* @author Ash Vardanian
|
|
5
|
+
* @date March 10, 2026
|
|
6
|
+
*
|
|
7
|
+
* @sa include/numkong/sets.h
|
|
8
|
+
*/
|
|
9
|
+
#ifndef NK_SETS_V128RELAXED_H
|
|
10
|
+
#define NK_SETS_V128RELAXED_H
|
|
11
|
+
|
|
12
|
+
#if NK_TARGET_V128RELAXED
|
|
13
|
+
|
|
14
|
+
#include "numkong/set/v128relaxed.h"
|
|
15
|
+
#include "numkong/dots/v128relaxed.h"
|
|
16
|
+
|
|
17
|
+
#if defined(__cplusplus)
|
|
18
|
+
extern "C" {
|
|
19
|
+
#endif
|
|
20
|
+
|
|
21
|
+
#if defined(__clang__)
|
|
22
|
+
#pragma clang attribute push(__attribute__((target("relaxed-simd"))), apply_to = function)
|
|
23
|
+
#endif
|
|
24
|
+
|
|
25
|
+
nk_define_cross_normalized_packed_(hamming, u1, v128relaxed, u1x8, u1x8, u32, /*norm_value_type=*/u32, u32,
|
|
26
|
+
nk_b128_vec_t, nk_dots_packed_u1_v128relaxed, nk_hamming_u32x4_from_dot_v128relaxed_,
|
|
27
|
+
nk_dots_reduce_sum_u1_, nk_load_b128_v128relaxed_, nk_partial_load_b32x4_serial_,
|
|
28
|
+
nk_store_b128_v128relaxed_, nk_partial_store_b32x4_serial_,
|
|
29
|
+
/*dimensions_per_value=*/8)
|
|
30
|
+
|
|
31
|
+
nk_define_cross_normalized_packed_(jaccard, u1, v128relaxed, u1x8, u1x8, u32, /*norm_value_type=*/u32, f32,
|
|
32
|
+
nk_b128_vec_t, nk_dots_packed_u1_v128relaxed, nk_jaccard_f32x4_from_dot_v128relaxed_,
|
|
33
|
+
nk_dots_reduce_sum_u1_, nk_load_b128_v128relaxed_, nk_partial_load_b32x4_serial_,
|
|
34
|
+
nk_store_b128_v128relaxed_, nk_partial_store_b32x4_serial_,
|
|
35
|
+
/*dimensions_per_value=*/8)
|
|
36
|
+
|
|
37
|
+
nk_define_cross_normalized_symmetric_(hamming, u1, v128relaxed, u1x8, u32, /*norm_value_type=*/u32, u32, nk_b128_vec_t,
|
|
38
|
+
nk_dots_symmetric_u1_v128relaxed, nk_hamming_u32x4_from_dot_v128relaxed_,
|
|
39
|
+
nk_dots_reduce_sum_u1_, nk_load_b128_v128relaxed_, nk_partial_load_b32x4_serial_,
|
|
40
|
+
nk_store_b128_v128relaxed_, nk_partial_store_b32x4_serial_,
|
|
41
|
+
/*dimensions_per_value=*/8)
|
|
42
|
+
|
|
43
|
+
nk_define_cross_normalized_symmetric_(jaccard, u1, v128relaxed, u1x8, u32, /*norm_value_type=*/u32, f32, nk_b128_vec_t,
|
|
44
|
+
nk_dots_symmetric_u1_v128relaxed, nk_jaccard_f32x4_from_dot_v128relaxed_,
|
|
45
|
+
nk_dots_reduce_sum_u1_, nk_load_b128_v128relaxed_, nk_partial_load_b32x4_serial_,
|
|
46
|
+
nk_store_b128_v128relaxed_, nk_partial_store_b32x4_serial_,
|
|
47
|
+
/*dimensions_per_value=*/8)
|
|
48
|
+
|
|
49
|
+
#if defined(__clang__)
|
|
50
|
+
#pragma clang attribute pop
|
|
51
|
+
#endif
|
|
52
|
+
|
|
53
|
+
#if defined(__cplusplus)
|
|
54
|
+
} // extern "C"
|
|
55
|
+
#endif
|
|
56
|
+
|
|
57
|
+
#endif // NK_TARGET_V128RELAXED
|
|
58
|
+
#endif // NK_SETS_V128RELAXED_H
|
|
@@ -0,0 +1,339 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @brief SIMD-accelerated Batched Set Distances.
|
|
3
|
+
* @file include/numkong/sets.h
|
|
4
|
+
* @author Ash Vardanian
|
|
5
|
+
*
|
|
6
|
+
* This module provides efficient batched computation of Hamming and Jaccard distances
|
|
7
|
+
* between large collections of sets. Unlike the single-vector `set.h` module, this module
|
|
8
|
+
* is optimized for matrix-style operations where you compute distances between:
|
|
9
|
+
*
|
|
10
|
+
* - All pairs of rows in a query matrix Q against rows in values matrix V
|
|
11
|
+
* - All pairs within a single values matrix V (symmetric kernel)
|
|
12
|
+
*
|
|
13
|
+
* For dtypes:
|
|
14
|
+
*
|
|
15
|
+
* - u1: 1-bit binary (packed octets) → u32 Hamming / f32 Jaccard
|
|
16
|
+
*
|
|
17
|
+
* For hardware architectures:
|
|
18
|
+
*
|
|
19
|
+
* - Arm: NEON, SME+BI32
|
|
20
|
+
* - x86: Haswell, Ice Lake
|
|
21
|
+
*
|
|
22
|
+
* @section numerical_stability Numerical Stability
|
|
23
|
+
*
|
|
24
|
+
* Hamming u1: u32 popcount accumulator. Overflows at n_bits > 2^32.
|
|
25
|
+
* Jaccard u1: u32 intersection count, f32 division. Popcount values above 2^24 lose
|
|
26
|
+
* precision in f32 cast. Streaming variants use u64 accumulation internally.
|
|
27
|
+
*
|
|
28
|
+
* @section use_cases Use Cases
|
|
29
|
+
*
|
|
30
|
+
* - Binary similarity search: Find nearest neighbors in Hamming/Jaccard space
|
|
31
|
+
* - MinHash/SimHash: Compute Jaccard similarity for document fingerprints
|
|
32
|
+
* - Locality-sensitive hashing (LSH): Build similarity graphs
|
|
33
|
+
* - Binary neural network inference: Compute distances for BNN outputs
|
|
34
|
+
*
|
|
35
|
+
* @section math Mathematical Background
|
|
36
|
+
*
|
|
37
|
+
* Hamming distance: Number of positions where bits differ
|
|
38
|
+
* hamming(a, b) = popcount(a XOR b)
|
|
39
|
+
*
|
|
40
|
+
* Jaccard distance: 1 minus the Jaccard similarity
|
|
41
|
+
* jaccard(a, b) = 1 - |a ∩ b| / |a ∪ b|
|
|
42
|
+
* = 1 - popcount(a AND b) / popcount(a OR b)
|
|
43
|
+
*
|
|
44
|
+
* For Jaccard, we use the identity: |a ∪ b| = |a| + |b| - |a ∩ b|
|
|
45
|
+
* This allows precomputing |a| and |b| (population counts) during packing.
|
|
46
|
+
*/
|
|
47
|
+
|
|
48
|
+
#ifndef NK_SETS_H
|
|
49
|
+
#define NK_SETS_H
|
|
50
|
+
|
|
51
|
+
#include "numkong/types.h"
|
|
52
|
+
#include "numkong/dots.h"
|
|
53
|
+
|
|
54
|
+
#if defined(__cplusplus)
|
|
55
|
+
extern "C" {
|
|
56
|
+
#endif
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* @brief Compute Hamming distances between V rows and packed Q rows.
|
|
60
|
+
* @param[in] v Input values matrix
|
|
61
|
+
* @param[in] q_packed Packed queries matrix
|
|
62
|
+
* @param[out] result Row-major results matrix
|
|
63
|
+
* @param[in] rows Number of rows in the results matrix
|
|
64
|
+
* @param[in] cols Number of columns in the results matrix
|
|
65
|
+
* @param[in] d Number of dimensions (depth) per vector
|
|
66
|
+
* @param[in] v_stride_in_bytes Byte stride between rows of A
|
|
67
|
+
* @param[in] r_stride_in_bytes Byte stride between rows of C
|
|
68
|
+
*/
|
|
69
|
+
NK_DYNAMIC void nk_hammings_packed_u1(nk_u1x8_t const *v, void const *q_packed, nk_u32_t *result, nk_size_t rows,
|
|
70
|
+
nk_size_t cols, nk_size_t d, nk_size_t v_stride_in_bytes,
|
|
71
|
+
nk_size_t r_stride_in_bytes);
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* @brief Computes C = A × Aᵀ symmetric Gram matrix of Hamming distances.
|
|
75
|
+
* @param[in] vectors Input matrix of row vectors in row-major order.
|
|
76
|
+
* @param[in] n_vectors Number of vectors (rows) in the input matrix.
|
|
77
|
+
* @param[in] d Dimension of each vector (columns).
|
|
78
|
+
* @param[in] stride Row stride in bytes for the input matrix.
|
|
79
|
+
* @param[out] result Output symmetric matrix (n_vectors × n_vectors).
|
|
80
|
+
* @param[in] result_stride Row stride in bytes for the result matrix.
|
|
81
|
+
* @param[in] row_start Starting row offset of results to compute (needed for parallelism).
|
|
82
|
+
* @param[in] row_count Number of rows of results to compute (needed for parallelism).
|
|
83
|
+
*/
|
|
84
|
+
NK_DYNAMIC void nk_hammings_symmetric_u1(nk_u1x8_t const *vectors, nk_size_t n_vectors, nk_size_t d, nk_size_t stride,
|
|
85
|
+
nk_u32_t *result, nk_size_t result_stride, nk_size_t row_start,
|
|
86
|
+
nk_size_t row_count);
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* @brief Compute Jaccard distances between V rows and packed Q rows.
|
|
90
|
+
* @param[in] v Input values matrix
|
|
91
|
+
* @param[in] q_packed Packed queries matrix (with norms)
|
|
92
|
+
* @param[out] result Row-major f32 results matrix
|
|
93
|
+
* @param[in] rows Number of rows in the results matrix
|
|
94
|
+
* @param[in] cols Number of columns in the results matrix
|
|
95
|
+
* @param[in] d Number of dimensions (depth) per vector
|
|
96
|
+
* @param[in] v_stride_in_bytes Byte stride between rows of A
|
|
97
|
+
* @param[in] r_stride_in_bytes Byte stride between rows of C
|
|
98
|
+
*/
|
|
99
|
+
NK_DYNAMIC void nk_jaccards_packed_u1(nk_u1x8_t const *v, void const *q_packed, nk_f32_t *result, nk_size_t rows,
|
|
100
|
+
nk_size_t cols, nk_size_t d, nk_size_t v_stride_in_bytes,
|
|
101
|
+
nk_size_t r_stride_in_bytes);
|
|
102
|
+
|
|
103
|
+
/**
|
|
104
|
+
* @brief Computes C = f(A, Aᵀ) symmetric Gram matrix of Jaccard distances.
|
|
105
|
+
* @param[in] vectors Input matrix of row vectors in row-major order.
|
|
106
|
+
* @param[in] n_vectors Number of vectors (rows).
|
|
107
|
+
* @param[in] d Dimension of each vector (columns).
|
|
108
|
+
* @param[in] stride Row stride in bytes.
|
|
109
|
+
* @param[out] result Output symmetric f32 matrix (n_vectors × n_vectors).
|
|
110
|
+
* @param[in] result_stride Row stride in bytes for the result matrix.
|
|
111
|
+
* @param[in] row_start Starting row offset (for parallelism).
|
|
112
|
+
* @param[in] row_count Number of rows to compute (for parallelism).
|
|
113
|
+
*/
|
|
114
|
+
NK_DYNAMIC void nk_jaccards_symmetric_u1(nk_u1x8_t const *vectors, nk_size_t n_vectors, nk_size_t d, nk_size_t stride,
|
|
115
|
+
nk_f32_t *result, nk_size_t result_stride, nk_size_t row_start,
|
|
116
|
+
nk_size_t row_count);
|
|
117
|
+
|
|
118
|
+
/** @copydoc nk_hammings_packed_u1 */
|
|
119
|
+
NK_PUBLIC void nk_hammings_packed_u1_serial(nk_u1x8_t const *v, void const *q_packed, nk_u32_t *result, nk_size_t rows,
|
|
120
|
+
nk_size_t cols, nk_size_t d, nk_size_t v_stride_in_bytes,
|
|
121
|
+
nk_size_t r_stride_in_bytes);
|
|
122
|
+
/** @copydoc nk_hammings_symmetric_u1 */
|
|
123
|
+
NK_PUBLIC void nk_hammings_symmetric_u1_serial(nk_u1x8_t const *vectors, nk_size_t n_vectors, nk_size_t d,
|
|
124
|
+
nk_size_t stride, nk_u32_t *result, nk_size_t result_stride,
|
|
125
|
+
nk_size_t row_start, nk_size_t row_count);
|
|
126
|
+
/** @copydoc nk_jaccards_packed_u1 */
|
|
127
|
+
NK_PUBLIC void nk_jaccards_packed_u1_serial(nk_u1x8_t const *v, void const *q_packed, nk_f32_t *result, nk_size_t rows,
|
|
128
|
+
nk_size_t cols, nk_size_t d, nk_size_t v_stride_in_bytes,
|
|
129
|
+
nk_size_t r_stride_in_bytes);
|
|
130
|
+
/** @copydoc nk_jaccards_symmetric_u1 */
|
|
131
|
+
NK_PUBLIC void nk_jaccards_symmetric_u1_serial(nk_u1x8_t const *vectors, nk_size_t n_vectors, nk_size_t d,
|
|
132
|
+
nk_size_t stride, nk_f32_t *result, nk_size_t result_stride,
|
|
133
|
+
nk_size_t row_start, nk_size_t row_count);
|
|
134
|
+
|
|
135
|
+
/* ARM SME with BI32 (binary integer outer products).
|
|
136
|
+
* Uses BMOPA/BMOPS for efficient popcount-based set distances.
|
|
137
|
+
*/
|
|
138
|
+
#if NK_TARGET_SMEBI32
|
|
139
|
+
/** @copydoc nk_hammings_packed_u1 */
|
|
140
|
+
NK_PUBLIC void nk_hammings_packed_u1_smebi32(nk_u1x8_t const *v, void const *q_packed, nk_u32_t *result, nk_size_t rows,
|
|
141
|
+
nk_size_t cols, nk_size_t d, nk_size_t v_stride_in_bytes,
|
|
142
|
+
nk_size_t r_stride_in_bytes);
|
|
143
|
+
/** @copydoc nk_hammings_symmetric_u1 */
|
|
144
|
+
NK_PUBLIC void nk_hammings_symmetric_u1_smebi32(nk_u1x8_t const *vectors, nk_size_t n_vectors, nk_size_t d,
|
|
145
|
+
nk_size_t stride, nk_u32_t *result, nk_size_t result_stride,
|
|
146
|
+
nk_size_t row_start, nk_size_t row_count);
|
|
147
|
+
/** @copydoc nk_jaccards_packed_u1 */
|
|
148
|
+
NK_PUBLIC void nk_jaccards_packed_u1_smebi32(nk_u1x8_t const *v, void const *q_packed, nk_f32_t *result, nk_size_t rows,
|
|
149
|
+
nk_size_t cols, nk_size_t d, nk_size_t v_stride_in_bytes,
|
|
150
|
+
nk_size_t r_stride_in_bytes);
|
|
151
|
+
/** @copydoc nk_jaccards_symmetric_u1 */
|
|
152
|
+
NK_PUBLIC void nk_jaccards_symmetric_u1_smebi32(nk_u1x8_t const *vectors, nk_size_t n_vectors, nk_size_t d,
|
|
153
|
+
nk_size_t stride, nk_f32_t *result, nk_size_t result_stride,
|
|
154
|
+
nk_size_t row_start, nk_size_t row_count);
|
|
155
|
+
#endif // NK_TARGET_SMEBI32
|
|
156
|
+
|
|
157
|
+
/* Haswell backends using AVX2 (Intel Core 4th gen).
|
|
158
|
+
* Supports F32/F64 via FMA, F16/BF16/FP8 via software emulation, I8/U8 via VPMADDUBSW+VPADDD.
|
|
159
|
+
*/
|
|
160
|
+
#if NK_TARGET_HASWELL
|
|
161
|
+
/** @copydoc nk_hammings_packed_u1 */
|
|
162
|
+
NK_PUBLIC void nk_hammings_packed_u1_haswell(nk_u1x8_t const *v, void const *q_packed, nk_u32_t *result, nk_size_t rows,
|
|
163
|
+
nk_size_t cols, nk_size_t d, nk_size_t v_stride_in_bytes,
|
|
164
|
+
nk_size_t r_stride_in_bytes);
|
|
165
|
+
/** @copydoc nk_hammings_symmetric_u1 */
|
|
166
|
+
NK_PUBLIC void nk_hammings_symmetric_u1_haswell(nk_u1x8_t const *vectors, nk_size_t n_vectors, nk_size_t d,
|
|
167
|
+
nk_size_t stride, nk_u32_t *result, nk_size_t result_stride,
|
|
168
|
+
nk_size_t row_start, nk_size_t row_count);
|
|
169
|
+
/** @copydoc nk_jaccards_packed_u1 */
|
|
170
|
+
NK_PUBLIC void nk_jaccards_packed_u1_haswell(nk_u1x8_t const *v, void const *q_packed, nk_f32_t *result, nk_size_t rows,
|
|
171
|
+
nk_size_t cols, nk_size_t d, nk_size_t v_stride_in_bytes,
|
|
172
|
+
nk_size_t r_stride_in_bytes);
|
|
173
|
+
/** @copydoc nk_jaccards_symmetric_u1 */
|
|
174
|
+
NK_PUBLIC void nk_jaccards_symmetric_u1_haswell(nk_u1x8_t const *vectors, nk_size_t n_vectors, nk_size_t d,
|
|
175
|
+
nk_size_t stride, nk_f32_t *result, nk_size_t result_stride,
|
|
176
|
+
nk_size_t row_start, nk_size_t row_count);
|
|
177
|
+
#endif // NK_TARGET_HASWELL
|
|
178
|
+
|
|
179
|
+
/* Ice Lake backends using AVX-512 with VNNI (Vector Neural Network Instructions).
|
|
180
|
+
* Adds VPDPBUSD for I8/U8, VPDPWSSD for I4/U4 with efficient dot products.
|
|
181
|
+
*/
|
|
182
|
+
#if NK_TARGET_ICELAKE
|
|
183
|
+
/** @copydoc nk_hammings_packed_u1 */
|
|
184
|
+
NK_PUBLIC void nk_hammings_packed_u1_icelake(nk_u1x8_t const *v, void const *q_packed, nk_u32_t *result, nk_size_t rows,
|
|
185
|
+
nk_size_t cols, nk_size_t d, nk_size_t v_stride_in_bytes,
|
|
186
|
+
nk_size_t r_stride_in_bytes);
|
|
187
|
+
/** @copydoc nk_hammings_symmetric_u1 */
|
|
188
|
+
NK_PUBLIC void nk_hammings_symmetric_u1_icelake(nk_u1x8_t const *vectors, nk_size_t n_vectors, nk_size_t d,
|
|
189
|
+
nk_size_t stride, nk_u32_t *result, nk_size_t result_stride,
|
|
190
|
+
nk_size_t row_start, nk_size_t row_count);
|
|
191
|
+
/** @copydoc nk_jaccards_packed_u1 */
|
|
192
|
+
NK_PUBLIC void nk_jaccards_packed_u1_icelake(nk_u1x8_t const *v, void const *q_packed, nk_f32_t *result, nk_size_t rows,
|
|
193
|
+
nk_size_t cols, nk_size_t d, nk_size_t v_stride_in_bytes,
|
|
194
|
+
nk_size_t r_stride_in_bytes);
|
|
195
|
+
/** @copydoc nk_jaccards_symmetric_u1 */
|
|
196
|
+
NK_PUBLIC void nk_jaccards_symmetric_u1_icelake(nk_u1x8_t const *vectors, nk_size_t n_vectors, nk_size_t d,
|
|
197
|
+
nk_size_t stride, nk_f32_t *result, nk_size_t result_stride,
|
|
198
|
+
nk_size_t row_start, nk_size_t row_count);
|
|
199
|
+
#endif // NK_TARGET_ICELAKE
|
|
200
|
+
|
|
201
|
+
/* ARM NEON backends (base NEON with F32/F64 support).
|
|
202
|
+
* Uses FMLA for F32 dots, FMLA (scalar) for F64.
|
|
203
|
+
*/
|
|
204
|
+
#if NK_TARGET_NEON
|
|
205
|
+
/** @copydoc nk_hammings_packed_u1 */
|
|
206
|
+
NK_PUBLIC void nk_hammings_packed_u1_neon(nk_u1x8_t const *v, void const *q_packed, nk_u32_t *result, nk_size_t rows,
|
|
207
|
+
nk_size_t cols, nk_size_t d, nk_size_t v_stride_in_bytes,
|
|
208
|
+
nk_size_t r_stride_in_bytes);
|
|
209
|
+
/** @copydoc nk_hammings_symmetric_u1 */
|
|
210
|
+
NK_PUBLIC void nk_hammings_symmetric_u1_neon(nk_u1x8_t const *vectors, nk_size_t n_vectors, nk_size_t d,
|
|
211
|
+
nk_size_t stride, nk_u32_t *result, nk_size_t result_stride,
|
|
212
|
+
nk_size_t row_start, nk_size_t row_count);
|
|
213
|
+
/** @copydoc nk_jaccards_packed_u1 */
|
|
214
|
+
NK_PUBLIC void nk_jaccards_packed_u1_neon(nk_u1x8_t const *v, void const *q_packed, nk_f32_t *result, nk_size_t rows,
|
|
215
|
+
nk_size_t cols, nk_size_t d, nk_size_t v_stride_in_bytes,
|
|
216
|
+
nk_size_t r_stride_in_bytes);
|
|
217
|
+
/** @copydoc nk_jaccards_symmetric_u1 */
|
|
218
|
+
NK_PUBLIC void nk_jaccards_symmetric_u1_neon(nk_u1x8_t const *vectors, nk_size_t n_vectors, nk_size_t d,
|
|
219
|
+
nk_size_t stride, nk_f32_t *result, nk_size_t result_stride,
|
|
220
|
+
nk_size_t row_start, nk_size_t row_count);
|
|
221
|
+
#endif // NK_TARGET_NEON
|
|
222
|
+
|
|
223
|
+
/* WASM Relaxed SIMD backends using wasm_i8x16_popcnt for popcount-based set distances.
|
|
224
|
+
*/
|
|
225
|
+
#if NK_TARGET_V128RELAXED
|
|
226
|
+
/** @copydoc nk_hammings_packed_u1 */
|
|
227
|
+
NK_PUBLIC void nk_hammings_packed_u1_v128relaxed(nk_u1x8_t const *v, void const *q_packed, nk_u32_t *result,
|
|
228
|
+
nk_size_t rows, nk_size_t cols, nk_size_t d,
|
|
229
|
+
nk_size_t v_stride_in_bytes, nk_size_t r_stride_in_bytes);
|
|
230
|
+
/** @copydoc nk_hammings_symmetric_u1 */
|
|
231
|
+
NK_PUBLIC void nk_hammings_symmetric_u1_v128relaxed(nk_u1x8_t const *vectors, nk_size_t n_vectors, nk_size_t d,
|
|
232
|
+
nk_size_t stride, nk_u32_t *result, nk_size_t result_stride,
|
|
233
|
+
nk_size_t row_start, nk_size_t row_count);
|
|
234
|
+
/** @copydoc nk_jaccards_packed_u1 */
|
|
235
|
+
NK_PUBLIC void nk_jaccards_packed_u1_v128relaxed(nk_u1x8_t const *v, void const *q_packed, nk_f32_t *result,
|
|
236
|
+
nk_size_t rows, nk_size_t cols, nk_size_t d,
|
|
237
|
+
nk_size_t v_stride_in_bytes, nk_size_t r_stride_in_bytes);
|
|
238
|
+
/** @copydoc nk_jaccards_symmetric_u1 */
|
|
239
|
+
NK_PUBLIC void nk_jaccards_symmetric_u1_v128relaxed(nk_u1x8_t const *vectors, nk_size_t n_vectors, nk_size_t d,
|
|
240
|
+
nk_size_t stride, nk_f32_t *result, nk_size_t result_stride,
|
|
241
|
+
nk_size_t row_start, nk_size_t row_count);
|
|
242
|
+
#endif // NK_TARGET_V128RELAXED
|
|
243
|
+
|
|
244
|
+
#if defined(__cplusplus)
|
|
245
|
+
} // extern "C"
|
|
246
|
+
#endif
|
|
247
|
+
|
|
248
|
+
#include "numkong/sets/serial.h"
|
|
249
|
+
#include "numkong/sets/neon.h"
|
|
250
|
+
#include "numkong/sets/icelake.h"
|
|
251
|
+
#include "numkong/sets/haswell.h"
|
|
252
|
+
#include "numkong/sets/smebi32.h"
|
|
253
|
+
#include "numkong/sets/v128relaxed.h"
|
|
254
|
+
|
|
255
|
+
#if defined(__cplusplus)
|
|
256
|
+
extern "C" {
|
|
257
|
+
#endif
|
|
258
|
+
|
|
259
|
+
#if !NK_DYNAMIC_DISPATCH
|
|
260
|
+
|
|
261
|
+
NK_PUBLIC void nk_hammings_packed_u1(nk_u1x8_t const *v, void const *q_packed, nk_u32_t *result, nk_size_t rows,
|
|
262
|
+
nk_size_t cols, nk_size_t d, nk_size_t v_stride_in_bytes,
|
|
263
|
+
nk_size_t r_stride_in_bytes) {
|
|
264
|
+
#if NK_TARGET_SMEBI32
|
|
265
|
+
nk_hammings_packed_u1_smebi32(v, q_packed, result, rows, cols, d, v_stride_in_bytes, r_stride_in_bytes);
|
|
266
|
+
#elif NK_TARGET_NEON
|
|
267
|
+
nk_hammings_packed_u1_neon(v, q_packed, result, rows, cols, d, v_stride_in_bytes, r_stride_in_bytes);
|
|
268
|
+
#elif NK_TARGET_ICELAKE
|
|
269
|
+
nk_hammings_packed_u1_icelake(v, q_packed, result, rows, cols, d, v_stride_in_bytes, r_stride_in_bytes);
|
|
270
|
+
#elif NK_TARGET_HASWELL
|
|
271
|
+
nk_hammings_packed_u1_haswell(v, q_packed, result, rows, cols, d, v_stride_in_bytes, r_stride_in_bytes);
|
|
272
|
+
#elif NK_TARGET_V128RELAXED
|
|
273
|
+
nk_hammings_packed_u1_v128relaxed(v, q_packed, result, rows, cols, d, v_stride_in_bytes, r_stride_in_bytes);
|
|
274
|
+
#else
|
|
275
|
+
nk_hammings_packed_u1_serial(v, q_packed, result, rows, cols, d, v_stride_in_bytes, r_stride_in_bytes);
|
|
276
|
+
#endif
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
NK_PUBLIC void nk_hammings_symmetric_u1(nk_u1x8_t const *vectors, nk_size_t n_vectors, nk_size_t d, nk_size_t stride,
|
|
280
|
+
nk_u32_t *result, nk_size_t result_stride, nk_size_t row_start,
|
|
281
|
+
nk_size_t row_count) {
|
|
282
|
+
#if NK_TARGET_SMEBI32
|
|
283
|
+
nk_hammings_symmetric_u1_smebi32(vectors, n_vectors, d, stride, result, result_stride, row_start, row_count);
|
|
284
|
+
#elif NK_TARGET_NEON
|
|
285
|
+
nk_hammings_symmetric_u1_neon(vectors, n_vectors, d, stride, result, result_stride, row_start, row_count);
|
|
286
|
+
#elif NK_TARGET_ICELAKE
|
|
287
|
+
nk_hammings_symmetric_u1_icelake(vectors, n_vectors, d, stride, result, result_stride, row_start, row_count);
|
|
288
|
+
#elif NK_TARGET_HASWELL
|
|
289
|
+
nk_hammings_symmetric_u1_haswell(vectors, n_vectors, d, stride, result, result_stride, row_start, row_count);
|
|
290
|
+
#elif NK_TARGET_V128RELAXED
|
|
291
|
+
nk_hammings_symmetric_u1_v128relaxed(vectors, n_vectors, d, stride, result, result_stride, row_start, row_count);
|
|
292
|
+
#else
|
|
293
|
+
nk_hammings_symmetric_u1_serial(vectors, n_vectors, d, stride, result, result_stride, row_start, row_count);
|
|
294
|
+
#endif
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
NK_PUBLIC void nk_jaccards_packed_u1(nk_u1x8_t const *v, void const *q_packed, nk_f32_t *result, nk_size_t rows,
|
|
298
|
+
nk_size_t cols, nk_size_t d, nk_size_t v_stride_in_bytes,
|
|
299
|
+
nk_size_t r_stride_in_bytes) {
|
|
300
|
+
#if NK_TARGET_SMEBI32
|
|
301
|
+
nk_jaccards_packed_u1_smebi32(v, q_packed, result, rows, cols, d, v_stride_in_bytes, r_stride_in_bytes);
|
|
302
|
+
#elif NK_TARGET_NEON
|
|
303
|
+
nk_jaccards_packed_u1_neon(v, q_packed, result, rows, cols, d, v_stride_in_bytes, r_stride_in_bytes);
|
|
304
|
+
#elif NK_TARGET_ICELAKE
|
|
305
|
+
nk_jaccards_packed_u1_icelake(v, q_packed, result, rows, cols, d, v_stride_in_bytes, r_stride_in_bytes);
|
|
306
|
+
#elif NK_TARGET_HASWELL
|
|
307
|
+
nk_jaccards_packed_u1_haswell(v, q_packed, result, rows, cols, d, v_stride_in_bytes, r_stride_in_bytes);
|
|
308
|
+
#elif NK_TARGET_V128RELAXED
|
|
309
|
+
nk_jaccards_packed_u1_v128relaxed(v, q_packed, result, rows, cols, d, v_stride_in_bytes, r_stride_in_bytes);
|
|
310
|
+
#else
|
|
311
|
+
nk_jaccards_packed_u1_serial(v, q_packed, result, rows, cols, d, v_stride_in_bytes, r_stride_in_bytes);
|
|
312
|
+
#endif
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
NK_PUBLIC void nk_jaccards_symmetric_u1(nk_u1x8_t const *vectors, nk_size_t n_vectors, nk_size_t d, nk_size_t stride,
|
|
316
|
+
nk_f32_t *result, nk_size_t result_stride, nk_size_t row_start,
|
|
317
|
+
nk_size_t row_count) {
|
|
318
|
+
#if NK_TARGET_SMEBI32
|
|
319
|
+
nk_jaccards_symmetric_u1_smebi32(vectors, n_vectors, d, stride, result, result_stride, row_start, row_count);
|
|
320
|
+
#elif NK_TARGET_NEON
|
|
321
|
+
nk_jaccards_symmetric_u1_neon(vectors, n_vectors, d, stride, result, result_stride, row_start, row_count);
|
|
322
|
+
#elif NK_TARGET_ICELAKE
|
|
323
|
+
nk_jaccards_symmetric_u1_icelake(vectors, n_vectors, d, stride, result, result_stride, row_start, row_count);
|
|
324
|
+
#elif NK_TARGET_HASWELL
|
|
325
|
+
nk_jaccards_symmetric_u1_haswell(vectors, n_vectors, d, stride, result, result_stride, row_start, row_count);
|
|
326
|
+
#elif NK_TARGET_V128RELAXED
|
|
327
|
+
nk_jaccards_symmetric_u1_v128relaxed(vectors, n_vectors, d, stride, result, result_stride, row_start, row_count);
|
|
328
|
+
#else
|
|
329
|
+
nk_jaccards_symmetric_u1_serial(vectors, n_vectors, d, stride, result, result_stride, row_start, row_count);
|
|
330
|
+
#endif
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
#endif // !NK_DYNAMIC_DISPATCH
|
|
334
|
+
|
|
335
|
+
#if defined(__cplusplus)
|
|
336
|
+
} // extern "C"
|
|
337
|
+
#endif
|
|
338
|
+
|
|
339
|
+
#endif // NK_SETS_H
|
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
# Sparse Vector Operations in NumKong
|
|
2
|
+
|
|
3
|
+
NumKong implements set intersection and weighted dot products for sparse vectors stored as sorted arrays of unique indices with optional associated weights.
|
|
4
|
+
Set intersection counts common elements between two sorted index arrays; sparse dot product sums the products of weights at matching indices.
|
|
5
|
+
Used in inverted-index search, sparse feature matching, and graph intersection queries.
|
|
6
|
+
The separate index/weight stream design makes these primitives composable into batched sparse operations and future sparse GEMM workloads.
|
|
7
|
+
|
|
8
|
+
Set intersection:
|
|
9
|
+
|
|
10
|
+
```math
|
|
11
|
+
|A \cap B| = |\{i : i \in A \land i \in B\}|
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
Sparse dot product:
|
|
15
|
+
|
|
16
|
+
```math
|
|
17
|
+
\text{dot}(a, b) = \sum_{i \in A \cap B} w_a(i) \cdot w_b(i)
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
Reformulating as Python pseudocode:
|
|
21
|
+
|
|
22
|
+
```python
|
|
23
|
+
import numpy as np
|
|
24
|
+
|
|
25
|
+
def intersect(a_indices: np.ndarray, b_indices: np.ndarray) -> int:
|
|
26
|
+
return len(np.intersect1d(a_indices, b_indices))
|
|
27
|
+
|
|
28
|
+
def sparse_dot(a_indices: np.ndarray, a_weights: np.ndarray,
|
|
29
|
+
b_indices: np.ndarray, b_weights: np.ndarray) -> float:
|
|
30
|
+
common = np.intersect1d(a_indices, b_indices, return_indices=True)
|
|
31
|
+
return np.dot(a_weights[common[1]], b_weights[common[2]])
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
## Input & Output Types
|
|
35
|
+
|
|
36
|
+
| Input Type | Output Type | Description |
|
|
37
|
+
| ---------- | ----------- | -------------------------------------------- |
|
|
38
|
+
| `u16` | `u64` | 16-bit index intersection count |
|
|
39
|
+
| `u32` | `u64` | 32-bit index intersection count |
|
|
40
|
+
| `u64` | `u64` | 64-bit index intersection count |
|
|
41
|
+
| `u32+f32` | `f32` | Sparse dot with 32-bit indices, f32 weights |
|
|
42
|
+
| `u16+bf16` | `f32` | Sparse dot with 16-bit indices, bf16 weights |
|
|
43
|
+
|
|
44
|
+
## Optimizations
|
|
45
|
+
|
|
46
|
+
### Adaptive Merge vs Galloping Search
|
|
47
|
+
|
|
48
|
+
`nk_sparse_intersect_u32_serial` selects between linear merge and galloping (exponential) search based on length ratio: when `longer_length > 64 * shorter_length`, galloping search over the longer array is used.
|
|
49
|
+
Linear merge advances two pointers in lockstep at $O(|A| + |B|)$ using branch-free conditional increments: `i += ai < bj; j += ai >= bj` — no branch misprediction penalty.
|
|
50
|
+
Galloping binary-searches the longer array for each element of the shorter at $O(|A| \cdot \log |B|)$: an exponential probe doubles the search range until the target is bracketed, then binary search narrows within.
|
|
51
|
+
The crossover at 64x length ratio balances the per-element cost of binary search ($\log_2 |B|$ comparisons) against linear scan's single comparison per advance — the threshold was chosen empirically, as cache locality favors linear merge at moderate ratios.
|
|
52
|
+
|
|
53
|
+
### Broadcast-Compare SIMD Intersection on x86
|
|
54
|
+
|
|
55
|
+
`nk_sparse_intersect_u32_icelake` loads 16 indices from each array into ZMM registers, then rotates one register through multiple positions, comparing each rotation against the other with `VPCMPEQD` to test all 16x16 = 256 pairs.
|
|
56
|
+
The rotation approach uses `_mm512_shuffle_epi32` with permutation constants (`_MM_PERM_ADCB`, etc.) to cycle elements through comparison positions — contending for port 5 (~1cy per shuffle, ~3cy for `_mm512_alignr_epi32`).
|
|
57
|
+
Match counts are extracted via `_mm_popcnt_u32` on the comparison masks, accumulating intersection size without materializing matched elements.
|
|
58
|
+
Before each 16x16 comparison block, a fast overlap check (`a_max < b_min || b_max < a_min`) skips non-overlapping register loads entirely — critical for sparse workloads where most pairs have disjoint index ranges.
|
|
59
|
+
No native `_mm512_2intersect_epi16` instruction exists in any x86 ISA — UInt16 intersection must convert indices to UInt32 before comparison, halving effective throughput.
|
|
60
|
+
|
|
61
|
+
### VP2INTERSECT on AMD Turin
|
|
62
|
+
|
|
63
|
+
`nk_sparse_intersect_u32_turin` uses the `VP2INTERSECT` instruction (Zen5), which produces _two_ 16-bit masks in a single operation — one indicating which elements of A matched any element of B, and vice versa.
|
|
64
|
+
This replaces the entire shuffle-rotate-compare sequence from Ice Lake with a single instruction, eliminating port-5 contention entirely.
|
|
65
|
+
Even on Turin, UInt16 intersection requires zero-extending to UInt32 first — no `VP2INTERSECT` variant operates on 16-bit elements.
|
|
66
|
+
For UInt64, `_mm512_2intersect_epi64` processes 8x8 = 64 pairs per instruction — half the throughput of UInt32 but still far faster than the Ice Lake shuffle approach.
|
|
67
|
+
|
|
68
|
+
### SVE2 Set Membership via svmatch and svhistcnt
|
|
69
|
+
|
|
70
|
+
`nk_sparse_intersect_u16_sve2` uses the `svmatch_u16` instruction — true hardware set membership testing that matches each element against a 128-bit lane of candidates.
|
|
71
|
+
However, `svmatch` only operates on UInt8 and UInt16 — no UInt32 or UInt64 variant exists in SVE2.
|
|
72
|
+
For UInt32/UInt64, `nk_sparse_intersect_u32_sve2` uses `svhistcnt_u32_z` (histogram count): this computes a prefix-match count for each element against preceding elements in the combined register, and a reverse pass captures the upper triangle — ORing both halves yields the full intersection mask.
|
|
73
|
+
NEON (`nk_sparse_intersect_u32_neon`) lacks compress-store entirely — when intersection results must be materialized (not just counted), the kernel falls back to serial extraction, using `vclz_u32` (count leading zeros) to compute pointer advance steps from comparison masks.
|
|
74
|
+
|
|
75
|
+
### BFloat16 Weights in Sparse Dot Products
|
|
76
|
+
|
|
77
|
+
`nk_sparse_dot_u16bf16_sve2` loads BFloat16 weights alongside UInt16 indices, selecting matching weights via `svsel_s16` after intersection detection, then accumulating with `svbfdot_f32` — a single instruction that multiplies BFloat16 pairs and adds to a Float32 accumulator.
|
|
78
|
+
`nk_sparse_dot_u16bf16_turin` zero-extends UInt16 indices to UInt32 for `VP2INTERSECT`, then compresses matching BFloat16 weights with `VPCOMPRESSW` and accumulates via `_mm256_dpbf16_ps` (6cy latency on Genoa).
|
|
79
|
+
BFloat16 weights halve memory traffic compared to Float32 (16-bit vs 32-bit per weight) while preserving sufficient precision for learned sparse attention weights and embedding lookups.
|
|
80
|
+
The index/weight stream separation enables type-independent intersection (UInt16/UInt32/UInt64 indices) with type-specific accumulation (BFloat16 or Float32 weights) — the same intersection code path serves both weight types.
|
|
81
|
+
|
|
82
|
+
### Implications for Sparse GEMM
|
|
83
|
+
|
|
84
|
+
Current sparse operations handle inner-product dot products — one pair of sparse vectors at a time.
|
|
85
|
+
Extending to batched sparse GEMM (SpMM, SpGEMM) would require simultaneous intersection of multiple sparse vectors — the broadcast-compare pattern scales naturally, since one document vector's indices can be broadcast against multiple query vectors' indices in the same ZMM/SVE registers.
|
|
86
|
+
The 64x galloping threshold is tuned for individual vector pairs; batched workloads with different sparsity patterns per row would benefit from adaptive per-pair threshold selection.
|
|
87
|
+
Hardware support remains the bottleneck: no ISA provides native sparse outer-product instructions, and `VP2INTERSECT` exists only on AMD Zen5+ — Intel Tiger Lake's implementation had 36-41cy latency, making it slower than the manual shuffle approach on Ice Lake.
|
|
88
|
+
|
|
89
|
+
## Performance
|
|
90
|
+
|
|
91
|
+
The following performance tables are produced by manually re-running `nk_test` and `nk_bench` included internal tools to measure both accuracy and throughput at different input shapes.
|
|
92
|
+
The input size is controlled by `NK_SPARSE_FIRST_LENGTH`, `NK_SPARSE_SECOND_LENGTH`, and `NK_SPARSE_INTERSECTION` environment variables.
|
|
93
|
+
Columns show throughput at 1%, 50%, and 95% intersection ratio with both set lengths fixed at 4096.
|
|
94
|
+
The throughput is measured in GB/s as the number of input bytes per second.
|
|
95
|
+
Each kernel runs for at least 20 seconds per configuration.
|
|
96
|
+
Benchmark threads are pinned to specific cores; on machines with heterogeneous core types (e.g., Apple P/E cores), only the fastest cores are used.
|
|
97
|
+
Workloads that significantly degrade CPU frequencies (Intel AMX, Apple SME) run in separate passes to avoid affecting throughput measurements of other kernels.
|
|
98
|
+
Accuracy is reported as mean ULP (units in last place) averaged over all test pairs — the average number of representable floating-point values between the computed result and the exact answer.
|
|
99
|
+
|
|
100
|
+
### Intel Sapphire Rapids
|
|
101
|
+
|
|
102
|
+
#### Native
|
|
103
|
+
|
|
104
|
+
| Kernel | 1% | 50% | 95% |
|
|
105
|
+
| :-------------------------------- | -----------------------: | -----------------------: | -----------------------: |
|
|
106
|
+
| __u64__ | ░░░░░░░░░░░░░░░░░░░░░░░░ | ░░░░░░░░░░░░░░░░░░░░░░░░ | ░░░░░░░░░░░░░░░░░░░░░░░░ |
|
|
107
|
+
| `nk_sparse_intersect_u64_serial` | 2.96 gb/s | 3.06 gb/s | 3.27 gb/s |
|
|
108
|
+
| `nk_sparse_intersect_u64_icelake` | 3.64 gb/s | 3.83 gb/s | 3.74 gb/s |
|
|
109
|
+
| __u32__ | ░░░░░░░░░░░░░░░░░░░░░░░░ | ░░░░░░░░░░░░░░░░░░░░░░░░ | ░░░░░░░░░░░░░░░░░░░░░░░░ |
|
|
110
|
+
| `nk_sparse_intersect_u32_serial` | 1.51 gb/s | 1.55 gb/s | 1.69 gb/s |
|
|
111
|
+
| `nk_sparse_intersect_u32_icelake` | 4.15 gb/s | 4.29 gb/s | 4.25 gb/s |
|
|
112
|
+
| __u16__ | ░░░░░░░░░░░░░░░░░░░░░░░░ | ░░░░░░░░░░░░░░░░░░░░░░░░ | ░░░░░░░░░░░░░░░░░░░░░░░░ |
|
|
113
|
+
| `nk_sparse_intersect_u16_serial` | 0.747 gb/s | 0.793 gb/s | 0.824 gb/s |
|
|
114
|
+
| `nk_sparse_intersect_u16_icelake` | 3.06 gb/s | 3.09 gb/s | 3.10 gb/s |
|
|
115
|
+
| __f32__ | ░░░░░░░░░░░░░░░░░░░░░░░░ | ░░░░░░░░░░░░░░░░░░░░░░░░ | ░░░░░░░░░░░░░░░░░░░░░░░░ |
|
|
116
|
+
| `nk_sparse_dot_u32f32_serial` | 2.78 gb/s, 6.6 ulp | 2.78 gb/s, 5.9 ulp | 2.79 gb/s, 6.4 ulp |
|
|
117
|
+
| `nk_sparse_dot_u32f32_icelake` | 7.80 gb/s, 3.8 ulp | 6.46 gb/s, 4 ulp | 5.86 gb/s, 3.8 ulp |
|
|
118
|
+
| __bf16__ | ░░░░░░░░░░░░░░░░░░░░░░░░ | ░░░░░░░░░░░░░░░░░░░░░░░░ | ░░░░░░░░░░░░░░░░░░░░░░░░ |
|
|
119
|
+
| `nk_sparse_dot_u16bf16_serial` | 0.366 gb/s, 0 ulp | 0.364 gb/s, 0 ulp | 0.366 gb/s, 0 ulp |
|
|
120
|
+
|
|
121
|
+
#### WASM
|
|
122
|
+
|
|
123
|
+
Measured with Wasmtime v42 (Cranelift backend).
|
|
124
|
+
|
|
125
|
+
| Kernel | 1% | 50% | 95% |
|
|
126
|
+
| :------------------------------- | -----------------------: | -----------------------: | -----------------------: |
|
|
127
|
+
| __u64__ | ░░░░░░░░░░░░░░░░░░░░░░░░ | ░░░░░░░░░░░░░░░░░░░░░░░░ | ░░░░░░░░░░░░░░░░░░░░░░░░ |
|
|
128
|
+
| `nk_sparse_intersect_u64_serial` | 1.74 gb/s | 1.74 gb/s | 1.74 gb/s |
|
|
129
|
+
| __u32__ | ░░░░░░░░░░░░░░░░░░░░░░░░ | ░░░░░░░░░░░░░░░░░░░░░░░░ | ░░░░░░░░░░░░░░░░░░░░░░░░ |
|
|
130
|
+
| `nk_sparse_intersect_u32_serial` | 0.492 gb/s | 0.492 gb/s | 0.492 gb/s |
|
|
131
|
+
| __u16__ | ░░░░░░░░░░░░░░░░░░░░░░░░ | ░░░░░░░░░░░░░░░░░░░░░░░░ | ░░░░░░░░░░░░░░░░░░░░░░░░ |
|
|
132
|
+
| `nk_sparse_intersect_u16_serial` | 0.309 gb/s | 0.309 gb/s | 0.309 gb/s |
|
|
133
|
+
| __f32__ | ░░░░░░░░░░░░░░░░░░░░░░░░ | ░░░░░░░░░░░░░░░░░░░░░░░░ | ░░░░░░░░░░░░░░░░░░░░░░░░ |
|
|
134
|
+
| `nk_sparse_dot_u32f32_serial` | 1.87 gb/s, 9.1 ulp | 1.87 gb/s, 9.1 ulp | 1.87 gb/s, 9.1 ulp |
|
|
135
|
+
| __bf16__ | ░░░░░░░░░░░░░░░░░░░░░░░░ | ░░░░░░░░░░░░░░░░░░░░░░░░ | ░░░░░░░░░░░░░░░░░░░░░░░░ |
|
|
136
|
+
| `nk_sparse_dot_u16bf16_serial` | 0.927 gb/s, 0 ulp | 0.927 gb/s, 0 ulp | 0.927 gb/s, 0 ulp |
|
|
137
|
+
|
|
138
|
+
### Apple M4
|
|
139
|
+
|
|
140
|
+
#### Native
|
|
141
|
+
|
|
142
|
+
| Kernel | 1% | 50% | 95% |
|
|
143
|
+
| :------------------------------- | -----------------------: | -----------------------: | -----------------------: |
|
|
144
|
+
| __u64__ | ░░░░░░░░░░░░░░░░░░░░░░░░ | ░░░░░░░░░░░░░░░░░░░░░░░░ | ░░░░░░░░░░░░░░░░░░░░░░░░ |
|
|
145
|
+
| `nk_sparse_intersect_u64_serial` | 3.74 gb/s | 3.96 gb/s | 4.17 gb/s |
|
|
146
|
+
| `nk_sparse_intersect_u64_neon` | 4.91 gb/s | 4.61 gb/s | 4.46 gb/s |
|
|
147
|
+
| __u32__ | ░░░░░░░░░░░░░░░░░░░░░░░░ | ░░░░░░░░░░░░░░░░░░░░░░░░ | ░░░░░░░░░░░░░░░░░░░░░░░░ |
|
|
148
|
+
| `nk_sparse_intersect_u32_serial` | 1.88 gb/s | 1.98 gb/s | 2.09 gb/s |
|
|
149
|
+
| `nk_sparse_intersect_u32_neon` | 2.81 gb/s | 2.60 gb/s | 2.46 gb/s |
|
|
150
|
+
| __u16__ | ░░░░░░░░░░░░░░░░░░░░░░░░ | ░░░░░░░░░░░░░░░░░░░░░░░░ | ░░░░░░░░░░░░░░░░░░░░░░░░ |
|
|
151
|
+
| `nk_sparse_intersect_u16_serial` | 0.951 gb/s | 0.995 gb/s | 1.05 gb/s |
|
|
152
|
+
| `nk_sparse_intersect_u16_neon` | 1.86 gb/s | 1.85 gb/s | 1.85 gb/s |
|
|
153
|
+
| __f32__ | ░░░░░░░░░░░░░░░░░░░░░░░░ | ░░░░░░░░░░░░░░░░░░░░░░░░ | ░░░░░░░░░░░░░░░░░░░░░░░░ |
|
|
154
|
+
| `nk_sparse_dot_u32f32_serial` | 3.73 gb/s, 7.1 ulp | 3.74 gb/s, 7.1 ulp | 3.79 gb/s, 7.0 ulp |
|
|
155
|
+
| __bf16__ | ░░░░░░░░░░░░░░░░░░░░░░░░ | ░░░░░░░░░░░░░░░░░░░░░░░░ | ░░░░░░░░░░░░░░░░░░░░░░░░ |
|
|
156
|
+
| `nk_sparse_dot_u16bf16_serial` | 1.73 gb/s, 0 ulp | 1.72 gb/s, 0 ulp | 1.72 gb/s, 0 ulp |
|