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
@@ -385,7 +385,7 @@ IndexIVF* parse_IndexIVF(
385
385
  }
386
386
  if (match("FlatPanorama([0-9]+)?(_([0-9]+))?")) {
387
387
  int nlevels = mres_to_int(sm[1], 8); // default to 8 levels
388
- int bs = mres_to_int(sm[3], 128);
388
+ int bs = mres_to_int(sm[3], Panorama::kDefaultBatchSize);
389
389
  return new IndexIVFFlatPanorama(
390
390
  get_q(), d, nlist, nlevels, mt, own_il, bs);
391
391
  }
@@ -575,6 +575,13 @@ IndexHNSW* parse_IndexHNSW(
575
575
  if (match(sq_pattern)) {
576
576
  return new IndexHNSWSQ(d, sq_types[sm[1].str()], hnsw_M, mt);
577
577
  }
578
+ // Keep the bare RaBitQ token consistent with Flat and IVF: it means 1 bit.
579
+ // Use an explicit width such as RaBitQ4 to enable staged refinement.
580
+ if (match("RaBitQ([1-9])?")) {
581
+ // the capture is the bare digit, so no substr offset here
582
+ int nb_bits = mres_to_int(sm[1], 1);
583
+ return new IndexHNSWRaBitQ(d, hnsw_M, nb_bits, mt);
584
+ }
578
585
  if (match("([0-9]+)\\+PQ([0-9]+)?")) {
579
586
  int ncent = mres_to_int(sm[1]);
580
587
  int pq_m = mres_to_int(sm[2]);
@@ -440,8 +440,11 @@ void ArrayInvertedListsPanorama::resize(size_t list_no, size_t new_size) {
440
440
  const uint8_t* ArrayInvertedListsPanorama::get_single_code(
441
441
  size_t list_no,
442
442
  size_t offset) const {
443
- assert(list_no < nlist);
444
- assert(offset < ids[list_no].size());
443
+ // Throw rather than assert: Panorama::reconstruct takes an unsized
444
+ // pointer, so these are the only bounds available and they must hold
445
+ // in opt builds too.
446
+ FAISS_THROW_IF_NOT(list_no < nlist);
447
+ FAISS_THROW_IF_NOT(offset < ids[list_no].size());
445
448
 
446
449
  uint8_t* recons_buffer = new uint8_t[code_size];
447
450
 
@@ -669,10 +672,10 @@ namespace {
669
672
  int translate_list_no(const VStackInvertedLists* vil, idx_t list_no) {
670
673
  FAISS_THROW_IF_NOT(
671
674
  list_no >= 0 && static_cast<size_t>(list_no) < vil->nlist);
672
- int i0 = 0, i1 = vil->ils.size();
675
+ size_t i0 = 0, i1 = vil->ils.size();
673
676
  const idx_t* cumsz = vil->cumsz.data();
674
677
  while (i0 + 1 < i1) {
675
- int imed = (i0 + i1) / 2;
678
+ size_t imed = i0 + (i1 - i0) / 2;
676
679
  if (list_no >= cumsz[imed]) {
677
680
  i0 = imed;
678
681
  } else {
@@ -11,6 +11,7 @@
11
11
 
12
12
  #include <pthread.h>
13
13
 
14
+ #include <memory>
14
15
  #include <unordered_set>
15
16
 
16
17
  #include <sys/mman.h>
@@ -714,12 +715,17 @@ void OnDiskInvertedListsIOHook::write(const InvertedLists* ils, IOWriter* f)
714
715
 
715
716
  InvertedLists* OnDiskInvertedListsIOHook::read(IOReader* f, int io_flags)
716
717
  const {
717
- OnDiskInvertedLists* od = new OnDiskInvertedLists();
718
+ auto od = std::make_unique<OnDiskInvertedLists>();
718
719
  od->read_only = io_flags & IO_FLAG_READ_ONLY;
719
720
  READ1(od->nlist);
720
721
  READ1(od->code_size);
721
722
  // this is a POD object
722
723
  READVECTOR(od->lists);
724
+ FAISS_THROW_IF_NOT_FMT(
725
+ od->lists.size() == od->nlist,
726
+ "OnDisk inverted lists: read %zu lists for nlist %zu",
727
+ od->lists.size(),
728
+ od->nlist);
723
729
  {
724
730
  std::vector<OnDiskInvertedLists::Slot> v;
725
731
  READVECTOR(v);
@@ -759,7 +765,7 @@ InvertedLists* OnDiskInvertedListsIOHook::read(IOReader* f, int io_flags)
759
765
  if (!(io_flags & IO_FLAG_SKIP_IVF_DATA)) {
760
766
  od->do_mmap();
761
767
  }
762
- return od;
768
+ return od.release();
763
769
  }
764
770
 
765
771
  /** read from a ArrayInvertedLists into this invertedlist type */
@@ -769,7 +775,7 @@ InvertedLists* OnDiskInvertedListsIOHook::read_ArrayInvertedLists(
769
775
  size_t nlist,
770
776
  size_t code_size,
771
777
  const std::vector<size_t>& sizes) const {
772
- auto ails = new OnDiskInvertedLists();
778
+ auto ails = std::make_unique<OnDiskInvertedLists>();
773
779
  ails->nlist = nlist;
774
780
  ails->code_size = code_size;
775
781
  ails->read_only = true;
@@ -819,7 +825,7 @@ InvertedLists* OnDiskInvertedListsIOHook::read_ArrayInvertedLists(
819
825
  // resume normal reading of file
820
826
  fseek(fdesc, o, SEEK_SET);
821
827
 
822
- return ails;
828
+ return ails.release();
823
829
  }
824
830
 
825
831
  } // namespace faiss
@@ -67,11 +67,13 @@ IndexSVSVamana::IndexSVSVamana(
67
67
  size_t degree,
68
68
  MetricType metric,
69
69
  SVSStorageKind storage,
70
- bool is_static)
70
+ bool is_static,
71
+ bool store_vectors)
71
72
  : Index(d, metric),
72
73
  graph_max_degree{degree},
73
74
  is_static{is_static},
74
- storage_kind{storage} {
75
+ storage_kind{storage},
76
+ store_vectors{store_vectors} {
75
77
  prune_to = graph_max_degree < 4 ? graph_max_degree : graph_max_degree - 4;
76
78
  alpha = metric == METRIC_L2 ? 1.2f : 0.95f;
77
79
 
@@ -124,6 +126,14 @@ IndexSVSVamana::~IndexSVSVamana() {
124
126
  }
125
127
 
126
128
  void IndexSVSVamana::add(idx_t n, const float* x) {
129
+ // Opting out after data has been added would leave stored_vectors
130
+ // misaligned with the ids in the index, so release it instead of growing.
131
+ if (!store_vectors && stored_vectors_valid) {
132
+ stored_vectors.clear();
133
+ stored_vectors.shrink_to_fit();
134
+ stored_vectors_valid = false;
135
+ }
136
+
127
137
  if (is_static) {
128
138
  FAISS_THROW_IF_MSG(
129
139
  impl,
@@ -167,7 +177,8 @@ void IndexSVSVamana::reconstruct(idx_t key, float* recons) const {
167
177
  FAISS_THROW_IF_NOT_MSG(
168
178
  stored_vectors_valid && !stored_vectors.empty(),
169
179
  "IndexSVSVamana::reconstruct: stored_vectors unavailable "
170
- "(invalidated by remove_ids or not restored after deserialization)");
180
+ "(store_vectors disabled, invalidated by remove_ids, or not "
181
+ "restored after deserialization)");
171
182
  std::memcpy(recons, stored_vectors.data() + key * d, sizeof(float) * d);
172
183
  }
173
184
 
@@ -187,7 +198,8 @@ void IndexSVSVamana::reset() {
187
198
  }
188
199
  }
189
200
  stored_vectors.clear();
190
- stored_vectors_valid = true;
201
+ stored_vectors.shrink_to_fit();
202
+ stored_vectors_valid = store_vectors;
191
203
  mmap_owner.reset(); // Release the memory mapping
192
204
  is_trained = false;
193
205
  ntotal = 0;
@@ -111,7 +111,8 @@ struct IndexSVSVamana : Index {
111
111
  size_t degree,
112
112
  MetricType metric = METRIC_L2,
113
113
  SVSStorageKind storage = SVSStorageKind::SVS_FP32,
114
- bool is_static = false);
114
+ bool is_static = false,
115
+ bool store_vectors = true);
115
116
 
116
117
  ~IndexSVSVamana() override;
117
118
 
@@ -164,6 +165,12 @@ struct IndexSVSVamana : Index {
164
165
  std::vector<float> stored_vectors;
165
166
  bool stored_vectors_valid{true};
166
167
 
168
+ // Set to false before the first add() to skip the stored_vectors copy,
169
+ // saving ntotal * d * 4 bytes at the cost of reconstruct() support and
170
+ // hence of use as an IVF coarse quantizer. Clearing it after vectors have
171
+ // been added drops the copy, which can no longer be aligned with the ids.
172
+ bool store_vectors{true};
173
+
167
174
  protected:
168
175
  /* Initializes the implementation. For static indexes the data is consumed
169
176
  at build time; for dynamic indexes n/x are ignored and add() populates
@@ -34,7 +34,9 @@ IndexSVSVamanaLVQ::IndexSVSVamanaLVQ(
34
34
  size_t degree,
35
35
  MetricType metric,
36
36
  SVSStorageKind storage,
37
- bool is_static)
38
- : IndexSVSVamana(d, degree, metric, storage, is_static) {}
37
+ bool is_static,
38
+ bool store_vectors)
39
+ : IndexSVSVamana(d, degree, metric, storage, is_static, store_vectors) {
40
+ }
39
41
 
40
42
  } // namespace faiss
@@ -35,7 +35,8 @@ struct IndexSVSVamanaLVQ : IndexSVSVamana {
35
35
  size_t degree,
36
36
  MetricType metric = METRIC_L2,
37
37
  SVSStorageKind storage = SVSStorageKind::SVS_LVQ4x0,
38
- bool is_static = false);
38
+ bool is_static = false,
39
+ bool store_vectors = true);
39
40
 
40
41
  ~IndexSVSVamanaLVQ() override = default;
41
42
  };
@@ -44,8 +44,15 @@ IndexSVSVamanaLeanVec::IndexSVSVamanaLeanVec(
44
44
  MetricType metric,
45
45
  size_t leanvec_dims,
46
46
  SVSStorageKind storage_kind,
47
- bool is_static)
48
- : IndexSVSVamana(d, degree, metric, storage_kind, is_static) {
47
+ bool is_static,
48
+ bool store_vectors)
49
+ : IndexSVSVamana(
50
+ d,
51
+ degree,
52
+ metric,
53
+ storage_kind,
54
+ is_static,
55
+ store_vectors) {
49
56
  is_trained = false;
50
57
  leanvec_d = leanvec_dims == 0 ? d / 2 : leanvec_dims;
51
58
  }
@@ -36,7 +36,8 @@ struct IndexSVSVamanaLeanVec : IndexSVSVamana {
36
36
  MetricType metric = METRIC_L2,
37
37
  size_t leanvec_dims = 0,
38
38
  SVSStorageKind storage = SVSStorageKind::SVS_LeanVec4x4,
39
- bool is_static = false);
39
+ bool is_static = false,
40
+ bool store_vectors = true);
40
41
 
41
42
  ~IndexSVSVamanaLeanVec() override;
42
43
 
@@ -541,17 +541,19 @@ void exhaustive_L2sqr_blas<Top1BlockResultHandler<CMax<float, int64_t>>>(
541
541
  return;
542
542
  }
543
543
 
544
- with_selected_simd_levels<AVAILABLE_SIMD_LEVELS_A1>([&]<SIMDLevel SL>() {
545
- if constexpr (
546
- SL == SIMDLevel::AVX2 || SL == SIMDLevel::AVX512 ||
547
- SL == SIMDLevel::ARM_SVE) {
548
- exhaustive_L2sqr_blas_cmax<SL>(x, y, d, nx, ny, res, y_norms);
549
- } else {
550
- exhaustive_L2sqr_blas_default_impl<
551
- Top1BlockResultHandler<CMax<float, int64_t>>>(
552
- x, y, d, nx, ny, res, y_norms);
553
- }
554
- });
544
+ with_selected_simd_levels<AVAILABLE_SIMD_LEVELS_BASE_WITH_SVE>(
545
+ [&]<SIMDLevel SL>() {
546
+ if constexpr (
547
+ SL == SIMDLevel::AVX2 || SL == SIMDLevel::AVX512 ||
548
+ SL == SIMDLevel::ARM_SVE) {
549
+ exhaustive_L2sqr_blas_cmax<SL>(
550
+ x, y, d, nx, ny, res, y_norms);
551
+ } else {
552
+ exhaustive_L2sqr_blas_default_impl<
553
+ Top1BlockResultHandler<CMax<float, int64_t>>>(
554
+ x, y, d, nx, ny, res, y_norms);
555
+ }
556
+ });
555
557
  }
556
558
 
557
559
  struct Run_search_inner_product {
@@ -563,7 +565,11 @@ struct Run_search_inner_product {
563
565
  size_t d,
564
566
  size_t nx,
565
567
  size_t ny) {
566
- if (res.sel ||
568
+ // ny == 0 goes to the sequential path: it guards only on nx, so its
569
+ // per-query begin()/end() still runs and each handler writes its own
570
+ // neutral distance and -1 label. The BLAS path instead returns early
571
+ // on ny == 0, before the handler is initialized.
572
+ if (res.sel || ny == 0 ||
567
573
  nx * d < static_cast<size_t>(distance_compute_blas_threshold)) {
568
574
  exhaustive_inner_product_seq(x, y, d, nx, ny, res);
569
575
  } else {
@@ -582,7 +588,8 @@ struct Run_search_L2sqr {
582
588
  size_t nx,
583
589
  size_t ny,
584
590
  const float* y_norm2) {
585
- if (res.sel ||
591
+ // See the note on ny == 0 in Run_search_inner_product.
592
+ if (res.sel || ny == 0 ||
586
593
  nx * d < static_cast<size_t>(distance_compute_blas_threshold)) {
587
594
  exhaustive_L2sqr_seq(x, y, d, nx, ny, res);
588
595
  } else {
@@ -29,22 +29,22 @@
29
29
  namespace faiss {
30
30
 
31
31
  inline float fvec_L1_dispatch(const float* x, const float* y, size_t d) {
32
- return with_selected_simd_levels<AVAILABLE_SIMD_LEVELS_A1>(
32
+ return with_selected_simd_levels<AVAILABLE_SIMD_LEVELS_BASE_WITH_SVE>(
33
33
  [&]<SIMDLevel SL>() { return fvec_L1<SL>(x, y, d); });
34
34
  }
35
35
 
36
36
  inline float fvec_Linf_dispatch(const float* x, const float* y, size_t d) {
37
- return with_selected_simd_levels<AVAILABLE_SIMD_LEVELS_A1>(
37
+ return with_selected_simd_levels<AVAILABLE_SIMD_LEVELS_BASE_WITH_SVE>(
38
38
  [&]<SIMDLevel SL>() { return fvec_Linf<SL>(x, y, d); });
39
39
  }
40
40
 
41
41
  inline float fvec_norm_L2sqr_dispatch(const float* x, size_t d) {
42
- return with_selected_simd_levels<AVAILABLE_SIMD_LEVELS_A1>(
42
+ return with_selected_simd_levels<AVAILABLE_SIMD_LEVELS_BASE_WITH_SVE>(
43
43
  [&]<SIMDLevel SL>() { return fvec_norm_L2sqr<SL>(x, d); });
44
44
  }
45
45
 
46
46
  inline float fvec_L2sqr_dispatch(const float* x, const float* y, size_t d) {
47
- return with_selected_simd_levels<AVAILABLE_SIMD_LEVELS_A1>(
47
+ return with_selected_simd_levels<AVAILABLE_SIMD_LEVELS_BASE_WITH_SVE>(
48
48
  [&]<SIMDLevel SL>() { return fvec_L2sqr<SL>(x, y, d); });
49
49
  }
50
50
 
@@ -52,7 +52,7 @@ inline float fvec_inner_product_dispatch(
52
52
  const float* x,
53
53
  const float* y,
54
54
  size_t d) {
55
- return with_selected_simd_levels<AVAILABLE_SIMD_LEVELS_A1>(
55
+ return with_selected_simd_levels<AVAILABLE_SIMD_LEVELS_BASE_WITH_SVE>(
56
56
  [&]<SIMDLevel SL>() { return fvec_inner_product<SL>(x, y, d); });
57
57
  }
58
58
 
@@ -67,10 +67,11 @@ inline void fvec_inner_product_batch_4_dispatch(
67
67
  float& dis1,
68
68
  float& dis2,
69
69
  float& dis3) {
70
- with_selected_simd_levels<AVAILABLE_SIMD_LEVELS_A1>([&]<SIMDLevel SL>() {
71
- fvec_inner_product_batch_4<SL>(
72
- x, y0, y1, y2, y3, d, dis0, dis1, dis2, dis3);
73
- });
70
+ with_selected_simd_levels<AVAILABLE_SIMD_LEVELS_BASE_WITH_SVE>(
71
+ [&]<SIMDLevel SL>() {
72
+ fvec_inner_product_batch_4<SL>(
73
+ x, y0, y1, y2, y3, d, dis0, dis1, dis2, dis3);
74
+ });
74
75
  }
75
76
 
76
77
  inline void fvec_L2sqr_batch_4_dispatch(
@@ -84,9 +85,11 @@ inline void fvec_L2sqr_batch_4_dispatch(
84
85
  float& dis1,
85
86
  float& dis2,
86
87
  float& dis3) {
87
- with_selected_simd_levels<AVAILABLE_SIMD_LEVELS_A1>([&]<SIMDLevel SL>() {
88
- fvec_L2sqr_batch_4<SL>(x, y0, y1, y2, y3, d, dis0, dis1, dis2, dis3);
89
- });
88
+ with_selected_simd_levels<AVAILABLE_SIMD_LEVELS_BASE_WITH_SVE>(
89
+ [&]<SIMDLevel SL>() {
90
+ fvec_L2sqr_batch_4<SL>(
91
+ x, y0, y1, y2, y3, d, dis0, dis1, dis2, dis3);
92
+ });
90
93
  }
91
94
 
92
95
  inline void fvec_L2sqr_ny_transposed_dispatch(
@@ -97,9 +100,11 @@ inline void fvec_L2sqr_ny_transposed_dispatch(
97
100
  size_t d,
98
101
  size_t d_offset,
99
102
  size_t ny) {
100
- with_selected_simd_levels<AVAILABLE_SIMD_LEVELS_A1>([&]<SIMDLevel SL>() {
101
- fvec_L2sqr_ny_transposed<SL>(dis, x, y, y_sqlen, d, d_offset, ny);
102
- });
103
+ with_selected_simd_levels<AVAILABLE_SIMD_LEVELS_BASE_WITH_SVE>(
104
+ [&]<SIMDLevel SL>() {
105
+ fvec_L2sqr_ny_transposed<SL>(
106
+ dis, x, y, y_sqlen, d, d_offset, ny);
107
+ });
103
108
  }
104
109
 
105
110
  inline void fvec_inner_products_ny_dispatch(
@@ -108,9 +113,10 @@ inline void fvec_inner_products_ny_dispatch(
108
113
  const float* y,
109
114
  size_t d,
110
115
  size_t ny) {
111
- with_selected_simd_levels<AVAILABLE_SIMD_LEVELS_A1>([&]<SIMDLevel SL>() {
112
- fvec_inner_products_ny<SL>(ip, x, y, d, ny);
113
- });
116
+ with_selected_simd_levels<AVAILABLE_SIMD_LEVELS_BASE_WITH_SVE>(
117
+ [&]<SIMDLevel SL>() {
118
+ fvec_inner_products_ny<SL>(ip, x, y, d, ny);
119
+ });
114
120
  }
115
121
 
116
122
  inline void fvec_L2sqr_ny_dispatch(
@@ -119,7 +125,7 @@ inline void fvec_L2sqr_ny_dispatch(
119
125
  const float* y,
120
126
  size_t d,
121
127
  size_t ny) {
122
- with_selected_simd_levels<AVAILABLE_SIMD_LEVELS_A1>(
128
+ with_selected_simd_levels<AVAILABLE_SIMD_LEVELS_BASE_WITH_SVE>(
123
129
  [&]<SIMDLevel SL>() { fvec_L2sqr_ny<SL>(dis, x, y, d, ny); });
124
130
  }
125
131
 
@@ -129,7 +135,7 @@ inline size_t fvec_L2sqr_ny_nearest_dispatch(
129
135
  const float* y,
130
136
  size_t d,
131
137
  size_t ny) {
132
- return with_selected_simd_levels<AVAILABLE_SIMD_LEVELS_A1>(
138
+ return with_selected_simd_levels<AVAILABLE_SIMD_LEVELS_BASE_WITH_SVE>(
133
139
  [&]<SIMDLevel SL>() {
134
140
  return fvec_L2sqr_ny_nearest<SL>(
135
141
  distances_tmp_buffer, x, y, d, ny);
@@ -144,7 +150,7 @@ inline size_t fvec_L2sqr_ny_nearest_y_transposed_dispatch(
144
150
  size_t d,
145
151
  size_t d_offset,
146
152
  size_t ny) {
147
- return with_selected_simd_levels<AVAILABLE_SIMD_LEVELS_A1>(
153
+ return with_selected_simd_levels<AVAILABLE_SIMD_LEVELS_BASE_WITH_SVE>(
148
154
  [&]<SIMDLevel SL>() {
149
155
  return fvec_L2sqr_ny_nearest_y_transposed<SL>(
150
156
  distances_tmp_buffer, x, y, y_sqlen, d, d_offset, ny);
@@ -157,7 +163,7 @@ inline void fvec_madd_dispatch(
157
163
  float bf,
158
164
  const float* b,
159
165
  float* c) {
160
- with_selected_simd_levels<AVAILABLE_SIMD_LEVELS_A1>(
166
+ with_selected_simd_levels<AVAILABLE_SIMD_LEVELS_BASE_WITH_SVE>(
161
167
  [&]<SIMDLevel SL>() { fvec_madd<SL>(n, a, bf, b, c); });
162
168
  }
163
169
 
@@ -167,7 +173,7 @@ inline int fvec_madd_and_argmin_dispatch(
167
173
  float bf,
168
174
  const float* b,
169
175
  float* c) {
170
- return with_selected_simd_levels<AVAILABLE_SIMD_LEVELS_A1>(
176
+ return with_selected_simd_levels<AVAILABLE_SIMD_LEVELS_BASE_WITH_SVE>(
171
177
  [&]<SIMDLevel SL>() {
172
178
  return fvec_madd_and_argmin<SL>(n, a, bf, b, c);
173
179
  });
@@ -241,7 +247,7 @@ auto with_VectorDistance(
241
247
  if constexpr (!has_simd) {
242
248
  return call.template operator()<SIMDLevel::NONE>();
243
249
  } else {
244
- return with_simd_level(call);
250
+ return with_simd_level_with_sve(call);
245
251
  }
246
252
  };
247
253
  return with_metric_type(metric, dispatch_metric);
@@ -51,7 +51,7 @@ bool exhaustive_L2sqr_fused_cmax(
51
51
  return true;
52
52
  }
53
53
 
54
- return with_selected_simd_levels<AVAILABLE_SIMD_LEVELS_A0>(
54
+ return with_selected_simd_levels<AVAILABLE_SIMD_LEVELS_BASE>(
55
55
  [&]<SIMDLevel SL>() {
56
56
  return exhaustive_L2sqr_fused_cmax<SL>(
57
57
  x, y, d, nx, ny, res, y_norms);
@@ -146,8 +146,14 @@ void hammings(
146
146
  size_t nb,
147
147
  size_t ncodes,
148
148
  hamdis_t* __restrict dis) {
149
- with_simd_level_a0_spr([&]<SIMDLevel SL>() {
150
- hammings_fixSL<SL>(a, b, na, nb, ncodes, dis);
149
+ with_simd_level_with_vpopcnt([&]<SIMDLevel SL>() {
150
+ // Ragged sizes have their own kernel; keeping it out of
151
+ // hammings_fixSL() leaves the word-level paths untouched.
152
+ if (ncodes % 8 != 0) {
153
+ hammings_ragged_fixSL<SL>(a, b, na, nb, ncodes, dis);
154
+ } else {
155
+ hammings_fixSL<SL>(a, b, na, nb, ncodes, dis);
156
+ }
151
157
  });
152
158
  }
153
159
 
@@ -170,7 +176,7 @@ void hammings_knn_hc(
170
176
  int order,
171
177
  ApproxTopK_mode_t approx_topk_mode,
172
178
  const faiss::IDSelector* sel) {
173
- with_simd_level_a0_spr([&]<SIMDLevel SL>() {
179
+ with_simd_level_with_vpopcnt([&]<SIMDLevel SL>() {
174
180
  hammings_knn_hc_fixSL<SL>(
175
181
  ha, a, b, nb, ncodes, order, approx_topk_mode, sel);
176
182
  });
@@ -186,7 +192,7 @@ void hammings_knn_mc(
186
192
  int32_t* __restrict distances,
187
193
  int64_t* __restrict labels,
188
194
  const faiss::IDSelector* sel) {
189
- with_simd_level_a0_spr([&]<SIMDLevel SL>() {
195
+ with_simd_level_with_vpopcnt([&]<SIMDLevel SL>() {
190
196
  hammings_knn_mc_fixSL<SL>(
191
197
  a, b, na, nb, k, ncodes, distances, labels, sel);
192
198
  });
@@ -201,7 +207,7 @@ void hamming_range_search(
201
207
  size_t code_size,
202
208
  RangeSearchResult* result,
203
209
  const faiss::IDSelector* sel) {
204
- with_simd_level_a0_spr([&]<SIMDLevel SL>() {
210
+ with_simd_level_with_vpopcnt([&]<SIMDLevel SL>() {
205
211
  hamming_range_search_fixSL<SL>(
206
212
  a, b, na, nb, radius, code_size, result, sel);
207
213
  });
@@ -215,7 +221,7 @@ void hamming_count_thres(
215
221
  hamdis_t ht,
216
222
  size_t ncodes,
217
223
  size_t* nptr) {
218
- with_simd_level_a0_spr([&]<SIMDLevel SL>() {
224
+ with_simd_level_with_vpopcnt([&]<SIMDLevel SL>() {
219
225
  hamming_count_thres_fixSL<SL>(bs1, bs2, n1, n2, ht, ncodes, nptr);
220
226
  });
221
227
  }
@@ -226,7 +232,7 @@ void crosshamming_count_thres(
226
232
  hamdis_t ht,
227
233
  size_t ncodes,
228
234
  size_t* nptr) {
229
- with_simd_level_a0_spr([&]<SIMDLevel SL>() {
235
+ with_simd_level_with_vpopcnt([&]<SIMDLevel SL>() {
230
236
  crosshamming_count_thres_fixSL<SL>(dbs, n, ht, ncodes, nptr);
231
237
  });
232
238
  }
@@ -240,7 +246,7 @@ size_t match_hamming_thres(
240
246
  size_t ncodes,
241
247
  int64_t* idx,
242
248
  hamdis_t* dis) {
243
- return with_simd_level_a0_spr([&]<SIMDLevel SL>() -> size_t {
249
+ return with_simd_level_with_vpopcnt([&]<SIMDLevel SL>() -> size_t {
244
250
  return match_hamming_thres_fixSL<SL>(
245
251
  bs1, bs2, n1, n2, ht, ncodes, idx, dis);
246
252
  });
@@ -253,7 +259,7 @@ void generalized_hammings_knn_hc(
253
259
  size_t nb,
254
260
  size_t code_size,
255
261
  int ordered) {
256
- with_simd_level_a0_spr([&]<SIMDLevel SL>() {
262
+ with_simd_level_with_vpopcnt([&]<SIMDLevel SL>() {
257
263
  generalized_hammings_knn_hc_fixSL<SL>(ha, a, b, nb, code_size, ordered);
258
264
  });
259
265
  }
@@ -111,7 +111,7 @@ FAISS_API extern size_t hamming_batch_size;
111
111
  *
112
112
  * @param a size na * nbytespercode
113
113
  * @param b size nb * nbytespercode
114
- * @param nbytespercode should be multiple of 8
114
+ * @param nbytespercode any size; multiples of 8 take a faster kernel
115
115
  * @param dis output distances, size na * nb
116
116
  */
117
117
  void hammings(
@@ -327,6 +327,15 @@ void hamming_range_search_fixSL(
327
327
  RangeSearchResult* result,
328
328
  const IDSelector* sel);
329
329
 
330
+ template <SIMDLevel SL>
331
+ void hammings_ragged_fixSL(
332
+ const uint8_t* a,
333
+ const uint8_t* b,
334
+ size_t na,
335
+ size_t nb,
336
+ size_t ncodes,
337
+ hamdis_t* dis);
338
+
330
339
  template <SIMDLevel SL>
331
340
  void hammings_fixSL(
332
341
  const uint8_t* a,
@@ -210,12 +210,23 @@ inline void hammings_impl(
210
210
  size_t n2,
211
211
  hamdis_t* __restrict dis) {
212
212
  size_t i, j;
213
- const size_t nwords = nbits / 64;
213
+ constexpr size_t nwords = nbits / 64;
214
214
  for (i = 0; i < n1; i++) {
215
215
  const uint64_t* __restrict bs1_ = bs1 + i * nwords;
216
216
  hamdis_t* __restrict dis_ = dis + i * n2;
217
- for (j = 0; j < n2; j++) {
218
- dis_[j] = hamming<nbits>(bs1_, bs2 + j * nwords);
217
+ if constexpr (nwords >= 4) {
218
+ // Wide codes are quicker one candidate at a time; otherwise the
219
+ // compiler batches candidates and must gather scattered bytes.
220
+ #if defined(__clang__)
221
+ #pragma clang loop vectorize(disable)
222
+ #endif
223
+ for (j = 0; j < n2; j++) {
224
+ dis_[j] = hamming<nbits>(bs1_, bs2 + j * nwords);
225
+ }
226
+ } else {
227
+ for (j = 0; j < n2; j++) {
228
+ dis_[j] = hamming<nbits>(bs1_, bs2 + j * nwords);
229
+ }
219
230
  }
220
231
  }
221
232
  }
@@ -0,0 +1,24 @@
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_AVX512_VPOPCNT
9
+
10
+ #define THE_SIMD_LEVEL SIMDLevel::AVX512_VPOPCNT
11
+ #include <faiss/utils/hamming_distance/hamming_computer-avx512_vpopcnt.h>
12
+ #include <faiss/utils/hamming_distance/hamming_impl.h>
13
+
14
+ // Must follow the computer specializations above.
15
+ // clang-format off
16
+ // NOLINTNEXTLINE(facebook-hte-InlineHeader)
17
+ #include <faiss/impl/binary_hamming/IndexBinaryIVF_impl.h>
18
+ // clang-format on
19
+
20
+ // Fails the build if the batch path stops being selected for this computer.
21
+ static_assert(faiss::has_hamming_batch<
22
+ faiss::HammingComputer20_tpl<faiss::SIMDLevel::AVX512_VPOPCNT>>);
23
+
24
+ #endif // COMPILE_SIMD_AVX512_VPOPCNT
@@ -12,7 +12,7 @@
12
12
  // Types without custom AVX512 code inherit from the NONE specializations
13
13
  // in hamming_computer-generic.h. HammingComputer64 and
14
14
  // HammingComputerDefault use scalar popcount here; the VPOPCNTDQ fast
15
- // path lives in hamming_computer-avx512_spr.h (AVX512_SPR level).
15
+ // path lives in hamming_computer-avx512_vpopcnt.h (AVX512_VPOPCNT level).
16
16
  // GenHammingComputer classes leverage SSE/AVX2 intrinsics.
17
17
 
18
18
  #include <cassert>