faiss 0.6.3 → 0.6.4

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.
Files changed (87) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +4 -0
  3. data/lib/faiss/version.rb +1 -1
  4. data/vendor/faiss/faiss/Clustering.h +8 -0
  5. data/vendor/faiss/faiss/Index.h +1 -1
  6. data/vendor/faiss/faiss/IndexBinaryFromFloat.cpp +23 -2
  7. data/vendor/faiss/faiss/IndexBinaryHNSW.cpp +12 -140
  8. data/vendor/faiss/faiss/IndexBinaryHNSW.h +0 -6
  9. data/vendor/faiss/faiss/IndexBinaryIVF.cpp +6 -14
  10. data/vendor/faiss/faiss/IndexBinaryIVF.h +8 -1
  11. data/vendor/faiss/faiss/IndexFlat.cpp +8 -4
  12. data/vendor/faiss/faiss/IndexFlat.h +2 -2
  13. data/vendor/faiss/faiss/IndexHNSW.cpp +301 -91
  14. data/vendor/faiss/faiss/IndexHNSW.h +31 -7
  15. data/vendor/faiss/faiss/IndexIDMap.cpp +11 -6
  16. data/vendor/faiss/faiss/IndexIVF.cpp +19 -4
  17. data/vendor/faiss/faiss/IndexIVFPQ.cpp +9 -1
  18. data/vendor/faiss/faiss/IndexIVFPQ.h +11 -0
  19. data/vendor/faiss/faiss/IndexShards.cpp +3 -3
  20. data/vendor/faiss/faiss/IndexShardsIVF.cpp +4 -3
  21. data/vendor/faiss/faiss/SuperKMeans.cpp +31 -8
  22. data/vendor/faiss/faiss/SuperKMeans.h +3 -2
  23. data/vendor/faiss/faiss/VectorTransform.cpp +38 -0
  24. data/vendor/faiss/faiss/VectorTransform.h +3 -0
  25. data/vendor/faiss/faiss/clone_index.cpp +3 -0
  26. data/vendor/faiss/faiss/gpu/GpuClonerOptions.h +1 -5
  27. data/vendor/faiss/faiss/gpu/GpuDistance.h +2 -5
  28. data/vendor/faiss/faiss/gpu/GpuIndex.h +4 -5
  29. data/vendor/faiss/faiss/gpu/GpuIndexCagra.h +65 -42
  30. data/vendor/faiss/faiss/impl/AdditiveQuantizer.cpp +1 -1
  31. data/vendor/faiss/faiss/impl/ClusteringInitialization.cpp +5 -2
  32. data/vendor/faiss/faiss/impl/HNSW.cpp +419 -115
  33. data/vendor/faiss/faiss/impl/HNSW.h +46 -7
  34. data/vendor/faiss/faiss/impl/IDSelector.h +44 -0
  35. data/vendor/faiss/faiss/impl/NNDescent.cpp +10 -3
  36. data/vendor/faiss/faiss/impl/Panorama.h +11 -2
  37. data/vendor/faiss/faiss/impl/ProductQuantizer.cpp +4 -4
  38. data/vendor/faiss/faiss/impl/RaBitQUtils.cpp +1 -1
  39. data/vendor/faiss/faiss/impl/RaBitQuantizer.cpp +74 -8
  40. data/vendor/faiss/faiss/impl/RaBitQuantizer.h +47 -4
  41. data/vendor/faiss/faiss/impl/RaBitQuantizerMultiBit.cpp +2 -2
  42. data/vendor/faiss/faiss/impl/ResultHandler.h +9 -3
  43. data/vendor/faiss/faiss/impl/ScalarQuantizer.cpp +32 -30
  44. data/vendor/faiss/faiss/impl/ScalarQuantizer.h +6 -10
  45. data/vendor/faiss/faiss/impl/binary_hamming/IndexBinaryIVF_impl.h +89 -13
  46. data/vendor/faiss/faiss/impl/expanded_scanners.h +5 -1
  47. data/vendor/faiss/faiss/impl/fast_scan/decompose_qbs.h +1 -0
  48. data/vendor/faiss/faiss/impl/index_read.cpp +130 -29
  49. data/vendor/faiss/faiss/impl/index_write.cpp +31 -26
  50. data/vendor/faiss/faiss/impl/pq_code_distance/IVFPQScanner_impl.h +73 -39
  51. data/vendor/faiss/faiss/impl/pq_code_distance/pq_code_distance-generic.cpp +4 -4
  52. data/vendor/faiss/faiss/impl/scalar_quantizer/EDENQuantizer.cpp +6 -1
  53. data/vendor/faiss/faiss/impl/scalar_quantizer/distance_computers.h +2 -2
  54. data/vendor/faiss/faiss/impl/scalar_quantizer/sq-avx2.cpp +6 -2
  55. data/vendor/faiss/faiss/impl/scalar_quantizer/sq-dispatch.h +37 -17
  56. data/vendor/faiss/faiss/impl/scalar_quantizer/sq-neon.cpp +109 -10
  57. data/vendor/faiss/faiss/impl/simd_dispatch.h +40 -22
  58. data/vendor/faiss/faiss/index_factory.cpp +8 -1
  59. data/vendor/faiss/faiss/invlists/InvertedLists.cpp +7 -4
  60. data/vendor/faiss/faiss/invlists/OnDiskInvertedLists.cpp +10 -4
  61. data/vendor/faiss/faiss/svs/IndexSVSVamana.cpp +16 -4
  62. data/vendor/faiss/faiss/svs/IndexSVSVamana.h +8 -1
  63. data/vendor/faiss/faiss/svs/IndexSVSVamanaLVQ.cpp +4 -2
  64. data/vendor/faiss/faiss/svs/IndexSVSVamanaLVQ.h +2 -1
  65. data/vendor/faiss/faiss/svs/IndexSVSVamanaLeanVec.cpp +9 -2
  66. data/vendor/faiss/faiss/svs/IndexSVSVamanaLeanVec.h +2 -1
  67. data/vendor/faiss/faiss/utils/distances.cpp +20 -13
  68. data/vendor/faiss/faiss/utils/distances_dispatch.h +30 -24
  69. data/vendor/faiss/faiss/utils/distances_fused/distances_fused.cpp +1 -1
  70. data/vendor/faiss/faiss/utils/hamming.cpp +15 -9
  71. data/vendor/faiss/faiss/utils/hamming.h +10 -1
  72. data/vendor/faiss/faiss/utils/hamming_distance/common.h +14 -3
  73. data/vendor/faiss/faiss/utils/hamming_distance/hamming_avx512_vpopcnt.cpp +24 -0
  74. data/vendor/faiss/faiss/utils/hamming_distance/hamming_computer-avx512.h +1 -1
  75. data/vendor/faiss/faiss/utils/hamming_distance/{hamming_computer-avx512_spr.h → hamming_computer-avx512_vpopcnt.h} +85 -24
  76. data/vendor/faiss/faiss/utils/hamming_distance/hamming_impl.h +141 -0
  77. data/vendor/faiss/faiss/utils/simd_impl/distances_arm_sve.cpp +194 -30
  78. data/vendor/faiss/faiss/utils/simd_impl/distances_rvv.cpp +93 -55
  79. data/vendor/faiss/faiss/utils/simd_impl/rabitq_avx512.cpp +57 -40
  80. data/vendor/faiss/faiss/utils/simd_impl/{rabitq_avx512_spr.cpp → rabitq_avx512_vpopcnt.cpp} +21 -24
  81. data/vendor/faiss/faiss/utils/simd_impl/super_kmeans_dispatch.h +2 -7
  82. data/vendor/faiss/faiss/utils/simd_impl/super_kmeans_kernels.h +6 -1
  83. data/vendor/faiss/faiss/utils/simd_impl/super_kmeans_kernels_sve.cpp +34 -0
  84. data/vendor/faiss/faiss/utils/simd_levels.cpp +172 -67
  85. data/vendor/faiss/faiss/utils/simd_levels.h +26 -15
  86. metadata +6 -5
  87. data/vendor/faiss/faiss/utils/hamming_distance/hamming_avx512_spr.cpp +0 -15
