faiss 0.6.2 → 0.6.3

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 (144) 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/AutoTune.cpp +3 -1
  5. data/vendor/faiss/faiss/Clustering.cpp +9 -1
  6. data/vendor/faiss/faiss/IVFlib.cpp +14 -3
  7. data/vendor/faiss/faiss/Index.h +2 -2
  8. data/vendor/faiss/faiss/IndexAdditiveQuantizer.cpp +9 -10
  9. data/vendor/faiss/faiss/IndexAdditiveQuantizerFastScan.cpp +2 -3
  10. data/vendor/faiss/faiss/IndexBinaryFromFloat.cpp +1 -2
  11. data/vendor/faiss/faiss/IndexBinaryHNSW.cpp +4 -5
  12. data/vendor/faiss/faiss/IndexBinaryHash.cpp +5 -9
  13. data/vendor/faiss/faiss/IndexBinaryIVF.cpp +2 -4
  14. data/vendor/faiss/faiss/IndexEDEN.cpp +273 -0
  15. data/vendor/faiss/faiss/IndexEDEN.h +57 -0
  16. data/vendor/faiss/faiss/IndexFastScan.cpp +15 -4
  17. data/vendor/faiss/faiss/IndexFlat.cpp +13 -50
  18. data/vendor/faiss/faiss/IndexHNSW.cpp +10 -11
  19. data/vendor/faiss/faiss/IndexIDMap.cpp +16 -3
  20. data/vendor/faiss/faiss/IndexIDMap.h +2 -0
  21. data/vendor/faiss/faiss/IndexIVF.cpp +17 -6
  22. data/vendor/faiss/faiss/IndexIVFAdditiveQuantizer.cpp +1 -1
  23. data/vendor/faiss/faiss/IndexIVFAdditiveQuantizerFastScan.cpp +3 -4
  24. data/vendor/faiss/faiss/IndexIVFEDEN.cpp +302 -0
  25. data/vendor/faiss/faiss/IndexIVFEDEN.h +70 -0
  26. data/vendor/faiss/faiss/IndexIVFFastScan.cpp +5 -6
  27. data/vendor/faiss/faiss/IndexIVFFlat.cpp +3 -4
  28. data/vendor/faiss/faiss/IndexIVFIndependentQuantizer.cpp +1 -1
  29. data/vendor/faiss/faiss/IndexIVFPQ.cpp +40 -22
  30. data/vendor/faiss/faiss/IndexIVFPQFastScan.cpp +0 -1
  31. data/vendor/faiss/faiss/IndexIVFRaBitQ.cpp +19 -49
  32. data/vendor/faiss/faiss/IndexIVFRaBitQFastScan.cpp +180 -76
  33. data/vendor/faiss/faiss/IndexIVFRaBitQFastScan.h +5 -4
  34. data/vendor/faiss/faiss/IndexIVFSpectralHash.cpp +8 -6
  35. data/vendor/faiss/faiss/IndexLSH.cpp +2 -3
  36. data/vendor/faiss/faiss/IndexLattice.cpp +5 -0
  37. data/vendor/faiss/faiss/IndexNNDescent.cpp +9 -2
  38. data/vendor/faiss/faiss/IndexNSG.cpp +7 -2
  39. data/vendor/faiss/faiss/IndexPQ.cpp +6 -8
  40. data/vendor/faiss/faiss/IndexPreTransform.cpp +15 -0
  41. data/vendor/faiss/faiss/IndexRaBitQ.cpp +2 -2
  42. data/vendor/faiss/faiss/IndexRaBitQFastScan.cpp +1 -2
  43. data/vendor/faiss/faiss/IndexRaBitQFastScan.h +5 -1
  44. data/vendor/faiss/faiss/IndexRefine.cpp +30 -1
  45. data/vendor/faiss/faiss/IndexReplicas.cpp +1 -2
  46. data/vendor/faiss/faiss/IndexShards.cpp +2 -2
  47. data/vendor/faiss/faiss/IndexShardsIVF.cpp +2 -2
  48. data/vendor/faiss/faiss/MetaIndexes.cpp +2 -4
  49. data/vendor/faiss/faiss/SuperKMeans.cpp +256 -240
  50. data/vendor/faiss/faiss/SuperKMeans.h +30 -0
  51. data/vendor/faiss/faiss/VectorTransform.cpp +33 -2
  52. data/vendor/faiss/faiss/clone_index.cpp +5 -0
  53. data/vendor/faiss/faiss/factory_tools.cpp +47 -4
  54. data/vendor/faiss/faiss/gpu/GpuCloner.cpp +11 -11
  55. data/vendor/faiss/faiss/gpu/GpuIndex.h +34 -11
  56. data/vendor/faiss/faiss/gpu/GpuIndexCagra.h +47 -0
  57. data/vendor/faiss/faiss/gpu/GpuIndexIVF.h +17 -0
  58. data/vendor/faiss/faiss/gpu/GpuIndexIVFScalarQuantizer.h +16 -0
  59. data/vendor/faiss/faiss/gpu/perf/PerfClustering.cpp +1 -1
  60. data/vendor/faiss/faiss/gpu/perf/PerfIVFPQAdd.cpp +2 -2
  61. data/vendor/faiss/faiss/gpu/test/TestGpuIndexIVFScalarQuantizer.cpp +180 -0
  62. data/vendor/faiss/faiss/gpu_metal/MetalIndexIVFFlat.h +1 -5
  63. data/vendor/faiss/faiss/gpu_metal/MetalIndexIVFPQ.h +88 -0
  64. data/vendor/faiss/faiss/gpu_metal/impl/MetalIVFPQ.h +134 -0
  65. data/vendor/faiss/faiss/impl/ClusteringInitialization.cpp +2 -2
  66. data/vendor/faiss/faiss/impl/DistanceComputer.h +34 -0
  67. data/vendor/faiss/faiss/impl/EDENQuantizer.h +119 -0
  68. data/vendor/faiss/faiss/impl/HNSW.cpp +109 -152
  69. data/vendor/faiss/faiss/impl/LocalSearchQuantizer.cpp +2 -2
  70. data/vendor/faiss/faiss/impl/NSG.cpp +3 -1
  71. data/vendor/faiss/faiss/impl/Panorama.h +9 -7
  72. data/vendor/faiss/faiss/impl/PolysemousTraining.cpp +152 -84
  73. data/vendor/faiss/faiss/impl/ProductQuantizer.cpp +34 -22
  74. data/vendor/faiss/faiss/impl/RaBitQUtils.cpp +44 -36
  75. data/vendor/faiss/faiss/impl/RaBitQUtils.h +35 -0
  76. data/vendor/faiss/faiss/impl/RaBitQuantizer.cpp +168 -67
  77. data/vendor/faiss/faiss/impl/RaBitQuantizer.h +19 -0
  78. data/vendor/faiss/faiss/impl/RaBitQuantizerMultiBit.cpp +2 -11
  79. data/vendor/faiss/faiss/impl/ResultHandler.h +25 -31
  80. data/vendor/faiss/faiss/impl/ScalarQuantizer.cpp +258 -57
  81. data/vendor/faiss/faiss/impl/ScalarQuantizer.h +20 -0
  82. data/vendor/faiss/faiss/impl/ThreadedIndex-inl.h +2 -2
  83. data/vendor/faiss/faiss/impl/VisitedTable.cpp +22 -2
  84. data/vendor/faiss/faiss/impl/VisitedTable.h +20 -0
  85. data/vendor/faiss/faiss/impl/binary_hamming/IndexBinaryIVF_impl.h +1 -1
  86. data/vendor/faiss/faiss/impl/binary_hamming/avx2.cpp +4 -4
  87. data/vendor/faiss/faiss/impl/fast_scan/dispatching.h +35 -2
  88. data/vendor/faiss/faiss/impl/hnsw/LockVector.cpp +1 -1
  89. data/vendor/faiss/faiss/impl/index_read.cpp +376 -36
  90. data/vendor/faiss/faiss/impl/index_write.cpp +55 -4
  91. data/vendor/faiss/faiss/impl/lattice_Zn.cpp +8 -9
  92. data/vendor/faiss/faiss/impl/platform_macros.h +3 -1
  93. data/vendor/faiss/faiss/impl/polysemous_training/avx512.cpp +284 -0
  94. data/vendor/faiss/faiss/impl/polysemous_training/dispatch.h +115 -0
  95. data/vendor/faiss/faiss/impl/pq_code_distance/IVFPQ_QueryTables.cpp +0 -1
  96. data/vendor/faiss/faiss/impl/pq_code_distance/PQDistanceComputer_impl.h +26 -15
  97. data/vendor/faiss/faiss/impl/pq_code_distance/avx2.cpp +4 -4
  98. data/vendor/faiss/faiss/impl/result_handler/ResultHandler.cpp +195 -0
  99. data/vendor/faiss/faiss/impl/result_handler/avx2.cpp +133 -0
  100. data/vendor/faiss/faiss/impl/result_handler/avx512.cpp +281 -0
  101. data/vendor/faiss/faiss/impl/scalar_quantizer/EDENQuantizer-avx2.cpp +72 -0
  102. data/vendor/faiss/faiss/impl/scalar_quantizer/EDENQuantizer-avx512.cpp +228 -0
  103. data/vendor/faiss/faiss/impl/scalar_quantizer/EDENQuantizer.cpp +882 -0
  104. data/vendor/faiss/faiss/impl/scalar_quantizer/quantizers.h +9 -8
  105. data/vendor/faiss/faiss/impl/scalar_quantizer/sq-avx2.cpp +85 -23
  106. data/vendor/faiss/faiss/impl/scalar_quantizer/sq-avx512-impl.h +30 -30
  107. data/vendor/faiss/faiss/impl/scalar_quantizer/sq-avx512-spr.cpp +4 -5
  108. data/vendor/faiss/faiss/impl/scalar_quantizer/sq-avx512.cpp +101 -34
  109. data/vendor/faiss/faiss/impl/scalar_quantizer/sq-dispatch.h +136 -0
  110. data/vendor/faiss/faiss/impl/scalar_quantizer/sq-neon.cpp +16 -16
  111. data/vendor/faiss/faiss/impl/simd_dispatch.h +30 -9
  112. data/vendor/faiss/faiss/index_factory.cpp +32 -6
  113. data/vendor/faiss/faiss/invlists/DirectMap.cpp +1 -1
  114. data/vendor/faiss/faiss/invlists/InvertedLists.cpp +2 -2
  115. data/vendor/faiss/faiss/invlists/OnDiskInvertedLists.cpp +19 -4
  116. data/vendor/faiss/faiss/python/python_callbacks.cpp +3 -1
  117. data/vendor/faiss/faiss/svs/IndexSVSFaissUtils.h +60 -0
  118. data/vendor/faiss/faiss/svs/IndexSVSFlat.cpp +26 -1
  119. data/vendor/faiss/faiss/svs/IndexSVSFlat.h +13 -0
  120. data/vendor/faiss/faiss/svs/IndexSVSIVF.cpp +1 -1
  121. data/vendor/faiss/faiss/svs/IndexSVSIVFLeanVec.cpp +1 -1
  122. data/vendor/faiss/faiss/svs/IndexSVSVamana.cpp +31 -1
  123. data/vendor/faiss/faiss/svs/IndexSVSVamana.h +15 -2
  124. data/vendor/faiss/faiss/svs/IndexSVSVamanaLeanVec.cpp +1 -2
  125. data/vendor/faiss/faiss/utils/approx_topk_hamming/approx_topk_hamming.h +1 -1
  126. data/vendor/faiss/faiss/utils/distances.cpp +14 -2
  127. data/vendor/faiss/faiss/utils/distances_simd.cpp +4 -3
  128. data/vendor/faiss/faiss/utils/extra_distances.cpp +4 -14
  129. data/vendor/faiss/faiss/utils/extra_distances.h +1 -2
  130. data/vendor/faiss/faiss/utils/hamming.cpp +1 -1
  131. data/vendor/faiss/faiss/utils/quantize_lut.cpp +29 -8
  132. data/vendor/faiss/faiss/utils/rabitq_simd.h +202 -0
  133. data/vendor/faiss/faiss/utils/simd_impl/distances_avx2.cpp +0 -1
  134. data/vendor/faiss/faiss/utils/simd_impl/distances_avx512.cpp +263 -15
  135. data/vendor/faiss/faiss/utils/simd_impl/distances_rvv.cpp +160 -18
  136. data/vendor/faiss/faiss/utils/simd_impl/rabitq_avx2.cpp +245 -0
  137. data/vendor/faiss/faiss/utils/simd_impl/rabitq_avx512.cpp +273 -0
  138. data/vendor/faiss/faiss/utils/simd_impl/rabitq_avx512_spr.cpp +92 -0
  139. data/vendor/faiss/faiss/utils/simd_impl/rabitq_neon.cpp +11 -0
  140. data/vendor/faiss/faiss/utils/simd_impl/rabitq_rvv.cpp +143 -6
  141. data/vendor/faiss/faiss/utils/simd_levels.cpp +44 -0
  142. data/vendor/faiss/faiss/utils/simd_levels.h +14 -0
  143. data/vendor/faiss/faiss/utils/utils.cpp +9 -27
  144. metadata +16 -1
