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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +4 -0
- data/lib/faiss/version.rb +1 -1
- data/vendor/faiss/faiss/AutoTune.cpp +3 -1
- data/vendor/faiss/faiss/Clustering.cpp +9 -1
- data/vendor/faiss/faiss/IVFlib.cpp +14 -3
- data/vendor/faiss/faiss/Index.h +2 -2
- data/vendor/faiss/faiss/IndexAdditiveQuantizer.cpp +9 -10
- data/vendor/faiss/faiss/IndexAdditiveQuantizerFastScan.cpp +2 -3
- data/vendor/faiss/faiss/IndexBinaryFromFloat.cpp +1 -2
- data/vendor/faiss/faiss/IndexBinaryHNSW.cpp +4 -5
- data/vendor/faiss/faiss/IndexBinaryHash.cpp +5 -9
- data/vendor/faiss/faiss/IndexBinaryIVF.cpp +2 -4
- data/vendor/faiss/faiss/IndexEDEN.cpp +273 -0
- data/vendor/faiss/faiss/IndexEDEN.h +57 -0
- data/vendor/faiss/faiss/IndexFastScan.cpp +15 -4
- data/vendor/faiss/faiss/IndexFlat.cpp +13 -50
- data/vendor/faiss/faiss/IndexHNSW.cpp +10 -11
- data/vendor/faiss/faiss/IndexIDMap.cpp +16 -3
- data/vendor/faiss/faiss/IndexIDMap.h +2 -0
- data/vendor/faiss/faiss/IndexIVF.cpp +17 -6
- data/vendor/faiss/faiss/IndexIVFAdditiveQuantizer.cpp +1 -1
- data/vendor/faiss/faiss/IndexIVFAdditiveQuantizerFastScan.cpp +3 -4
- data/vendor/faiss/faiss/IndexIVFEDEN.cpp +302 -0
- data/vendor/faiss/faiss/IndexIVFEDEN.h +70 -0
- data/vendor/faiss/faiss/IndexIVFFastScan.cpp +5 -6
- data/vendor/faiss/faiss/IndexIVFFlat.cpp +3 -4
- data/vendor/faiss/faiss/IndexIVFIndependentQuantizer.cpp +1 -1
- data/vendor/faiss/faiss/IndexIVFPQ.cpp +40 -22
- data/vendor/faiss/faiss/IndexIVFPQFastScan.cpp +0 -1
- data/vendor/faiss/faiss/IndexIVFRaBitQ.cpp +19 -49
- data/vendor/faiss/faiss/IndexIVFRaBitQFastScan.cpp +180 -76
- data/vendor/faiss/faiss/IndexIVFRaBitQFastScan.h +5 -4
- data/vendor/faiss/faiss/IndexIVFSpectralHash.cpp +8 -6
- data/vendor/faiss/faiss/IndexLSH.cpp +2 -3
- data/vendor/faiss/faiss/IndexLattice.cpp +5 -0
- data/vendor/faiss/faiss/IndexNNDescent.cpp +9 -2
- data/vendor/faiss/faiss/IndexNSG.cpp +7 -2
- data/vendor/faiss/faiss/IndexPQ.cpp +6 -8
- data/vendor/faiss/faiss/IndexPreTransform.cpp +15 -0
- data/vendor/faiss/faiss/IndexRaBitQ.cpp +2 -2
- data/vendor/faiss/faiss/IndexRaBitQFastScan.cpp +1 -2
- data/vendor/faiss/faiss/IndexRaBitQFastScan.h +5 -1
- data/vendor/faiss/faiss/IndexRefine.cpp +30 -1
- data/vendor/faiss/faiss/IndexReplicas.cpp +1 -2
- data/vendor/faiss/faiss/IndexShards.cpp +2 -2
- data/vendor/faiss/faiss/IndexShardsIVF.cpp +2 -2
- data/vendor/faiss/faiss/MetaIndexes.cpp +2 -4
- data/vendor/faiss/faiss/SuperKMeans.cpp +256 -240
- data/vendor/faiss/faiss/SuperKMeans.h +30 -0
- data/vendor/faiss/faiss/VectorTransform.cpp +33 -2
- data/vendor/faiss/faiss/clone_index.cpp +5 -0
- data/vendor/faiss/faiss/factory_tools.cpp +47 -4
- data/vendor/faiss/faiss/gpu/GpuCloner.cpp +11 -11
- data/vendor/faiss/faiss/gpu/GpuIndex.h +34 -11
- data/vendor/faiss/faiss/gpu/GpuIndexCagra.h +47 -0
- data/vendor/faiss/faiss/gpu/GpuIndexIVF.h +17 -0
- data/vendor/faiss/faiss/gpu/GpuIndexIVFScalarQuantizer.h +16 -0
- data/vendor/faiss/faiss/gpu/perf/PerfClustering.cpp +1 -1
- data/vendor/faiss/faiss/gpu/perf/PerfIVFPQAdd.cpp +2 -2
- data/vendor/faiss/faiss/gpu/test/TestGpuIndexIVFScalarQuantizer.cpp +180 -0
- data/vendor/faiss/faiss/gpu_metal/MetalIndexIVFFlat.h +1 -5
- data/vendor/faiss/faiss/gpu_metal/MetalIndexIVFPQ.h +88 -0
- data/vendor/faiss/faiss/gpu_metal/impl/MetalIVFPQ.h +134 -0
- data/vendor/faiss/faiss/impl/ClusteringInitialization.cpp +2 -2
- data/vendor/faiss/faiss/impl/DistanceComputer.h +34 -0
- data/vendor/faiss/faiss/impl/EDENQuantizer.h +119 -0
- data/vendor/faiss/faiss/impl/HNSW.cpp +109 -152
- data/vendor/faiss/faiss/impl/LocalSearchQuantizer.cpp +2 -2
- data/vendor/faiss/faiss/impl/NSG.cpp +3 -1
- data/vendor/faiss/faiss/impl/Panorama.h +9 -7
- data/vendor/faiss/faiss/impl/PolysemousTraining.cpp +152 -84
- data/vendor/faiss/faiss/impl/ProductQuantizer.cpp +34 -22
- data/vendor/faiss/faiss/impl/RaBitQUtils.cpp +44 -36
- data/vendor/faiss/faiss/impl/RaBitQUtils.h +35 -0
- data/vendor/faiss/faiss/impl/RaBitQuantizer.cpp +168 -67
- data/vendor/faiss/faiss/impl/RaBitQuantizer.h +19 -0
- data/vendor/faiss/faiss/impl/RaBitQuantizerMultiBit.cpp +2 -11
- data/vendor/faiss/faiss/impl/ResultHandler.h +25 -31
- data/vendor/faiss/faiss/impl/ScalarQuantizer.cpp +258 -57
- data/vendor/faiss/faiss/impl/ScalarQuantizer.h +20 -0
- data/vendor/faiss/faiss/impl/ThreadedIndex-inl.h +2 -2
- data/vendor/faiss/faiss/impl/VisitedTable.cpp +22 -2
- data/vendor/faiss/faiss/impl/VisitedTable.h +20 -0
- data/vendor/faiss/faiss/impl/binary_hamming/IndexBinaryIVF_impl.h +1 -1
- data/vendor/faiss/faiss/impl/binary_hamming/avx2.cpp +4 -4
- data/vendor/faiss/faiss/impl/fast_scan/dispatching.h +35 -2
- data/vendor/faiss/faiss/impl/hnsw/LockVector.cpp +1 -1
- data/vendor/faiss/faiss/impl/index_read.cpp +376 -36
- data/vendor/faiss/faiss/impl/index_write.cpp +55 -4
- data/vendor/faiss/faiss/impl/lattice_Zn.cpp +8 -9
- data/vendor/faiss/faiss/impl/platform_macros.h +3 -1
- data/vendor/faiss/faiss/impl/polysemous_training/avx512.cpp +284 -0
- data/vendor/faiss/faiss/impl/polysemous_training/dispatch.h +115 -0
- data/vendor/faiss/faiss/impl/pq_code_distance/IVFPQ_QueryTables.cpp +0 -1
- data/vendor/faiss/faiss/impl/pq_code_distance/PQDistanceComputer_impl.h +26 -15
- data/vendor/faiss/faiss/impl/pq_code_distance/avx2.cpp +4 -4
- data/vendor/faiss/faiss/impl/result_handler/ResultHandler.cpp +195 -0
- data/vendor/faiss/faiss/impl/result_handler/avx2.cpp +133 -0
- data/vendor/faiss/faiss/impl/result_handler/avx512.cpp +281 -0
- data/vendor/faiss/faiss/impl/scalar_quantizer/EDENQuantizer-avx2.cpp +72 -0
- data/vendor/faiss/faiss/impl/scalar_quantizer/EDENQuantizer-avx512.cpp +228 -0
- data/vendor/faiss/faiss/impl/scalar_quantizer/EDENQuantizer.cpp +882 -0
- data/vendor/faiss/faiss/impl/scalar_quantizer/quantizers.h +9 -8
- data/vendor/faiss/faiss/impl/scalar_quantizer/sq-avx2.cpp +85 -23
- data/vendor/faiss/faiss/impl/scalar_quantizer/sq-avx512-impl.h +30 -30
- data/vendor/faiss/faiss/impl/scalar_quantizer/sq-avx512-spr.cpp +4 -5
- data/vendor/faiss/faiss/impl/scalar_quantizer/sq-avx512.cpp +101 -34
- data/vendor/faiss/faiss/impl/scalar_quantizer/sq-dispatch.h +136 -0
- data/vendor/faiss/faiss/impl/scalar_quantizer/sq-neon.cpp +16 -16
- data/vendor/faiss/faiss/impl/simd_dispatch.h +30 -9
- data/vendor/faiss/faiss/index_factory.cpp +32 -6
- data/vendor/faiss/faiss/invlists/DirectMap.cpp +1 -1
- data/vendor/faiss/faiss/invlists/InvertedLists.cpp +2 -2
- data/vendor/faiss/faiss/invlists/OnDiskInvertedLists.cpp +19 -4
- data/vendor/faiss/faiss/python/python_callbacks.cpp +3 -1
- data/vendor/faiss/faiss/svs/IndexSVSFaissUtils.h +60 -0
- data/vendor/faiss/faiss/svs/IndexSVSFlat.cpp +26 -1
- data/vendor/faiss/faiss/svs/IndexSVSFlat.h +13 -0
- data/vendor/faiss/faiss/svs/IndexSVSIVF.cpp +1 -1
- data/vendor/faiss/faiss/svs/IndexSVSIVFLeanVec.cpp +1 -1
- data/vendor/faiss/faiss/svs/IndexSVSVamana.cpp +31 -1
- data/vendor/faiss/faiss/svs/IndexSVSVamana.h +15 -2
- data/vendor/faiss/faiss/svs/IndexSVSVamanaLeanVec.cpp +1 -2
- data/vendor/faiss/faiss/utils/approx_topk_hamming/approx_topk_hamming.h +1 -1
- data/vendor/faiss/faiss/utils/distances.cpp +14 -2
- data/vendor/faiss/faiss/utils/distances_simd.cpp +4 -3
- data/vendor/faiss/faiss/utils/extra_distances.cpp +4 -14
- data/vendor/faiss/faiss/utils/extra_distances.h +1 -2
- data/vendor/faiss/faiss/utils/hamming.cpp +1 -1
- data/vendor/faiss/faiss/utils/quantize_lut.cpp +29 -8
- data/vendor/faiss/faiss/utils/rabitq_simd.h +202 -0
- data/vendor/faiss/faiss/utils/simd_impl/distances_avx2.cpp +0 -1
- data/vendor/faiss/faiss/utils/simd_impl/distances_avx512.cpp +263 -15
- data/vendor/faiss/faiss/utils/simd_impl/distances_rvv.cpp +160 -18
- data/vendor/faiss/faiss/utils/simd_impl/rabitq_avx2.cpp +245 -0
- data/vendor/faiss/faiss/utils/simd_impl/rabitq_avx512.cpp +273 -0
- data/vendor/faiss/faiss/utils/simd_impl/rabitq_avx512_spr.cpp +92 -0
- data/vendor/faiss/faiss/utils/simd_impl/rabitq_neon.cpp +11 -0
- data/vendor/faiss/faiss/utils/simd_impl/rabitq_rvv.cpp +143 -6
- data/vendor/faiss/faiss/utils/simd_levels.cpp +44 -0
- data/vendor/faiss/faiss/utils/simd_levels.h +14 -0
- data/vendor/faiss/faiss/utils/utils.cpp +9 -27
- 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
|