@@ -16,6 +16,42 @@
16
16
 
17
17
  namespace faiss {
18
18
 
19
+ template <typename Vec, typename Reduce>
20
+ static inline float rvv_reduce(
21
+ Vec value,
22
+ size_t vl,
23
+ float identity,
24
+ Reduce reduce) {
25
+ vfloat32m1_t init = __riscv_vfmv_s_f_f32m1(identity, 1);
26
+ vfloat32m1_t result = reduce(value, init, vl);
27
+ return __riscv_vfmv_f_s_f32m1_f32(result);
28
+ }
29
+
30
+ static inline size_t rvv_argmin(const float* values, size_t n) {
31
+ size_t vlmax = __riscv_vsetvlmax_e32m8();
32
+ vfloat32m8_t vmin = __riscv_vfmv_v_f_f32m8(__builtin_inff(), vlmax);
33
+ size_t i = 0;
34
+ while (i < n) {
35
+ size_t vl = __riscv_vsetvl_e32m8(n - i);
36
+ vfloat32m8_t vd = __riscv_vle32_v_f32m8(values + i, vl);
37
+ vmin = __riscv_vfmin_vv_f32m8_tu(vmin, vmin, vd, vl);
38
+ i += vl;
39
+ }
40
+ float min_val = rvv_reduce(
41
+ vmin, vlmax, __builtin_inff(), __riscv_vfredmin_vs_f32m8_f32m1);
42
+ i = 0;
43
+ while (i < n) {
44
+ size_t vl = __riscv_vsetvl_e32m8(n - i);
45
+ vfloat32m8_t vd = __riscv_vle32_v_f32m8(values + i, vl);
46
+ long j = __riscv_vfirst_m_b4(
47
+ __riscv_vmfeq_vf_f32m8_b4(vd, min_val, vl), vl);
48
+ if (j >= 0)
49
+ return i + static_cast<size_t>(j);
50
+ i += vl;
51
+ }
52
+ return n;
53
+ }
54
+
19
55
  template <>
20
56
  float fvec_norm_L2sqr<SIMDLevel::RISCV_RVV>(const float* x, size_t d) {
21
57
  size_t vlmax = __riscv_vsetvlmax_e32m8();
@@ -27,9 +63,7 @@ float fvec_norm_L2sqr<SIMDLevel::RISCV_RVV>(const float* x, size_t d) {
27
63
  acc = __riscv_vfmacc_vv_f32m8_tu(acc, vx, vx, vl);
28
64
  i += vl;
29
65
  }
30
- vfloat32m1_t sum = __riscv_vfmv_s_f_f32m1(0.0f, 1);
31
- sum = __riscv_vfredusum_vs_f32m8_f32m1(acc, sum, vlmax);
32
- return __riscv_vfmv_f_s_f32m1_f32(sum);
66
+ return rvv_reduce(acc, vlmax, 0.0f, __riscv_vfredusum_vs_f32m8_f32m1);
33
67
  }
34
68
 
35
69
  template <>
@@ -48,9 +82,7 @@ float fvec_L2sqr<SIMDLevel::RISCV_RVV>(
48
82
  acc = __riscv_vfmacc_vv_f32m8_tu(acc, vx, vx, vl);
49
83
  i += vl;
50
84
  }
51
- vfloat32m1_t sum = __riscv_vfmv_s_f_f32m1(0.0f, 1);
52
- sum = __riscv_vfredusum_vs_f32m8_f32m1(acc, sum, vlmax);
53
- return __riscv_vfmv_f_s_f32m1_f32(sum);
85
+ return rvv_reduce(acc, vlmax, 0.0f, __riscv_vfredusum_vs_f32m8_f32m1);
54
86
  }
