faiss 0.6.2 → 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 (178) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +8 -0
  3. data/lib/faiss/version.rb +1 -1
  4. data/vendor/faiss/faiss/AutoTune.cpp +3 -1
  5. data/vendor/faiss/faiss/Clustering.cpp +9 -1
  6. data/vendor/faiss/faiss/Clustering.h +8 -0
  7. data/vendor/faiss/faiss/IVFlib.cpp +14 -3
  8. data/vendor/faiss/faiss/Index.h +2 -2
  9. data/vendor/faiss/faiss/IndexAdditiveQuantizer.cpp +9 -10
  10. data/vendor/faiss/faiss/IndexAdditiveQuantizerFastScan.cpp +2 -3
  11. data/vendor/faiss/faiss/IndexBinaryFromFloat.cpp +24 -4
  12. data/vendor/faiss/faiss/IndexBinaryHNSW.cpp +16 -145
  13. data/vendor/faiss/faiss/IndexBinaryHNSW.h +0 -6
  14. data/vendor/faiss/faiss/IndexBinaryHash.cpp +5 -9
  15. data/vendor/faiss/faiss/IndexBinaryIVF.cpp +8 -18
  16. data/vendor/faiss/faiss/IndexBinaryIVF.h +8 -1
  17. data/vendor/faiss/faiss/IndexEDEN.cpp +273 -0
  18. data/vendor/faiss/faiss/IndexEDEN.h +57 -0
  19. data/vendor/faiss/faiss/IndexFastScan.cpp +15 -4
  20. data/vendor/faiss/faiss/IndexFlat.cpp +21 -54
  21. data/vendor/faiss/faiss/IndexFlat.h +2 -2
  22. data/vendor/faiss/faiss/IndexHNSW.cpp +311 -102
  23. data/vendor/faiss/faiss/IndexHNSW.h +31 -7
  24. data/vendor/faiss/faiss/IndexIDMap.cpp +26 -8
  25. data/vendor/faiss/faiss/IndexIDMap.h +2 -0
  26. data/vendor/faiss/faiss/IndexIVF.cpp +36 -10
  27. data/vendor/faiss/faiss/IndexIVFAdditiveQuantizer.cpp +1 -1
  28. data/vendor/faiss/faiss/IndexIVFAdditiveQuantizerFastScan.cpp +3 -4
  29. data/vendor/faiss/faiss/IndexIVFEDEN.cpp +302 -0
  30. data/vendor/faiss/faiss/IndexIVFEDEN.h +70 -0
  31. data/vendor/faiss/faiss/IndexIVFFastScan.cpp +5 -6
  32. data/vendor/faiss/faiss/IndexIVFFlat.cpp +3 -4
  33. data/vendor/faiss/faiss/IndexIVFIndependentQuantizer.cpp +1 -1
  34. data/vendor/faiss/faiss/IndexIVFPQ.cpp +49 -23
  35. data/vendor/faiss/faiss/IndexIVFPQ.h +11 -0
  36. data/vendor/faiss/faiss/IndexIVFPQFastScan.cpp +0 -1
  37. data/vendor/faiss/faiss/IndexIVFRaBitQ.cpp +19 -49
  38. data/vendor/faiss/faiss/IndexIVFRaBitQFastScan.cpp +180 -76
  39. data/vendor/faiss/faiss/IndexIVFRaBitQFastScan.h +5 -4
  40. data/vendor/faiss/faiss/IndexIVFSpectralHash.cpp +8 -6
  41. data/vendor/faiss/faiss/IndexLSH.cpp +2 -3
  42. data/vendor/faiss/faiss/IndexLattice.cpp +5 -0
  43. data/vendor/faiss/faiss/IndexNNDescent.cpp +9 -2
  44. data/vendor/faiss/faiss/IndexNSG.cpp +7 -2
  45. data/vendor/faiss/faiss/IndexPQ.cpp +6 -8
  46. data/vendor/faiss/faiss/IndexPreTransform.cpp +15 -0
  47. data/vendor/faiss/faiss/IndexRaBitQ.cpp +2 -2
  48. data/vendor/faiss/faiss/IndexRaBitQFastScan.cpp +1 -2
  49. data/vendor/faiss/faiss/IndexRaBitQFastScan.h +5 -1
  50. data/vendor/faiss/faiss/IndexRefine.cpp +30 -1
  51. data/vendor/faiss/faiss/IndexReplicas.cpp +1 -2
  52. data/vendor/faiss/faiss/IndexShards.cpp +5 -5
  53. data/vendor/faiss/faiss/IndexShardsIVF.cpp +6 -5
  54. data/vendor/faiss/faiss/MetaIndexes.cpp +2 -4
  55. data/vendor/faiss/faiss/SuperKMeans.cpp +286 -247
  56. data/vendor/faiss/faiss/SuperKMeans.h +33 -2
  57. data/vendor/faiss/faiss/VectorTransform.cpp +71 -2
  58. data/vendor/faiss/faiss/VectorTransform.h +3 -0
  59. data/vendor/faiss/faiss/clone_index.cpp +8 -0
  60. data/vendor/faiss/faiss/factory_tools.cpp +47 -4
  61. data/vendor/faiss/faiss/gpu/GpuCloner.cpp +11 -11
  62. data/vendor/faiss/faiss/gpu/GpuClonerOptions.h +1 -5
  63. data/vendor/faiss/faiss/gpu/GpuDistance.h +2 -5
  64. data/vendor/faiss/faiss/gpu/GpuIndex.h +38 -16
  65. data/vendor/faiss/faiss/gpu/GpuIndexCagra.h +71 -1
  66. data/vendor/faiss/faiss/gpu/GpuIndexIVF.h +17 -0
  67. data/vendor/faiss/faiss/gpu/GpuIndexIVFScalarQuantizer.h +16 -0
  68. data/vendor/faiss/faiss/gpu/perf/PerfClustering.cpp +1 -1
  69. data/vendor/faiss/faiss/gpu/perf/PerfIVFPQAdd.cpp +2 -2
  70. data/vendor/faiss/faiss/gpu/test/TestGpuIndexIVFScalarQuantizer.cpp +180 -0
  71. data/vendor/faiss/faiss/gpu_metal/MetalIndexIVFFlat.h +1 -5
  72. data/vendor/faiss/faiss/gpu_metal/MetalIndexIVFPQ.h +88 -0
  73. data/vendor/faiss/faiss/gpu_metal/impl/MetalIVFPQ.h +134 -0
  74. data/vendor/faiss/faiss/impl/AdditiveQuantizer.cpp +1 -1
  75. data/vendor/faiss/faiss/impl/ClusteringInitialization.cpp +7 -4
  76. data/vendor/faiss/faiss/impl/DistanceComputer.h +34 -0
  77. data/vendor/faiss/faiss/impl/EDENQuantizer.h +119 -0
  78. data/vendor/faiss/faiss/impl/HNSW.cpp +528 -267
  79. data/vendor/faiss/faiss/impl/HNSW.h +46 -7
  80. data/vendor/faiss/faiss/impl/IDSelector.h +44 -0
  81. data/vendor/faiss/faiss/impl/LocalSearchQuantizer.cpp +2 -2
  82. data/vendor/faiss/faiss/impl/NNDescent.cpp +10 -3
  83. data/vendor/faiss/faiss/impl/NSG.cpp +3 -1
  84. data/vendor/faiss/faiss/impl/Panorama.h +20 -9
  85. data/vendor/faiss/faiss/impl/PolysemousTraining.cpp +152 -84
  86. data/vendor/faiss/faiss/impl/ProductQuantizer.cpp +38 -26
  87. data/vendor/faiss/faiss/impl/RaBitQUtils.cpp +45 -37
  88. data/vendor/faiss/faiss/impl/RaBitQUtils.h +35 -0
  89. data/vendor/faiss/faiss/impl/RaBitQuantizer.cpp +239 -72
  90. data/vendor/faiss/faiss/impl/RaBitQuantizer.h +66 -4
  91. data/vendor/faiss/faiss/impl/RaBitQuantizerMultiBit.cpp +4 -13
  92. data/vendor/faiss/faiss/impl/ResultHandler.h +34 -34
  93. data/vendor/faiss/faiss/impl/ScalarQuantizer.cpp +287 -84
  94. data/vendor/faiss/faiss/impl/ScalarQuantizer.h +26 -10
  95. data/vendor/faiss/faiss/impl/ThreadedIndex-inl.h +2 -2
  96. data/vendor/faiss/faiss/impl/VisitedTable.cpp +22 -2
  97. data/vendor/faiss/faiss/impl/VisitedTable.h +20 -0
  98. data/vendor/faiss/faiss/impl/binary_hamming/IndexBinaryIVF_impl.h +90 -14
  99. data/vendor/faiss/faiss/impl/binary_hamming/avx2.cpp +4 -4
  100. data/vendor/faiss/faiss/impl/expanded_scanners.h +5 -1
  101. data/vendor/faiss/faiss/impl/fast_scan/decompose_qbs.h +1 -0
  102. data/vendor/faiss/faiss/impl/fast_scan/dispatching.h +35 -2
  103. data/vendor/faiss/faiss/impl/hnsw/LockVector.cpp +1 -1
  104. data/vendor/faiss/faiss/impl/index_read.cpp +491 -50
  105. data/vendor/faiss/faiss/impl/index_write.cpp +86 -30
  106. data/vendor/faiss/faiss/impl/lattice_Zn.cpp +8 -9
  107. data/vendor/faiss/faiss/impl/platform_macros.h +3 -1
  108. data/vendor/faiss/faiss/impl/polysemous_training/avx512.cpp +284 -0
  109. data/vendor/faiss/faiss/impl/polysemous_training/dispatch.h +115 -0
  110. data/vendor/faiss/faiss/impl/pq_code_distance/IVFPQScanner_impl.h +73 -39
  111. data/vendor/faiss/faiss/impl/pq_code_distance/IVFPQ_QueryTables.cpp +0 -1
  112. data/vendor/faiss/faiss/impl/pq_code_distance/PQDistanceComputer_impl.h +26 -15
  113. data/vendor/faiss/faiss/impl/pq_code_distance/avx2.cpp +4 -4
  114. data/vendor/faiss/faiss/impl/pq_code_distance/pq_code_distance-generic.cpp +4 -4
  115. data/vendor/faiss/faiss/impl/result_handler/ResultHandler.cpp +195 -0
  116. data/vendor/faiss/faiss/impl/result_handler/avx2.cpp +133 -0
  117. data/vendor/faiss/faiss/impl/result_handler/avx512.cpp +281 -0
  118. data/vendor/faiss/faiss/impl/scalar_quantizer/EDENQuantizer-avx2.cpp +72 -0
  119. data/vendor/faiss/faiss/impl/scalar_quantizer/EDENQuantizer-avx512.cpp +228 -0
  120. data/vendor/faiss/faiss/impl/scalar_quantizer/EDENQuantizer.cpp +887 -0
  121. data/vendor/faiss/faiss/impl/scalar_quantizer/distance_computers.h +2 -2
  122. data/vendor/faiss/faiss/impl/scalar_quantizer/quantizers.h +9 -8
  123. data/vendor/faiss/faiss/impl/scalar_quantizer/sq-avx2.cpp +90 -24
  124. data/vendor/faiss/faiss/impl/scalar_quantizer/sq-avx512-impl.h +30 -30
  125. data/vendor/faiss/faiss/impl/scalar_quantizer/sq-avx512-spr.cpp +4 -5
  126. data/vendor/faiss/faiss/impl/scalar_quantizer/sq-avx512.cpp +101 -34
  127. data/vendor/faiss/faiss/impl/scalar_quantizer/sq-dispatch.h +169 -13
  128. data/vendor/faiss/faiss/impl/scalar_quantizer/sq-neon.cpp +125 -26
  129. data/vendor/faiss/faiss/impl/simd_dispatch.h +70 -31
  130. data/vendor/faiss/faiss/index_factory.cpp +40 -7
  131. data/vendor/faiss/faiss/invlists/DirectMap.cpp +1 -1
  132. data/vendor/faiss/faiss/invlists/InvertedLists.cpp +9 -6
  133. data/vendor/faiss/faiss/invlists/OnDiskInvertedLists.cpp +29 -8
  134. data/vendor/faiss/faiss/python/python_callbacks.cpp +3 -1
  135. data/vendor/faiss/faiss/svs/IndexSVSFaissUtils.h +60 -0
  136. data/vendor/faiss/faiss/svs/IndexSVSFlat.cpp +26 -1
  137. data/vendor/faiss/faiss/svs/IndexSVSFlat.h +13 -0
  138. data/vendor/faiss/faiss/svs/IndexSVSIVF.cpp +1 -1
  139. data/vendor/faiss/faiss/svs/IndexSVSIVFLeanVec.cpp +1 -1
  140. data/vendor/faiss/faiss/svs/IndexSVSVamana.cpp +47 -5
  141. data/vendor/faiss/faiss/svs/IndexSVSVamana.h +23 -3
  142. data/vendor/faiss/faiss/svs/IndexSVSVamanaLVQ.cpp +4 -2
  143. data/vendor/faiss/faiss/svs/IndexSVSVamanaLVQ.h +2 -1
  144. data/vendor/faiss/faiss/svs/IndexSVSVamanaLeanVec.cpp +10 -4
  145. data/vendor/faiss/faiss/svs/IndexSVSVamanaLeanVec.h +2 -1
  146. data/vendor/faiss/faiss/utils/approx_topk_hamming/approx_topk_hamming.h +1 -1
  147. data/vendor/faiss/faiss/utils/distances.cpp +30 -11
  148. data/vendor/faiss/faiss/utils/distances_dispatch.h +30 -24
  149. data/vendor/faiss/faiss/utils/distances_fused/distances_fused.cpp +1 -1
  150. data/vendor/faiss/faiss/utils/distances_simd.cpp +4 -3
  151. data/vendor/faiss/faiss/utils/extra_distances.cpp +4 -14
  152. data/vendor/faiss/faiss/utils/extra_distances.h +1 -2
  153. data/vendor/faiss/faiss/utils/hamming.cpp +16 -10
  154. data/vendor/faiss/faiss/utils/hamming.h +10 -1
  155. data/vendor/faiss/faiss/utils/hamming_distance/common.h +14 -3
  156. data/vendor/faiss/faiss/utils/hamming_distance/hamming_avx512_vpopcnt.cpp +24 -0
  157. data/vendor/faiss/faiss/utils/hamming_distance/hamming_computer-avx512.h +1 -1
  158. data/vendor/faiss/faiss/utils/hamming_distance/{hamming_computer-avx512_spr.h → hamming_computer-avx512_vpopcnt.h} +85 -24
  159. data/vendor/faiss/faiss/utils/hamming_distance/hamming_impl.h +141 -0
  160. data/vendor/faiss/faiss/utils/quantize_lut.cpp +29 -8
  161. data/vendor/faiss/faiss/utils/rabitq_simd.h +202 -0
  162. data/vendor/faiss/faiss/utils/simd_impl/distances_arm_sve.cpp +194 -30
  163. data/vendor/faiss/faiss/utils/simd_impl/distances_avx2.cpp +0 -1
  164. data/vendor/faiss/faiss/utils/simd_impl/distances_avx512.cpp +263 -15
  165. data/vendor/faiss/faiss/utils/simd_impl/distances_rvv.cpp +198 -18
  166. data/vendor/faiss/faiss/utils/simd_impl/rabitq_avx2.cpp +245 -0
  167. data/vendor/faiss/faiss/utils/simd_impl/rabitq_avx512.cpp +330 -40
  168. data/vendor/faiss/faiss/utils/simd_impl/{rabitq_avx512_spr.cpp → rabitq_avx512_vpopcnt.cpp} +112 -23
  169. data/vendor/faiss/faiss/utils/simd_impl/rabitq_neon.cpp +11 -0
  170. data/vendor/faiss/faiss/utils/simd_impl/rabitq_rvv.cpp +143 -6
  171. data/vendor/faiss/faiss/utils/simd_impl/super_kmeans_dispatch.h +2 -7
  172. data/vendor/faiss/faiss/utils/simd_impl/super_kmeans_kernels.h +6 -1
  173. data/vendor/faiss/faiss/utils/simd_impl/super_kmeans_kernels_sve.cpp +34 -0
  174. data/vendor/faiss/faiss/utils/simd_levels.cpp +196 -47
  175. data/vendor/faiss/faiss/utils/simd_levels.h +33 -8
  176. data/vendor/faiss/faiss/utils/utils.cpp +9 -27
  177. metadata +21 -5
  178. data/vendor/faiss/faiss/utils/hamming_distance/hamming_avx512_spr.cpp +0 -15