@@ -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
@@ -0,0 +1,281 @@
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
+ // AVX-512 specialisations of Top1 and Reservoir add_results.
9
+ //
10
+ // Top-1: 16-wide branchless argmin/argmax via mask_blend.
11
+ // Reservoir: VPCOMPRESSPS / VPCOMPRESSD bulk-insert of passing elements,
12
+ // eliminating the per-element threshold branch entirely.
13
+
14
+ #ifdef COMPILE_SIMD_AVX512
15
+
16
+ #include <faiss/impl/ResultHandler.h>
17
+ #include <faiss/utils/popcount.h>
18
+
19
+ #include <immintrin.h>
20
+ #include <type_traits>
21
+
22
+ namespace faiss {
23
+
24
+ namespace {
25
+
26
+ /// Templated AVX-512 implementation of Top1 add_results for both CMax (keeps
27
+ /// the smallest distance) and CMin (keeps the largest similarity).
28
+ template <class C, bool use_sel>
29
+ void top1_add_results_avx512(
30
+ Top1BlockResultHandler<C, use_sel>* self,
31
+ size_t j0,
32
+ size_t j1,
33
+ const float* dis_tab_in) {
34
+ static_assert(
35
+ std::is_same<typename C::T, float>::value,
36
+ "This code expects float distances");
37
+ using TI = typename C::TI;
38
+ const __m512i vstep = _mm512_set_epi32(
39
+ 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0);
40
+
41
+ for (size_t qi = self->i0; qi < self->i1; qi++) {
42
+ const float* dis_tab_i = dis_tab_in + (j1 - j0) * (qi - self->i0) - j0;
43
+
44
+ // Hoist best_dis / best_idx into locals so the compiler keeps them in
45
+ // registers across the inner loop (no aliasing with dis_tab reads).
46
+ float best_dis = self->dis_tab[qi];
47
+ TI best_idx = self->ids_tab[qi];
48
+ size_t j = j0;
49
+
50
+ __m512 vbest = _mm512_set1_ps(best_dis);
51
+ __m512i vbest_idx = _mm512_set1_epi32((int32_t)best_idx);
52
+
53
+ for (; j + 16 <= j1; j += 16) {
54
+ __m512 vdis = _mm512_loadu_ps(dis_tab_i + j);
55
+ __m512i vidx =
56
+ _mm512_add_epi32(_mm512_set1_epi32((int32_t)j), vstep);
57
+
58
+ // CMax (L2 nearest neighbour): keep lane if dis < best.
59
+ // CMin (inner product): keep lane if dis > best.
60
+ __mmask16 mask;
61
+ if constexpr (C::is_max) {
62
+ mask = _mm512_cmp_ps_mask(vdis, vbest, _CMP_LT_OS);
63
+ } else {
64
+ mask = _mm512_cmp_ps_mask(vdis, vbest, _CMP_GT_OS);
65
+ }
66
+ vbest = _mm512_mask_blend_ps(mask, vbest, vdis);
67
+ vbest_idx = _mm512_mask_blend_epi32(mask, vbest_idx, vidx);
68
+ }
69
+
70
+ // Horizontal reduction across 16 lanes.
71
+ alignas(64) float best_arr[16];
72
+ alignas(64) int32_t idx_arr[16];
73
+ _mm512_store_ps(best_arr, vbest);
74
+ _mm512_store_si512((__m512i*)idx_arr, vbest_idx);
75
+ for (int k = 0; k < 16; k++) {
76
+ if (C::cmp(best_dis, best_arr[k])) {
77
+ best_dis = best_arr[k];
78
+ best_idx = (TI)idx_arr[k];
79
+ }
80
+ }
81
+
82
+ // Scalar tail.
83
+ for (; j < j1; j++) {
84
+ if (C::cmp(best_dis, dis_tab_i[j])) {
85
+ best_dis = dis_tab_i[j];
86
+ best_idx = (TI)j;
87
+ }
88
+ }
89
+
90
+ self->dis_tab[qi] = best_dis;
91
+ self->ids_tab[qi] = best_idx;
92
+ }
93
+ }
94
+
95
+ /// Templated AVX-512 implementation of Reservoir add_results for both CMax
96
+ /// and CMin. Uses VPCOMPRESSPS / VPCOMPRESSD to bulk-insert all elements that
97
+ /// beat the current threshold in a single pass, avoiding the per-element branch
98
+ /// that dominates the scalar path.
99
+ ///
100
+ /// Falls back to the scalar NONE path for small reservoirs (capacity < 32)
101
+ /// where the compress-path setup cost outweighs its throughput benefit.
102
+ template <class C, bool use_sel>
103
+ void reservoir_add_results_avx512(
104
+ ReservoirBlockResultHandler<C, use_sel>* self,
105
+ size_t j0,
106
+ size_t j1,
107
+ const float* dis_in) {
108
+ static_assert(
109
+ std::is_same<typename C::T, float>::value,
110
+ "This code expects float distances");
111
+ static_assert(
112
+ std::is_same<typename C::TI, int64_t>::value,
113
+ "This code expects int64_t indices");
114
+
115
+ // AVX-512 compress amortizes its setup cost only for large reservoirs.
116
+ // Benchmarks show a ~9% regression at k=10 (capacity≈20) and a ~25%
117
+ // gain at k=100 (capacity≈200). 32 = 2 × lane-width is the crossover.
118
+ // All reservoirs in a handler share the same capacity, so check once.
119
+ constexpr size_t AVX512_RESERVOIR_MIN_CAPACITY = 32;
120
+ if (self->i0 < self->i1 &&
121
+ self->reservoirs[0].capacity < AVX512_RESERVOIR_MIN_CAPACITY) {
122
+ reservoir_add_results_tpl<C, use_sel, SIMDLevel::NONE>(
123
+ self, j0, j1, dis_in);
124
+ return;
125
+ }
126
+
127
+ const __m512i vstep = _mm512_set_epi32(
128
+ 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0);
129
+
130
+ #pragma omp parallel for
131
+ for (int64_t qi = (int64_t)self->i0; qi < (int64_t)self->i1; qi++) {
132
+ ReservoirTopN<C>& res = self->reservoirs[qi - (int64_t)self->i0];
133
+ const float* dis_tab_i =
134
+ dis_in + (j1 - j0) * (qi - (int64_t)self->i0) - j0;
135
+ size_t j = j0;
136
+
137
+ for (; j + 16 <= j1; j += 16) {
138
+ // Near-capacity: fewer than 16 free slots remain. Fall back to
139
+ // the scalar add_result which handles overflow correctly.
140
+ if (res.i + 16 > res.capacity) {
141
+ for (size_t jj = j; jj < j + 16; jj++)
142
+ res.add_result(dis_tab_i[jj], jj);
143
+ continue;
144
+ }
145
+
146
+ __m512 vthresh = _mm512_set1_ps(res.threshold);
147
+ __m512 vdis = _mm512_loadu_ps(dis_tab_i + j);
148
+
149
+ // CMax (L2): keep elements with dis < threshold.
150
+ // CMin (IP): keep elements with dis > threshold.
151
+ __mmask16 mask;
152
+ if constexpr (C::is_max) {
153
+ mask = _mm512_cmp_ps_mask(vdis, vthresh, _CMP_LT_OS);
154
+ } else {
155
+ mask = _mm512_cmp_ps_mask(vdis, vthresh, _CMP_GT_OS);
156
+ }
157
+ if (mask == 0)
158
+ continue;
159
+
160
+ int count = popcount32(mask);
161
+
162
+ // Compress passing distances into a contiguous run (VPCOMPRESSPS).
163
+ __m512 passing_dis = _mm512_maskz_compress_ps(mask, vdis);
164
+
165
+ // Compress the sequential indices j..j+15 (VPCOMPRESSD).
166
+ __m512i vidx32 =
167
+ _mm512_add_epi32(_mm512_set1_epi32((int32_t)j), vstep);
168
+ __m512i passing_idx32 = _mm512_maskz_compress_epi32(mask, vidx32);
169
+
170
+ // Unconditional 16-element stores are safe: the res.i + 16 <=
171
+ // res.capacity check above guarantees slots res.i .. res.i+15
172
+ // are all unused. The (count .. 15) tail positions get garbage
173
+ // that res.i never reaches.
174
+ _mm512_storeu_ps(res.vals + res.i, passing_dis);
175
+
176
+ // Widen int32 indices to int64 (TI = int64_t) in two 8-element
177
+ // halves and store them.
178
+ _mm512_storeu_si512(
179
+ (void*)(res.ids + res.i),
180
+ _mm512_cvtepi32_epi64(
181
+ _mm512_castsi512_si256(passing_idx32)));
182
+ _mm512_storeu_si512(
183
+ (void*)(res.ids + res.i + 8),
184
+ _mm512_cvtepi32_epi64(
185
+ _mm512_extracti64x4_epi64(passing_idx32, 1)));
186
+
187
+ res.i += count;
188
+ if (res.i >= res.capacity) {
189
+ res.shrink_fuzzy();
190
+ }
191
+ }
192
+
193
+ // Scalar tail.
194
+ for (; j < j1; j++)
195
+ res.add_result(dis_tab_i[j], j);
196
+ }
197
+ }
198
+
199
+ } // namespace
200
+
201
+ // Explicit specialisations for AVX-512
202
+
203
+ template <>
204
+ void top1_add_results_tpl<CMax<float, int64_t>, false, SIMDLevel::AVX512>(
205
+ Top1BlockResultHandler<CMax<float, int64_t>, false>* self,
206
+ size_t j0,
207
+ size_t j1,
208
+ const float* dis_tab) {
209
+ top1_add_results_avx512<CMax<float, int64_t>, false>(self, j0, j1, dis_tab);
210
+ }
211
+
212
+ template <>
213
+ void top1_add_results_tpl<CMax<float, int64_t>, true, SIMDLevel::AVX512>(
214
+ Top1BlockResultHandler<CMax<float, int64_t>, true>* self,
215
+ size_t j0,
216
+ size_t j1,
217
+ const float* dis_tab) {
218
+ top1_add_results_avx512<CMax<float, int64_t>, true>(self, j0, j1, dis_tab);
219
+ }
220
+
221
+ template <>
222
+ void top1_add_results_tpl<CMin<float, int64_t>, false, SIMDLevel::AVX512>(
223
+ Top1BlockResultHandler<CMin<float, int64_t>, false>* self,
224
+ size_t j0,
225
+ size_t j1,
226
+ const float* dis_tab) {
227
+ top1_add_results_avx512<CMin<float, int64_t>, false>(self, j0, j1, dis_tab);
228
+ }
229
+
230
+ template <>
231
+ void top1_add_results_tpl<CMin<float, int64_t>, true, SIMDLevel::AVX512>(
232
+ Top1BlockResultHandler<CMin<float, int64_t>, true>* self,
233
+ size_t j0,
234
+ size_t j1,
235
+ const float* dis_tab) {
236
+ top1_add_results_avx512<CMin<float, int64_t>, true>(self, j0, j1, dis_tab);
237
+ }
238
+
239
+ template <>
240
+ void reservoir_add_results_tpl<CMax<float, int64_t>, false, SIMDLevel::AVX512>(
241
+ ReservoirBlockResultHandler<CMax<float, int64_t>, false>* self,
242
+ size_t j0,
243
+ size_t j1,
244
+ const float* dis_in) {
245
+ reservoir_add_results_avx512<CMax<float, int64_t>, false>(
246
+ self, j0, j1, dis_in);
247
+ }
248
+
249
+ template <>
250
+ void reservoir_add_results_tpl<CMax<float, int64_t>, true, SIMDLevel::AVX512>(
251
+ ReservoirBlockResultHandler<CMax<float, int64_t>, true>* self,
252
+ size_t j0,
253
+ size_t j1,
254
+ const float* dis_in) {
255
+ reservoir_add_results_avx512<CMax<float, int64_t>, true>(
256
+ self, j0, j1, dis_in);
257
+ }
258
+
259
+ template <>
260
+ void reservoir_add_results_tpl<CMin<float, int64_t>, false, SIMDLevel::AVX512>(
261
+ ReservoirBlockResultHandler<CMin<float, int64_t>, false>* self,
262
+ size_t j0,
263
+ size_t j1,
264
+ const float* dis_in) {
265
+ reservoir_add_results_avx512<CMin<float, int64_t>, false>(
266
+ self, j0, j1, dis_in);
267
+ }
268
+
269
+ template <>
270
+ void reservoir_add_results_tpl<CMin<float, int64_t>, true, SIMDLevel::AVX512>(
271
+ ReservoirBlockResultHandler<CMin<float, int64_t>, true>* self,
272
+ size_t j0,
273
+ size_t j1,
274
+ const float* dis_in) {
275
+ reservoir_add_results_avx512<CMin<float, int64_t>, true>(
276
+ self, j0, j1, dis_in);
277
+ }
278
+
279
+ } // namespace faiss
280
+
281
+ #endif // COMPILE_SIMD_AVX512
@@ -0,0 +1,72 @@
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_AVX2
9
+
10
+ #include <faiss/impl/EDENQuantizer.h>
11
+ #include <faiss/impl/simdlib/simdlib_avx2.h>
12
+
13
+ #include <immintrin.h>
14
+
15
+ namespace faiss {
16
+
17
+ namespace eden_distance {
18
+
19
+ namespace {
20
+
21
+ constexpr int kCodeDotLUTKindHalfByte = 2;
22
+
23
+ inline __m256i load_code_bytes_8(const uint8_t* const code[8], size_t i) {
24
+ return _mm256_setr_epi32(
25
+ code[0][i],
26
+ code[1][i],
27
+ code[2][i],
28
+ code[3][i],
29
+ code[4][i],
30
+ code[5][i],
31
+ code[6][i],
32
+ code[7][i]);
33
+ }
34
+
35
+ } // namespace
36
+
37
+ void compute_code_dot_lut_batch_8_avx2(
38
+ const uint8_t* const code[8],
39
+ const float* lut,
40
+ int lut_kind,
41
+ size_t packed_size,
42
+ float dots[8]) {
43
+ __m256 acc = _mm256_setzero_ps();
44
+ const __m256i low_mask = _mm256_set1_epi32(0x0f);
45
+ const __m256i high_offset = _mm256_set1_epi32(16);
46
+
47
+ if (lut_kind == kCodeDotLUTKindHalfByte) {
48
+ for (size_t i = 0; i < packed_size; i++) {
49
+ const __m256i bytes = load_code_bytes_8(code, i);
50
+ const __m256i low = _mm256_and_si256(bytes, low_mask);
51
+ const __m256i high =
52
+ _mm256_add_epi32(_mm256_srli_epi32(bytes, 4), high_offset);
53
+ acc = _mm256_add_ps(acc, _mm256_i32gather_ps(lut, low, 4));
54
+ acc = _mm256_add_ps(acc, _mm256_i32gather_ps(lut, high, 4));
55
+ lut += 32;
56
+ }
57
+ } else {
58
+ for (size_t i = 0; i < packed_size; i++) {
59
+ const __m256i bytes = load_code_bytes_8(code, i);
60
+ acc = _mm256_add_ps(acc, _mm256_i32gather_ps(lut, bytes, 4));
61
+ lut += 256;
62
+ }
63
+ }
64
+
65
+ _mm256_storeu_ps(dots, acc);
66
+ }
67
+
68
+ } // namespace eden_distance
69
+
70
+ } // namespace faiss
71
+
72
+ #endif