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