55
87
 
56
88
  template <>
@@ -68,9 +100,7 @@ float fvec_inner_product<SIMDLevel::RISCV_RVV>(
68
100
  acc = __riscv_vfmacc_vv_f32m8_tu(acc, vx, vy, vl);
69
101
  i += vl;
70
102
  }
71
- vfloat32m1_t sum = __riscv_vfmv_s_f_f32m1(0.0f, 1);
72
- sum = __riscv_vfredusum_vs_f32m8_f32m1(acc, sum, vlmax);
73
- return __riscv_vfmv_f_s_f32m1_f32(sum);
103
+ return rvv_reduce(acc, vlmax, 0.0f, __riscv_vfredusum_vs_f32m8_f32m1);
74
104
  }
75
105
 
76
106
  template <>
@@ -87,9 +117,7 @@ float fvec_L1<SIMDLevel::RISCV_RVV>(const float* x, const float* y, size_t d) {
87
117
  acc = __riscv_vfadd_vv_f32m8_tu(acc, acc, vx, vl);
88
118
  i += vl;
89
119
  }
90
- vfloat32m1_t sum = __riscv_vfmv_s_f_f32m1(0.0f, 1);
91
- sum = __riscv_vfredusum_vs_f32m8_f32m1(acc, sum, vlmax);
92
- return __riscv_vfmv_f_s_f32m1_f32(sum);
120
+ return rvv_reduce(acc, vlmax, 0.0f, __riscv_vfredusum_vs_f32m8_f32m1);
93
121
  }
94
122
 
95
123
  template <>
@@ -109,9 +137,7 @@ float fvec_Linf<SIMDLevel::RISCV_RVV>(
109
137
  vmax = __riscv_vfmax_vv_f32m8_tu(vmax, vmax, vx, vl);
110
138
  i += vl;
111
139
  }
112
- vfloat32m1_t max = __riscv_vfmv_s_f_f32m1(0.0f, 1);
113
- max = __riscv_vfredmax_vs_f32m8_f32m1(vmax, max, vlmax);
114
- return __riscv_vfmv_f_s_f32m1_f32(max);
140
+ return rvv_reduce(vmax, vlmax, 0.0f, __riscv_vfredmax_vs_f32m8_f32m1);
115
141
  }
116
142
 
117
143
  template <>
@@ -126,10 +152,29 @@ void fvec_inner_product_batch_4<SIMDLevel::RISCV_RVV>(
126
152
  float& dis1,
127
153
  float& dis2,
128
154
  float& dis3) {
129
- dis0 = fvec_inner_product<SIMDLevel::RISCV_RVV>(x, y0, d);
130
- dis1 = fvec_inner_product<SIMDLevel::RISCV_RVV>(x, y1, d);
131
- dis2 = fvec_inner_product<SIMDLevel::RISCV_RVV>(x, y2, d);
132
- dis3 = fvec_inner_product<SIMDLevel::RISCV_RVV>(x, y3, d);
155
+ size_t vlmax = __riscv_vsetvlmax_e32m4();
156
+ vfloat32m4_t vacc0 = __riscv_vfmv_v_f_f32m4(0.0f, vlmax);
157
+ vfloat32m4_t vacc1 = __riscv_vfmv_v_f_f32m4(0.0f, vlmax);
158
+ vfloat32m4_t vacc2 = __riscv_vfmv_v_f_f32m4(0.0f, vlmax);
159
+ vfloat32m4_t vacc3 = __riscv_vfmv_v_f_f32m4(0.0f, vlmax);
160
+ size_t i = 0;
161
+ while (i < d) {
162
+ size_t vl = __riscv_vsetvl_e32m4(d - i);
163
+ vfloat32m4_t vx = __riscv_vle32_v_f32m4(x + i, vl);
164
+ vfloat32m4_t vy = __riscv_vle32_v_f32m4(y0 + i, vl);
165
+ vacc0 = __riscv_vfmacc_vv_f32m4_tu(vacc0, vx, vy, vl);
166
+ vy = __riscv_vle32_v_f32m4(y1 + i, vl);
167
+ vacc1 = __riscv_vfmacc_vv_f32m4_tu(vacc1, vx, vy, vl);
168
+ vy = __riscv_vle32_v_f32m4(y2 + i, vl);
169
+ vacc2 = __riscv_vfmacc_vv_f32m4_tu(vacc2, vx, vy, vl);
170
+ vy = __riscv_vle32_v_f32m4(y3 + i, vl);
171
+ vacc3 = __riscv_vfmacc_vv_f32m4_tu(vacc3, vx, vy, vl);
172
+ i += vl;
173
+ }
174
+ dis0 = rvv_reduce(vacc0, vlmax, 0.0f, __riscv_vfredusum_vs_f32m4_f32m1);
175
+ dis1 = rvv_reduce(vacc1, vlmax, 0.0f, __riscv_vfredusum_vs_f32m4_f32m1);
176
+ dis2 = rvv_reduce(vacc2, vlmax, 0.0f, __riscv_vfredusum_vs_f32m4_f32m1);
177
+ dis3 = rvv_reduce(vacc3, vlmax, 0.0f, __riscv_vfredusum_vs_f32m4_f32m1);
133
178
  }