@@ -29,19 +29,31 @@ struct WrappedSearchResult {
29
29
  ResultHandler& res;
30
30
  size_t nup = 0;
31
31
  idx_t list_no;
32
-
32
+ size_t list_size;
33
33
  const idx_t* ids;
34
34
  const IDSelector* sel;
35
+ IDSelectorContextDispatch dispatch;
35
36
 
36
37
  WrappedSearchResult(
37
38
  idx_t list_no_in,
39
+ size_t list_size_in,
38
40
  const idx_t* ids_in,
39
41
  const IDSelector* sel_in,
40
42
  ResultHandler& res_in)
41
- : res(res_in), list_no(list_no_in), ids(ids_in), sel(sel_in) {}
43
+ : res(res_in),
44
+ list_no(list_no_in),
45
+ list_size(list_size_in),
46
+ ids(ids_in),
47
+ sel(sel_in),
48
+ // A selector implies real ids, so ids==nullptr iff store_pairs;
49
+ // that disables the context path exactly when ids[] is synthetic.
50
+ dispatch(sel_in, /*store_pairs=*/ids_in == nullptr) {}
42
51
 
43
52
  inline bool skip_entry(idx_t j) {
44
- return use_sel && !sel->is_member(ids[j]);
53
+ return use_sel &&
54
+ !dispatch.is_member(
55
+ ids[j],
56
+ IDScanContext{ids, list_size, static_cast<size_t>(j)});
45
57
  }
46
58
 
47
59
  inline void add(idx_t j, float dis) {
@@ -204,41 +216,41 @@ struct IVFPQScannerT : QueryTables {
204
216
  }
205
217
  }
206
218
 
219
+ float on_the_fly_dis0 = 0;
220
+
221
+ void prepare_on_the_fly_distance() {
222
+ on_the_fly_dis0 = 0;
223
+ if (!by_residual) {
224
+ return;
225
+ }
226
+ if (METRIC_TYPE == METRIC_INNER_PRODUCT) {
227
+ ivfpq.quantizer->reconstruct(key, residual_vec);
228
+ on_the_fly_dis0 = fvec_inner_product_dispatch(residual_vec, qi, d);
229
+ } else {
230
+ ivfpq.quantizer->compute_residual(qi, residual_vec, key);
231
+ }
232
+ }
233
+
234
+ float distance_to_code_on_the_fly(const uint8_t* code) const {
235
+ pq.decode(code, decoded_vec);
236
+ if (METRIC_TYPE == METRIC_INNER_PRODUCT) {
237
+ return on_the_fly_dis0 +
238
+ fvec_inner_product_dispatch(decoded_vec, qi, d);
239
+ }
240
+ const float* dvec = by_residual ? residual_vec : qi;
241
+ return fvec_L2sqr_dispatch(decoded_vec, dvec, d);
242
+ }
243
+
207
244
  /// nothing is precomputed: access residuals on-the-fly
208
245
  template <class SearchResultType>
209
246
  void scan_on_the_fly_dist(
210
247
  size_t ncode,
211
248
  const uint8_t* codes,
212
249
  SearchResultType& res) const {
213
- const float* dvec;
214
- float local_dis0 = 0;
215
- if (by_residual) {
216
- if (METRIC_TYPE == METRIC_INNER_PRODUCT) {
217
- ivfpq.quantizer->reconstruct(key, residual_vec);
218
- local_dis0 = fvec_inner_product_dispatch(residual_vec, qi, d);
219
- } else {
220
- ivfpq.quantizer->compute_residual(qi, residual_vec, key);
221
- }
222
- dvec = residual_vec;
223
- } else {
224
- dvec = qi;
225
- local_dis0 = 0;
226
- }
227
-
228
250
  for (size_t j = 0; j < ncode; j++, codes += pq.code_size) {
229
- if (res.skip_entry(j)) {
230
- continue;
231
- }
232
- pq.decode(codes, decoded_vec);
233
-
234
- float dis;
235
- if (METRIC_TYPE == METRIC_INNER_PRODUCT) {
236
- dis = local_dis0 +
237
- fvec_inner_product_dispatch(decoded_vec, qi, d);
238
- } else {
239
- dis = fvec_L2sqr_dispatch(decoded_vec, dvec, d);
251
+ if (!res.skip_entry(j)) {
252
+ res.add(j, distance_to_code_on_the_fly(codes));
240
253
  }
241
- res.add(j, dis);
242
254
  }
243
255
  }
244
256
 
@@ -426,7 +438,6 @@ struct IVFPQScannerT : QueryTables {
426
438
  *
427
439
  * precompute_mode is how much we precompute (2 = precompute distance tables,
428
440
  * 1 = precompute pointers to distances, 0 = compute distances one by one).
429
- * Currently only 2 is supported
430
441
  *
431
442
  * use_sel: store or ignore the IDSelector
432
443
  */
@@ -451,20 +462,29 @@ struct IVFPQScanner : IVFPQScannerT<idx_t, METRIC_TYPE, PQCodeDist>,
451
462
  }
452
463
 
453
464
  void set_query(const float* query) override {
454
- this->init_query(query);
465
+ if (precompute_mode == 0) {
466
+ this->qi = query;
467
+ } else {
468
+ this->init_query(query);
469
+ }
455
470
  }
456
471
 
457
472
  void set_list(idx_t list_no_in, float coarse_dis_in) override {
458
473
  this->list_no = list_no_in;
459
474
  this->init_list(list_no_in, coarse_dis_in, precompute_mode);
475
+ if (precompute_mode == 0) {
476
+ this->prepare_on_the_fly_distance();
477
+ }
460
478
  }
461
479
 
462
480
  float distance_to_code(const uint8_t* code) const override {
481
+ if (precompute_mode == 0) {
482
+ return this->distance_to_code_on_the_fly(code);
483
+ }
463
484
  FAISS_THROW_IF_NOT(precompute_mode == 2);
464
- float dis = this->dis0 +
485
+ return this->dis0 +
465
486
  PQCodeDist::distance_single_code(
466
- this->pq.M, this->pq.nbits, this->sim_table, code);
467
- return dis;
487
+ this->pq.M, this->pq.nbits, this->sim_table, code);
468
488
  }
469
489
 
470
490
  size_t scan_codes(
@@ -474,6 +494,7 @@ struct IVFPQScanner : IVFPQScannerT<idx_t, METRIC_TYPE, PQCodeDist>,
474
494
  ResultHandler& handler) const override {
475
495
  WrappedSearchResult<C, use_sel> res(
476
496
  this->key,
497
+ ncode,
477
498
  this->store_pairs ? nullptr : ids,
478
499
  this->sel,
479
500
  handler);
@@ -498,27 +519,40 @@ template <SIMDLevel SL>
498
519
  InvertedListScanner* make_IVFPQInvertedListScanner(
499
520
  const IndexIVFPQ& ivfpq,
500
521
  bool store_pairs,
501
- const IDSelector* sel);
522
+ const IDSelector* sel,
523
+ IndexIVFPQ::ScannerMode mode);
524
+
525
+ template <SIMDLevel SL>
526
+ inline InvertedListScanner* make_IVFPQInvertedListScanner(
527
+ const IndexIVFPQ& ivfpq,
528
+ bool store_pairs,
529
+ const IDSelector* sel) {
530
+ return make_IVFPQInvertedListScanner<SL>(
531
+ ivfpq, store_pairs, sel, IndexIVFPQ::ScannerMode::Precomputed);
532
+ }
502
533
 
503
534
  // NOLINTNEXTLINE(facebook-hte-MisplacedTemplateSpecialization)
504
535
  template <>
505
536
  InvertedListScanner* make_IVFPQInvertedListScanner<THE_SIMD_LEVEL>(
506
537
  const IndexIVFPQ& ivfpq,
507
538
  bool store_pairs,
508
- const IDSelector* sel) {
539
+ const IDSelector* sel,
540
+ IndexIVFPQ::ScannerMode mode) {
541
+ const int precompute_mode =
542
+ mode == IndexIVFPQ::ScannerMode::Precomputed ? 2 : 0;
509
543
  auto make = [&]<class PQCodeDist, bool use_sel>() -> InvertedListScanner* {
510
544
  if (ivfpq.metric_type == METRIC_INNER_PRODUCT) {
511
545
  return new IVFPQScanner<
512
546
  METRIC_INNER_PRODUCT,
513
547
  CMin<float, idx_t>,
514
548
  PQCodeDist,
515
- use_sel>(ivfpq, store_pairs, 2, sel);
549
+ use_sel>(ivfpq, store_pairs, precompute_mode, sel);
516
550
  } else if (ivfpq.metric_type == METRIC_L2) {
517
551
  return new IVFPQScanner<
518
552
  METRIC_L2,
519
553
  CMax<float, idx_t>,
520
554
  PQCodeDist,
521
- use_sel>(ivfpq, store_pairs, 2, sel);
555
+ use_sel>(ivfpq, store_pairs, precompute_mode, sel);
522
556
  } else {
523
557
  FAISS_THROW_MSG("unsupported metric type");
524
558
  }
@@ -7,7 +7,6 @@
7
7
 
8
8
  #include <faiss/impl/pq_code_distance/IVFPQ_QueryTables.h>
9
9
 
10
- #include <faiss/Clustering.h>
11
10
  #include <faiss/utils/distances_dispatch.h>
12
11
 
13
12
  namespace faiss {
@@ -20,23 +20,39 @@ namespace faiss {
20
20
  namespace pq_code_distance {
21
21
 
22
22
  template <class PQCodeDist>
23
- struct PQDistanceComputer : FlatCodesDistanceComputer {
23
+ struct PQDistanceComputer final : FlatCodesDistanceComputer {
24
24
  using PQDecoder = typename PQCodeDist::PQDecoder;
25
- size_t d;
26
25
  MetricType metric;
27
- idx_t nb;
28
26
  const ProductQuantizer& pq;
29
27
  const float* sdc;
30
28
  std::vector<float> precomputed_table;
31
- size_t ndis;
32
- const float* q;
33
29
 
34
30
  float distance_to_code(const uint8_t* code) final {
35
- ndis++;
36
-
37
- float dis = PQCodeDist::distance_single_code(
31
+ return PQCodeDist::distance_single_code(
38
32
  pq.M, pq.nbits, precomputed_table.data(), code);
39
- return dis;
33
+ }
34
+
35
+ void distance_to_code_batch_4(
36
+ const uint8_t* c1,
37
+ const uint8_t* c2,
38
+ const uint8_t* c3,
39
+ const uint8_t* c4,
40
+ float& d1,
41
+ float& d2,
42
+ float& d3,
43
+ float& d4) override {
44
+ PQCodeDist::distance_four_codes(
45
+ pq.M,
46
+ pq.nbits,
47
+ precomputed_table.data(),
48
+ c1,
49
+ c2,
50
+ c3,
51
+ c4,
52
+ d1,
53
+ d2,
54
+ d3,
55
+ d4);
40
56
  }
41
57
 
42
58
  float symmetric_dis(idx_t i, idx_t j) override {
@@ -50,7 +66,6 @@ struct PQDistanceComputer : FlatCodesDistanceComputer {
50
66
  accu += sdci[codei.decode() + (codej.decode() << codei.nbits)];
51
67
  sdci += uint64_t(1) << (2 * codei.nbits);
52
68
  }
53
- ndis++;
54
69
  return accu;
55
70
  }
56
71
 
@@ -58,18 +73,14 @@ struct PQDistanceComputer : FlatCodesDistanceComputer {
58
73
  : FlatCodesDistanceComputer(
59
74
  storage.codes.data(),
60
75
  storage.code_size),
61
- pq(storage.pq),
62
- q(nullptr) {
76
+ pq(storage.pq) {
63
77
  precomputed_table.resize(pq.M * pq.ksub);
64
- nb = storage.ntotal;
65
- d = storage.d;
66
78
  metric = storage.metric_type;
67
79
  if (pq.sdc_table.size() == pq.ksub * pq.ksub * pq.M) {
68
80
  sdc = pq.sdc_table.data();
69
81
  } else {
70
82
  sdc = nullptr;
71
83
  }
72
- ndis = 0;
73
84
  }
74
85
 
75
86
  void set_query(const float* x) override {
@@ -9,15 +9,15 @@
9
9
 
10
10
  #define THE_SIMD_LEVEL SIMDLevel::AVX2
11
11
 
12
- // NOLINTNEXTLINE(facebook-hte-InlineHeader)
12
+ // NOLINTNEXTLINE(facebook-hte-InlineHeader,facebook-unused-include-check)
13
13
  #include <faiss/impl/pq_code_distance/pq_code_distance-avx2.h>
14
- // NOLINTNEXTLINE(facebook-hte-InlineHeader)
14
+ // NOLINTNEXTLINE(facebook-hte-InlineHeader,facebook-unused-include-check)
15
15
  #include <faiss/utils/hamming_distance/hamming_computer-avx2.h>
16
16
  // NOLINTNEXTLINE(facebook-hte-InlineHeader,facebook-unused-include-check)
17
17
  #include <faiss/impl/pq_code_distance/pq_scan_impl.h>
18
- // NOLINTNEXTLINE(facebook-hte-InlineHeader)
18
+ // NOLINTNEXTLINE(facebook-hte-InlineHeader,facebook-unused-include-check)
19
19
  #include <faiss/impl/pq_code_distance/PQDistanceComputer_impl.h>
20
- // NOLINTNEXTLINE(facebook-hte-InlineHeader)
20
+ // NOLINTNEXTLINE(facebook-hte-InlineHeader,facebook-unused-include-check)
21
21
  #include <faiss/impl/pq_code_distance/IVFPQScanner_impl.h>
22
22
 
23
23
  #endif // COMPILE_SIMD_AVX2
@@ -7,7 +7,7 @@
7
7
 
8
8
  // This TU provides non-templated PQ code distance dispatch wrappers
9
9
  // (pq_code_distance_8bit_single, pq_code_distance_8bit_four) declared
10
- // in pq_code_distance-inl.h. These use with_simd_level to route to the
10
+ // in pq_code_distance-inl.h. These use with_simd_level_with_sve to route to the
11
11
  // best available SIMD implementation via pq_code_distance_8bit_*_impl
12
12
  // function template specializations.
13
13
  //
@@ -34,7 +34,7 @@ void pq_scan_8bit(
34
34
  float* heap_dis,
35
35
  int64_t* heap_ids,
36
36
  bool max_heap) {
37
- with_simd_level([&]<SIMDLevel SL>() {
37
+ with_simd_level_with_sve([&]<SIMDLevel SL>() {
38
38
  pq_scan_8bit_impl<SL>(
39
39
  M, dis_table, codes, ncodes, k, heap_dis, heap_ids, max_heap);
40
40
  });
@@ -44,7 +44,7 @@ float pq_code_distance_8bit_single(
44
44
  size_t M,
45
45
  const float* sim_table,
46
46
  const uint8_t* code) {
47
- return with_simd_level([&]<SIMDLevel SL>() {
47
+ return with_simd_level_with_sve([&]<SIMDLevel SL>() {
48
48
  return pq_code_distance_8bit_single_impl<SL>(M, sim_table, code);
49
49
  });
50
50
  }
@@ -60,7 +60,7 @@ void pq_code_distance_8bit_four(
60
60
  float& result1,
61
61
  float& result2,
62
62
  float& result3) {
63
- with_simd_level([&]<SIMDLevel SL>() {
63
+ with_simd_level_with_sve([&]<SIMDLevel SL>() {
64
64
  pq_code_distance_8bit_four_impl<SL>(
65
65
  M,
66
66
  sim_table,
@@ -0,0 +1,195 @@
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
+ // Generic (NONE) implementations of Top1 and Reservoir add_results, plus the
9
+ // runtime-dispatch method bodies. SIMD specialisations live in
10
+ // result_handler_avx2.cpp and result_handler_avx512.cpp.
11
+
12
+ #include <faiss/impl/ResultHandler.h>
13
+ #include <faiss/impl/simd_dispatch.h>
14
+
15
+ namespace faiss {
16
+
17
+ // ----------------------------------------------------------------
18
+ // SIMD-level masks
19
+ // ----------------------------------------------------------------
20
+
21
+ // Top-1: scalar fallback + AVX2 (8-wide) + AVX512 (16-wide).
22
+ constexpr int TOP1_SIMD_LEVELS = (1 << int(SIMDLevel::NONE)) |
23
+ (1 << int(SIMDLevel::AVX2)) | (1 << int(SIMDLevel::AVX512));
24
+
25
+ // Reservoir: scalar fallback + AVX512 compress path.
26
+ // VPCOMPRESSPS/VPCOMPRESSD require AVX512F so there is no AVX2 path.
27
+ // On non-AVX512 hosts the dispatch falls back to NONE automatically.
28
+ constexpr int RESERVOIR_SIMD_LEVELS =
29
+ (1 << int(SIMDLevel::NONE)) | (1 << int(SIMDLevel::AVX512));
30
+
31
+ // ----------------------------------------------------------------
32
+ // Scalar (NONE) helper implementations
33
+ // ----------------------------------------------------------------
34
+
35
+ namespace {
36
+
37
+ template <class C, bool use_sel>
38
+ void top1_add_results_none(
39
+ Top1BlockResultHandler<C, use_sel>* self,
40
+ size_t j0,
41
+ size_t j1,
42
+ const float* dis_tab_in) {
43
+ using T = typename C::T;
44
+ using TI = typename C::TI;
45
+
46
+ for (size_t qi = self->i0; qi < self->i1; qi++) {
47
+ const T* dis_tab_i = dis_tab_in + (j1 - j0) * (qi - self->i0) - j0;
48
+
49
+ // Hoist best_dis / best_idx into locals so the compiler keeps them in
50
+ // registers across the inner loop (no aliasing with dis_tab reads).
51
+ T best_dis = self->dis_tab[qi];
52
+ TI best_idx = self->ids_tab[qi];
53
+
54
+ for (size_t j = j0; j < j1; j++) {
55
+ if (C::cmp(best_dis, dis_tab_i[j])) {
56
+ best_dis = dis_tab_i[j];
57
+ best_idx = (TI)j;
58
+ }
59
+ }
60
+
61
+ self->dis_tab[qi] = best_dis;
62
+ self->ids_tab[qi] = best_idx;
63
+ }
64
+ }
65
+
66
+ template <class C, bool use_sel>
67
+ void reservoir_add_results_none(
68
+ ReservoirBlockResultHandler<C, use_sel>* self,
69
+ size_t j0,
70
+ size_t j1,
71
+ const float* dis_in) {
72
+ using T = typename C::T;
73
+ using TI = typename C::TI;
74
+
75
+ #pragma omp parallel for
76
+ for (int64_t qi = (int64_t)self->i0; qi < (int64_t)self->i1; qi++) {
77
+ ReservoirTopN<C>& res = self->reservoirs[qi - (int64_t)self->i0];
78
+ const T* dis_tab_i = dis_in + (j1 - j0) * (qi - (int64_t)self->i0) - j0;
79
+
80
+ // Hoist res.i and res.threshold into locals so the compiler keeps
81
+ // them in registers.
82
+ size_t ri = res.i;
83
+ T thresh = res.threshold;
84
+
85
+ for (size_t j = j0; j < j1; j++) {
86
+ T dis = dis_tab_i[j];
87
+ if (C::cmp(thresh, dis)) {
88
+ res.vals[ri] = dis;
89
+ res.ids[ri] = (TI)j;
90
+ ri++;
91
+ if (ri >= res.capacity) {
92
+ res.i = ri;
93
+ res.shrink_fuzzy();
94
+ ri = res.i;
95
+ thresh = res.threshold;
96
+ }
97
+ }
98
+ }
99
+ res.i = ri;
100
+ }
101
+ }
102
+
103
+ } // namespace
104
+
105
+ // ----------------------------------------------------------------
106
+ // SIMDLevel::NONE explicit specialisations
107
+ // ----------------------------------------------------------------
108
+
109
+ // Instantiate top1_add_results_tpl<C, use_sel, SIMDLevel::NONE> and
110
+ // reservoir_add_results_tpl<C, use_sel, SIMDLevel::NONE> for all
111
+ // (C, use_sel) combinations that the rest of FAISS uses.
112
+ #define INSTANTIATE_NONE(C, use_sel) \
113
+ template <> \
114
+ void top1_add_results_tpl<C, use_sel, SIMDLevel::NONE>( \
115
+ Top1BlockResultHandler<C, use_sel> * self, \
116
+ size_t j0, \
117
+ size_t j1, \
118
+ const float* dis_tab) { \
119
+ top1_add_results_none<C, use_sel>(self, j0, j1, dis_tab); \
120
+ } \
121
+ template <> \
122
+ void reservoir_add_results_tpl<C, use_sel, SIMDLevel::NONE>( \
123
+ ReservoirBlockResultHandler<C, use_sel> * self, \
124
+ size_t j0, \
125
+ size_t j1, \
126
+ const float* dis_in) { \
127
+ reservoir_add_results_none<C, use_sel>(self, j0, j1, dis_in); \
128
+ }
129
+
130
+ // Type aliases so the comma in CMax<float, int64_t> doesn't split macro args.
131
+ using CMaxFI = CMax<float, int64_t>;
132
+ using CMinFI = CMin<float, int64_t>;
133
+
134
+ INSTANTIATE_NONE(CMaxFI, false)
135
+ INSTANTIATE_NONE(CMaxFI, true)
136
+ INSTANTIATE_NONE(CMinFI, false)
137
+ INSTANTIATE_NONE(CMinFI, true)
138
+
139
+ #undef INSTANTIATE_NONE
140
+
141
+ // ----------------------------------------------------------------
142
+ // add_results method definitions — dispatch to the right SL kernel
143
+ // ----------------------------------------------------------------
144
+
145
+ template <class C, bool use_sel>
146
+ void Top1BlockResultHandler<C, use_sel>::add_results(
147
+ size_t j0,
148
+ size_t j1,
149
+ const T* dis_tab_2) {
150
+ with_selected_simd_levels<TOP1_SIMD_LEVELS>([&]<SIMDLevel SL>() {
151
+ top1_add_results_tpl<C, use_sel, SL>(this, j0, j1, dis_tab_2);
152
+ });
153
+ }
154
+
155
+ template <class C, bool use_sel>
156
+ void ReservoirBlockResultHandler<C, use_sel>::add_results(
157
+ size_t j0,
158
+ size_t j1,
159
+ const T* dis_in) {
160
+ with_selected_simd_levels<RESERVOIR_SIMD_LEVELS>([&]<SIMDLevel SL>() {
161
+ reservoir_add_results_tpl<C, use_sel, SL>(this, j0, j1, dis_in);
162
+ });
163
+ }
164
+
165
+ // ----------------------------------------------------------------
166
+ // Explicit class-template instantiations (force linkage)
167
+ // ----------------------------------------------------------------
168
+
169
+ template void Top1BlockResultHandler<CMax<float, int64_t>, false>::add_results(
170
+ size_t,
171
+ size_t,
172
+ const float*);
173
+ template void Top1BlockResultHandler<CMax<float, int64_t>, true>::add_results(
174
+ size_t,
175
+ size_t,
176
+ const float*);
177
+ template void Top1BlockResultHandler<CMin<float, int64_t>, false>::add_results(
178
+ size_t,
179
+ size_t,
180
+ const float*);
181
+ template void Top1BlockResultHandler<CMin<float, int64_t>, true>::add_results(
182
+ size_t,
183
+ size_t,
184
+ const float*);
185
+
186
+ template void ReservoirBlockResultHandler<CMax<float, int64_t>, false>::
187
+ add_results(size_t, size_t, const float*);
188
+ template void ReservoirBlockResultHandler<CMax<float, int64_t>, true>::
189
+ add_results(size_t, size_t, const float*);
190
+ template void ReservoirBlockResultHandler<CMin<float, int64_t>, false>::
191
+ add_results(size_t, size_t, const float*);
192
+ template void ReservoirBlockResultHandler<CMin<float, int64_t>, true>::
193
+ add_results(size_t, size_t, const float*);
194
+
195
+ } // namespace faiss
@@ -0,0 +1,133 @@
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
+ // AVX2 specialisation of Top1 add_results (8-wide branchless argmin/argmax).
9
+ // Reservoir stays on the NONE path — VPCOMPRESSPS requires AVX512F.
10
+
11
+ #ifdef COMPILE_SIMD_AVX2
12
+
13
+ #include <faiss/impl/ResultHandler.h>
14
+
15
+ #include <immintrin.h>
16
+ #include <type_traits>
17
+
18
+ namespace faiss {
19
+
20
+ namespace {
21
+
22
+ /// Templated AVX2 implementation of Top1 add_results for both CMax (keeps the
23
+ /// smallest distance) and CMin (keeps the largest similarity).
24
+ template <class C, bool use_sel>
25
+ void top1_add_results_avx2(
26
+ Top1BlockResultHandler<C, use_sel>* self,
27
+ size_t j0,
28
+ size_t j1,
29
+ const float* dis_tab_in) {
30
+ static_assert(
31
+ std::is_same<typename C::T, float>::value,
32
+ "This code expects float distances");
33
+ using TI = typename C::TI;
34
+ const __m256i vstep = _mm256_setr_epi32(0, 1, 2, 3, 4, 5, 6, 7);
35
+
36
+ for (size_t qi = self->i0; qi < self->i1; qi++) {
37
+ const float* dis_tab_i = dis_tab_in + (j1 - j0) * (qi - self->i0) - j0;
38
+
39
+ // Hoist best_dis / best_idx into locals so the compiler keeps them in
40
+ // registers across the inner loop (no aliasing with dis_tab reads).
41
+ float best_dis = self->dis_tab[qi];
42
+ TI best_idx = self->ids_tab[qi];
43
+ size_t j = j0;
44
+
45
+ __m256 vbest = _mm256_set1_ps(best_dis);
46
+ __m256i vbest_idx = _mm256_set1_epi32((int32_t)best_idx);
47
+
48
+ for (; j + 8 <= j1; j += 8) {
49
+ __m256 vdis = _mm256_loadu_ps(dis_tab_i + j);
50
+ __m256i vidx =
51
+ _mm256_add_epi32(_mm256_set1_epi32((int32_t)j), vstep);
52
+
53
+ // CMax (L2 nearest neighbour): keep lane if dis < best.
54
+ // CMin (inner product): keep lane if dis > best.
55
+ __m256 mask;
56
+ if constexpr (C::is_max) {
57
+ mask = _mm256_cmp_ps(vdis, vbest, _CMP_LT_OS);
58
+ } else {
59
+ mask = _mm256_cmp_ps(vdis, vbest, _CMP_GT_OS);
60
+ }
61
+ vbest = _mm256_blendv_ps(vbest, vdis, mask);
62
+ vbest_idx = _mm256_blendv_epi8(
63
+ vbest_idx, vidx, _mm256_castps_si256(mask));
64
+ }
65
+
66
+ // Horizontal reduction across 8 lanes.
67
+ alignas(32) float best_arr[8];
68
+ alignas(32) int32_t idx_arr[8];
69
+ _mm256_store_ps(best_arr, vbest);
70
+ _mm256_store_si256((__m256i*)idx_arr, vbest_idx);
71
+ for (int k = 0; k < 8; k++) {
72
+ if (C::cmp(best_dis, best_arr[k])) {
73
+ best_dis = best_arr[k];
74
+ best_idx = (TI)idx_arr[k];
75
+ }
76
+ }
77
+
78
+ // Scalar tail.
79
+ for (; j < j1; j++) {
80
+ if (C::cmp(best_dis, dis_tab_i[j])) {
81
+ best_dis = dis_tab_i[j];
82
+ best_idx = (TI)j;
83
+ }
84
+ }
85
+
86
+ self->dis_tab[qi] = best_dis;
87
+ self->ids_tab[qi] = best_idx;
88
+ }
89
+ }
90
+
91
+ } // namespace
92
+
93
+ // Explicit specialisations for AVX2
94
+
95
+ template <>
96
+ void top1_add_results_tpl<CMax<float, int64_t>, false, SIMDLevel::AVX2>(
97
+ Top1BlockResultHandler<CMax<float, int64_t>, false>* self,
98
+ size_t j0,
99
+ size_t j1,
100
+ const float* dis_tab) {
101
+ top1_add_results_avx2<CMax<float, int64_t>, false>(self, j0, j1, dis_tab);
102
+ }
103
+
104
+ template <>
105
+ void top1_add_results_tpl<CMax<float, int64_t>, true, SIMDLevel::AVX2>(
106
+ Top1BlockResultHandler<CMax<float, int64_t>, true>* self,
107
+ size_t j0,
108
+ size_t j1,
109
+ const float* dis_tab) {
110
+ top1_add_results_avx2<CMax<float, int64_t>, true>(self, j0, j1, dis_tab);
111
+ }
112
+
113
+ template <>
114
+ void top1_add_results_tpl<CMin<float, int64_t>, false, SIMDLevel::AVX2>(
115
+ Top1BlockResultHandler<CMin<float, int64_t>, false>* self,
116
+ size_t j0,
117
+ size_t j1,
118
+ const float* dis_tab) {
119
+ top1_add_results_avx2<CMin<float, int64_t>, false>(self, j0, j1, dis_tab);
120
+ }
121
+
122
+ template <>
123
+ void top1_add_results_tpl<CMin<float, int64_t>, true, SIMDLevel::AVX2>(
124
+ Top1BlockResultHandler<CMin<float, int64_t>, true>* self,
125
+ size_t j0,
126
+ size_t j1,
127
+ const float* dis_tab) {
128
+ top1_add_results_avx2<CMin<float, int64_t>, true>(self, j0, j1, dis_tab);
129
+ }
130
+
131
+ } // namespace faiss
132
+
133
+ #endif // COMPILE_SIMD_AVX2