134
179
 
135
180
  template <>
@@ -144,10 +189,33 @@ void fvec_L2sqr_batch_4<SIMDLevel::RISCV_RVV>(
144
189
  float& dis1,
145
190
  float& dis2,
146
191
  float& dis3) {
147
- dis0 = fvec_L2sqr<SIMDLevel::RISCV_RVV>(x, y0, d);
148
- dis1 = fvec_L2sqr<SIMDLevel::RISCV_RVV>(x, y1, d);
149
- dis2 = fvec_L2sqr<SIMDLevel::RISCV_RVV>(x, y2, d);
150
- dis3 = fvec_L2sqr<SIMDLevel::RISCV_RVV>(x, y3, d);
192
+ size_t vlmax = __riscv_vsetvlmax_e32m4();
193
+ vfloat32m4_t vacc0 = __riscv_vfmv_v_f_f32m4(0.0f, vlmax);
194
+ vfloat32m4_t vacc1 = __riscv_vfmv_v_f_f32m4(0.0f, vlmax);
195
+ vfloat32m4_t vacc2 = __riscv_vfmv_v_f_f32m4(0.0f, vlmax);
196
+ vfloat32m4_t vacc3 = __riscv_vfmv_v_f_f32m4(0.0f, vlmax);
197
+ size_t i = 0;
198
+ while (i < d) {
199
+ size_t vl = __riscv_vsetvl_e32m4(d - i);
200
+ vfloat32m4_t vx = __riscv_vle32_v_f32m4(x + i, vl);
201
+ vfloat32m4_t vy = __riscv_vle32_v_f32m4(y0 + i, vl);
202
+ vy = __riscv_vfsub_vv_f32m4(vx, vy, vl);
203
+ vacc0 = __riscv_vfmacc_vv_f32m4_tu(vacc0, vy, vy, vl);
204
+ vy = __riscv_vle32_v_f32m4(y1 + i, vl);
205
+ vy = __riscv_vfsub_vv_f32m4(vx, vy, vl);
206
+ vacc1 = __riscv_vfmacc_vv_f32m4_tu(vacc1, vy, vy, vl);
207
+ vy = __riscv_vle32_v_f32m4(y2 + i, vl);
208
+ vy = __riscv_vfsub_vv_f32m4(vx, vy, vl);
209
+ vacc2 = __riscv_vfmacc_vv_f32m4_tu(vacc2, vy, vy, vl);
210
+ vy = __riscv_vle32_v_f32m4(y3 + i, vl);
211
+ vy = __riscv_vfsub_vv_f32m4(vx, vy, vl);
212
+ vacc3 = __riscv_vfmacc_vv_f32m4_tu(vacc3, vy, vy, vl);
213
+ i += vl;
214
+ }
215
+ dis0 = rvv_reduce(vacc0, vlmax, 0.0f, __riscv_vfredusum_vs_f32m4_f32m1);
216
+ dis1 = rvv_reduce(vacc1, vlmax, 0.0f, __riscv_vfredusum_vs_f32m4_f32m1);
217
+ dis2 = rvv_reduce(vacc2, vlmax, 0.0f, __riscv_vfredusum_vs_f32m4_f32m1);
218
+ dis3 = rvv_reduce(vacc3, vlmax, 0.0f, __riscv_vfredusum_vs_f32m4_f32m1);
151
219
  }
152
220
 
153
221
  template <>
@@ -168,9 +236,8 @@ void fvec_L2sqr_ny_transposed<SIMDLevel::RISCV_RVV>(
168
236
  acc = __riscv_vfmacc_vv_f32m8_tu(acc, vx, vx, vl);
169
237
  i += vl;
170
238
  }
171
- vfloat32m1_t sum = __riscv_vfmv_s_f_f32m1(0.0f, 1);
172
- sum = __riscv_vfredusum_vs_f32m8_f32m1(acc, sum, vlmax);
173
- float x_sqlen = __riscv_vfmv_f_s_f32m1_f32(sum);
239
+ float x_sqlen =
240
+ rvv_reduce(acc, vlmax, 0.0f, __riscv_vfredusum_vs_f32m8_f32m1);
174
241
  i = 0;
175
242
  while (i < ny) {
176
243
  size_t vl = __riscv_vsetvl_e32m8(ny - i);
@@ -214,35 +281,6 @@ void fvec_L2sqr_ny<SIMDLevel::RISCV_RVV>(
214
281
  }
215
282
  }
216
283
 
217
- // Index of the first element equal to the minimum of values[0..n), or n when
218
- // there is none (e.g. n == 0). Shared by the *_nearest and madd_and_argmin
219
- // kernels so the vfmin/vfredmin/vfirst sequence lives in one place.
220
- static size_t rvv_argmin(const float* values, size_t n) {
221
- size_t vlmax = __riscv_vsetvlmax_e32m8();
222
- vfloat32m8_t vmin = __riscv_vfmv_v_f_f32m8(__builtin_inff(), vlmax);
223
- size_t i = 0;
224
- while (i < n) {
225
- size_t vl = __riscv_vsetvl_e32m8(n - i);
226
- vfloat32m8_t vd = __riscv_vle32_v_f32m8(values + i, vl);
227
- vmin = __riscv_vfmin_vv_f32m8_tu(vmin, vmin, vd, vl);
228
- i += vl;
229
- }
230
- vfloat32m1_t rmin = __riscv_vfmv_s_f_f32m1(__builtin_inff(), 1);
231
- rmin = __riscv_vfredmin_vs_f32m8_f32m1(vmin, rmin, vlmax);
232
- float min_val = __riscv_vfmv_f_s_f32m1_f32(rmin);
233
- i = 0;
234
- while (i < n) {
235
- size_t vl = __riscv_vsetvl_e32m8(n - i);
236
- vfloat32m8_t vd = __riscv_vle32_v_f32m8(values + i, vl);
237
- long j = __riscv_vfirst_m_b4(
238
- __riscv_vmfeq_vf_f32m8_b4(vd, min_val, vl), vl);
239
- if (j >= 0)
240
- return i + static_cast<size_t>(j);
241
- i += vl;
242
- }
243
- return n;
244
- }
245
-
246
284
  template <>
247
285
  size_t fvec_L2sqr_ny_nearest<SIMDLevel::RISCV_RVV>(
248
286
  float* distances_tmp_buffer,
@@ -623,16 +623,6 @@ namespace faiss::rabitq::multibit {
623
623
 
624
624
  namespace {
625
625
 
626
- inline float hsum_avx2(__m256 v) {
627
- __m128 hi = _mm256_extractf128_ps(v, 1);
628
- __m128 lo = _mm256_castps256_ps128(v);
629
- lo = _mm_add_ps(lo, hi);
630
- __m128 shuf = _mm_movehdup_ps(lo);
631
- lo = _mm_add_ps(lo, shuf);
632
- shuf = _mm_movehl_ps(shuf, lo);
633
- return _mm_cvtss_f32(_mm_add_ss(lo, shuf));
634
- }
635
-
636
626
  inline float ip_1exbit_avx512(
637
627
  const uint8_t* __restrict sign_bits,
638
628
  const uint8_t* __restrict ex_code,
@@ -664,60 +654,86 @@ inline float ip_1exbit_avx512(
664
654
  return result;
665
655
  }
666
656
 
667
- // AVX2+BMI2 bitplane kernel used as fallback for ex_bits >= 2.
668
- // AVX512 TU has AVX2 available. BMI2 guarded separately since
669
- // VIA Eden X4 has AVX2 without BMI2.
657
+ // Needs BMI2 for _pext_u64. Some AVX2 CPUs lack it, and FAISS_BMI2_FLAGS can
658
+ // be empty, so the dispatcher falls back to the scalar path without it.
670
659
  #ifdef __BMI2__
671
- inline float ip_bitplane_avx2(
660
+ // Bitplane kernel for ex_bits >= 2, 16 dims per iteration. A bitplane is
661
+ // already a bitmask, so it goes into a mask register and one masked add
662
+ // applies its weight. Reads of ex_code run a few bytes past the ex-code
663
+ // section into the record's own trailing factors, so they stay in bounds.
664
+ inline float ip_bitplane_avx512(
672
665
  const uint8_t* __restrict sign_bits,
673
666
  const uint8_t* __restrict ex_code,
674
667
  const float* __restrict rotated_q,
675
668
  size_t d,
676
669
  size_t ex_bits,
677
670
  float cb) {
678
- __m256 acc = _mm256_setzero_ps();
679
- const __m256 v_one = _mm256_set1_ps(1.0f);
680
- const __m256i bit_pos = _mm256_setr_epi32(1, 2, 4, 8, 16, 32, 64, 128);
681
- const __m256i zero = _mm256_setzero_si256();
682
- const __m256 v_cb = _mm256_set1_ps(cb);
671
+ __m512 acc = _mm512_setzero_ps();
672
+ const __m512 v_cb = _mm512_set1_ps(cb);
683
673
 
684
674
  uint64_t pext_masks[7];
685
- __m256 v_weights[8];
675
+ __m512 v_weights[8];
686
676
  for (size_t b = 0; b < ex_bits; b++) {
687
677
  uint64_t m = 0;
688
678
  for (int j = 0; j < 8; j++) {
689
679
  m |= (1ULL << (b + j * ex_bits));
690
680
  }
691
681
  pext_masks[b] = m;
692
- v_weights[b] = _mm256_set1_ps(static_cast<float>(1u << b));
682
+ v_weights[b] = _mm512_set1_ps(static_cast<float>(1u << b));
693
683
  }
694
- v_weights[ex_bits] = _mm256_set1_ps(static_cast<float>(1u << ex_bits));
684
+ v_weights[ex_bits] = _mm512_set1_ps(static_cast<float>(1u << ex_bits));
695
685
 
696
686
  size_t i = 0;
697
- for (; i + 8 <= d; i += 8) {
698
- __m256i sb_cmp = _mm256_cmpgt_epi32(
699
- _mm256_and_si256(_mm256_set1_epi32(sign_bits[i / 8]), bit_pos),
700
- zero);
701
- __m256 recon = _mm256_mul_ps(
702
- _mm256_and_ps(_mm256_castsi256_ps(sb_cmp), v_one),
703
- v_weights[ex_bits]);
687
+ for (; i + 16 <= d; i += 16) {
688
+ uint16_t sb = 0;
689
+ memcpy(&sb, sign_bits + (i / 8), sizeof(uint16_t));
690
+ __m512 recon = _mm512_maskz_mov_ps(
691
+ static_cast<__mmask16>(sb), v_weights[ex_bits]);
692
+
693
+ uint64_t lo64 = 0;
694
+ uint64_t hi64 = 0;
695
+ memcpy(&lo64, ex_code + (i / 8) * ex_bits, sizeof(uint64_t));
696
+ memcpy(&hi64, ex_code + ((i / 8) + 1) * ex_bits, sizeof(uint64_t));
697
+
698
+ for (size_t b = 0; b < ex_bits; b++) {
699
+ const uint32_t plane =
700
+ static_cast<uint32_t>(_pext_u64(lo64, pext_masks[b])) |
701
+ (static_cast<uint32_t>(_pext_u64(hi64, pext_masks[b]))
702
+ << 8);
703
+ recon = _mm512_mask_add_ps(
704
+ recon, static_cast<__mmask16>(plane), recon, v_weights[b]);
705
+ }
704
706
 
705
- uint64_t ex64 = 0;
706
- memcpy(&ex64, ex_code + (i / 8) * ex_bits, sizeof(uint64_t));
707
+ __m512 rq = _mm512_loadu_ps(rotated_q + i);
708
+ acc = _mm512_fmadd_ps(rq, _mm512_add_ps(recon, v_cb), acc);
709
+ }
710
+
711
+ // Half-width step: keeps the scalar tail under 8 dims when d is a multiple
712
+ // of 8 but not of 16 (e.g. 200, 1000). The upper 8 lanes are masked off
713
+ // throughout, and rotated_q is loaded masked so nothing is read past the
714
+ // end.
715
+ if (i + 8 <= d) {
716
+ const __mmask16 low8 = static_cast<__mmask16>(0x00ff);
717
+ __m512 recon = _mm512_maskz_mov_ps(
718
+ static_cast<__mmask16>(sign_bits[i / 8]), v_weights[ex_bits]);
719
+
720
+ uint64_t lo64 = 0;
721
+ memcpy(&lo64, ex_code + (i / 8) * ex_bits, sizeof(uint64_t));
707
722
 
708
723
  for (size_t b = 0; b < ex_bits; b++) {
709
- auto plane = static_cast<uint8_t>(_pext_u64(ex64, pext_masks[b]));
710
- __m256i p_cmp = _mm256_cmpgt_epi32(
711
- _mm256_and_si256(_mm256_set1_epi32(plane), bit_pos), zero);
712
- __m256 p_f = _mm256_and_ps(_mm256_castsi256_ps(p_cmp), v_one);
713
- recon = _mm256_fmadd_ps(p_f, v_weights[b], recon);
724
+ const uint32_t plane =
725
+ static_cast<uint32_t>(_pext_u64(lo64, pext_masks[b]));
726
+ recon = _mm512_mask_add_ps(
727
+ recon, static_cast<__mmask16>(plane), recon, v_weights[b]);
714
728
  }
715
729
 
716
- __m256 rq = _mm256_loadu_ps(rotated_q + i);
717
- acc = _mm256_fmadd_ps(rq, _mm256_add_ps(recon, v_cb), acc);
730
+ __m512 rq = _mm512_maskz_loadu_ps(low8, rotated_q + i);
731
+ acc = _mm512_fmadd_ps(
732
+ rq, _mm512_mask_add_ps(recon, low8, recon, v_cb), acc);
733
+ i += 8;
718
734
  }
719
735
 
720
- float result = hsum_avx2(acc);
736
+ float result = _mm512_reduce_add_ps(acc);
721
737
  result += ip_scalar(sign_bits, ex_code, rotated_q, i, d, ex_bits, cb);
722
738
  return result;
723
739
  }
@@ -739,7 +755,8 @@ float compute_inner_product<SIMDLevel::AVX512>(
739
755
 
740
756
  #ifdef __BMI2__
741
757
  if (ex_bits <= 7) {
742
- return ip_bitplane_avx2(sign_bits, ex_code, rotated_q, d, ex_bits, cb);
758
+ return ip_bitplane_avx512(
759
+ sign_bits, ex_code, rotated_q, d, ex_bits, cb);
743
760
  }
744
761
  #endif
745
762
  return ip_scalar(sign_bits, ex_code, rotated_q, 0, d, ex_bits, cb);
@@ -6,34 +6,31 @@
6
6
  */
7
7
 
8
8
  /**
9
- * @file rabitq_avx512_spr.cpp
9
+ * @file rabitq_avx512_vpopcnt.cpp
10
10
  *
11
- * RaBitQ SIMD kernels specialized for SIMDLevel::AVX512_SPR.
11
+ * RaBitQ SIMD kernels specialized for SIMDLevel::AVX512_VPOPCNT.
12
12
  *
13
- * Sapphire Rapids (SPR) and later Intel microarchitectures expose
14
- * AVX-512 VPOPCNTDQ (vpopcntq), which performs a per-lane 64-bit
15
- * popcount in a single instruction. This is used here to replace the
16
- * multi-step shuffle/pshufb-based popcount used by the generic AVX-512
17
- * specialization in rabitq_avx512.cpp. The popcount-heavy kernels
18
- * (bitwise_and_dot_product, bitwise_xor_dot_product, popcount) become
19
- * substantially shorter and faster on SPR+ as a result.
13
+ * AVX-512 VPOPCNTDQ performs a per-lane 64-bit popcount in a single
14
+ * instruction. It is available on CPUs including Ice Lake, Zen 4, and
15
+ * Sapphire Rapids, independently of the other SPR-only extensions. This
16
+ * replaces the multi-step shuffle-based popcount used by the generic
17
+ * AVX-512 specialization in rabitq_avx512.cpp.
20
18
  *
21
19
  * Build / dispatch behavior:
22
20
  * - faiss_avx512 (AVX-512 only, no SPR features): NOT compiled.
23
21
  * The existing AVX512 specialization in rabitq_avx512.cpp is used.
24
- * - faiss_avx512_spr (statically built for SPR+): compiled. The
25
- * SINGLE_SIMD_LEVEL is AVX512_SPR, so this specialization is
26
- * selected by static dispatch.
22
+ * - faiss_avx512_spr: compiled alongside the full SPR specialization and
23
+ * selected through the SPR -> VPOPCNT fallback.
27
24
  * - faiss with FAISS_OPT_LEVEL=dd (dynamic dispatch): compiled with
28
25
  * -mavx512vpopcntdq as a per-file flag. Selected at runtime when
29
- * SIMDConfig::level == SIMDLevel::AVX512_SPR.
26
+ * the CPU exposes AVX512_VPOPCNTDQ.
30
27
  *
31
28
  * The floating-point multi-bit inner-product kernel does not benefit
32
- * from VPOPCNTDQ, so this TU forwards compute_inner_product<SPR> to
29
+ * from VPOPCNTDQ, so this TU forwards compute_inner_product<VPOPCNT> to
33
30
  * the AVX512 implementation to avoid duplicating that code path.
34
31
  */
35
32
 
36
- #ifdef COMPILE_SIMD_AVX512_SPR
33
+ #ifdef COMPILE_SIMD_AVX512_VPOPCNT
37
34
 
38
35
  #include <faiss/utils/popcount.h>
39
36
  #include <faiss/utils/rabitq_simd.h>
@@ -47,7 +44,7 @@
47
44
  namespace faiss::rabitq {
48
45
 
49
46
  // Forward declarations for the AVX512 specializations defined in
50
- // rabitq_avx512.cpp. They live in the same TU group on SPR builds, so
47
+ // rabitq_avx512.cpp. They live in the same TU group in supported builds, so
51
48
  // we can reuse them as a tail handler / fallback. Declaring rather
52
49
  // than redefining avoids ODR risk and keeps a single source of truth
53
50
  // for the floating-point kernel.
@@ -75,8 +72,8 @@ inline __m512i popcount_512_vpopcntdq(__m512i v) {
75
72
  }
76
73
 
77
74
  // 256-bit popcount using AVX-512VL VPOPCNTDQ.
78
- // AVX512VL is part of the SPR feature set, so vpopcntq is available
79
- // on 256-bit registers via _mm256_popcnt_epi64.
75
+ // Baseline AVX-512 includes AVX512VL, so VPOPCNTDQ is also available on
76
+ // 256-bit registers via _mm256_popcnt_epi64.
80
77
  inline __m256i popcount_256_vpopcntdq(__m256i v) {
81
78
  return _mm256_popcnt_epi64(v);
82
79
  }
@@ -101,7 +98,7 @@ inline uint64_t reduce_add_128(__m128i v) {
101
98
  } // namespace
102
99
 
103
100
  template <>
104
- uint64_t bitwise_and_dot_product<SIMDLevel::AVX512_SPR>(
101
+ uint64_t bitwise_and_dot_product<SIMDLevel::AVX512_VPOPCNT>(
105
102
  const uint8_t* query,
106
103
  const uint8_t* data,
107
104
  size_t size,
@@ -187,7 +184,7 @@ uint64_t bitwise_and_dot_product<SIMDLevel::AVX512_SPR>(
187
184
 
188
185
  template <>
189
186
  BitwiseAndDotProductResult bitwise_and_dot_product_with_popcount<
190
- SIMDLevel::AVX512_SPR>(
187
+ SIMDLevel::AVX512_VPOPCNT>(
191
188
  const uint8_t* query,
192
189
  const uint8_t* data,
193
190
  size_t size,
@@ -278,7 +275,7 @@ BitwiseAndDotProductResult bitwise_and_dot_product_with_popcount<
278
275
  }
279
276
 
280
277
  template <>
281
- uint64_t bitwise_xor_dot_product<SIMDLevel::AVX512_SPR>(
278
+ uint64_t bitwise_xor_dot_product<SIMDLevel::AVX512_VPOPCNT>(
282
279
  const uint8_t* query,
283
280
  const uint8_t* data,
284
281
  size_t size,
@@ -357,7 +354,7 @@ uint64_t bitwise_xor_dot_product<SIMDLevel::AVX512_SPR>(
357
354
  }
358
355
 
359
356
  template <>
360
- uint64_t popcount<SIMDLevel::AVX512_SPR>(const uint8_t* data, size_t size) {
357
+ uint64_t popcount<SIMDLevel::AVX512_VPOPCNT>(const uint8_t* data, size_t size) {
361
358
  uint64_t sum = 0;
362
359
  size_t offset = 0;
363
360
 
@@ -419,7 +416,7 @@ float compute_inner_product<SIMDLevel::AVX512>(
419
416
  float cb);
420
417
 
421
418
  template <>
422
- float compute_inner_product<SIMDLevel::AVX512_SPR>(
419
+ float compute_inner_product<SIMDLevel::AVX512_VPOPCNT>(
423
420
  const uint8_t* __restrict sign_bits,
424
421
  const uint8_t* __restrict ex_code,
425
422
  const float* __restrict rotated_q,
@@ -432,4 +429,4 @@ float compute_inner_product<SIMDLevel::AVX512_SPR>(
432
429
 
433
430
  } // namespace faiss::rabitq::multibit
434
431
 
435
- #endif // COMPILE_SIMD_AVX512_SPR
432
+ #endif // COMPILE_SIMD_AVX512_VPOPCNT
@@ -9,13 +9,8 @@
9
9
 
10
10
  // Private dispatch wrapper for SuperKMeans's block_l2. Routes to the
11
11
  // highest available SIMD specialization at runtime (DD mode) or the
12
- // compiled-in level (static mode). aarch64 currently falls through to the
13
- // scalar primary template; adding NEON/SVE means just adding a new
14
- // specialization file alongside the AVX ones.
12
+ // compiled-in level (static mode).
15
13
  //
16
- // Known perf gap: aarch64 (NEON/SVE) specializations are not implemented yet.
17
- // aarch64 falls through to the scalar primary template. Validating SVE requires
18
- // a Graviton-class host; deferred to a focused follow-up.
19
14
 
20
15
  #include <faiss/impl/simd_dispatch.h>
21
16
  #include <faiss/utils/simd_impl/super_kmeans_kernels.h>
@@ -24,7 +19,7 @@ namespace faiss {
24
19
  namespace detail {
25
20
 
26
21
  inline float block_l2_dispatch(const float* x, const float* y, int n) {
27
- return with_selected_simd_levels<AVAILABLE_SIMD_LEVELS_A0>(
22
+ return with_simd_level_with_sve(
28
23
  [&]<SIMDLevel SL>() { return block_l2<SL>(x, y, n); });
29
24
  }
30
25
 
@@ -16,7 +16,7 @@ namespace detail {
16
16
 
17
17
  // Squared L2 over `n` dimensions; n in [1, pdx_block_size].
18
18
  // Primary template is the scalar fallback; SIMDLevels without a dedicated
19
- // specialization (ARM_NEON, ARM_SVE, NONE, ...) use it directly.
19
+ // specialization (ARM_NEON, NONE, ...) use it directly.
20
20
  template <SIMDLevel Level>
21
21
  inline float block_l2(const float* x, const float* y, int n) {
22
22
  float s = 0.0f;
@@ -39,5 +39,10 @@ template <>
39
39
  float block_l2<SIMDLevel::AVX512>(const float* x, const float* y, int n);
40
40
  #endif
41
41
 
42
+ #ifdef COMPILE_SIMD_ARM_SVE
43
+ template <>
44
+ float block_l2<SIMDLevel::ARM_SVE>(const float* x, const float* y, int n);
45
+ #endif
46
+
42
47
  } // namespace detail
43
48
  } // namespace faiss
@@ -0,0 +1,34 @@
1
+ /*
2
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
3
+ *
4
+ * This source code is licensed under the MIT license found in the
5
+ * LICENSE file in the root directory of this source tree.
6
+ */
7
+
8
+ #ifdef COMPILE_SIMD_ARM_SVE
9
+
10
+ #include <faiss/utils/simd_impl/super_kmeans_kernels.h>
11
+
12
+ #include <arm_sve.h>
13
+
14
+ namespace faiss {
15
+ namespace detail {
16
+
17
+ template <>
18
+ float block_l2<SIMDLevel::ARM_SVE>(const float* x, const float* y, int n) {
19
+ svfloat32_t acc = svdup_n_f32(0.0f);
20
+ const int lanes = static_cast<int>(svcntw());
21
+ for (int m = 0; m < n; m += lanes) {
22
+ const svbool_t pg = svwhilelt_b32(m, n);
23
+ const svfloat32_t xv = svld1_f32(pg, x + m);
24
+ const svfloat32_t yv = svld1_f32(pg, y + m);
25
+ const svfloat32_t diff = svsub_f32_x(pg, xv, yv);
26
+ acc = svmla_f32_m(pg, acc, diff, diff);
27
+ }
28
+ return svaddv_f32(svptrue_b32(), acc);
29
+ }
30
+
31
+ } // namespace detail
32
+ } // namespace faiss
33
+
34
+ #endif // COMPILE_SIMD_ARM_SVE