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
|
@@ -129,6 +129,22 @@ struct ElementOpIP {
|
|
|
129
129
|
}
|
|
130
130
|
};
|
|
131
131
|
|
|
132
|
+
struct ElementOpL2 {
|
|
133
|
+
static svfloat32_t op(svbool_t pg, svfloat32_t x, svfloat32_t y) {
|
|
134
|
+
const svfloat32_t diff = svsub_f32_x(pg, x, y);
|
|
135
|
+
return svmul_f32_x(pg, diff, diff);
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
static svfloat32_t merge(
|
|
139
|
+
svbool_t pg,
|
|
140
|
+
svfloat32_t z,
|
|
141
|
+
svfloat32_t x,
|
|
142
|
+
svfloat32_t y) {
|
|
143
|
+
const svfloat32_t diff = svsub_f32_x(pg, x, y);
|
|
144
|
+
return svmla_f32_x(pg, z, diff, diff);
|
|
145
|
+
}
|
|
146
|
+
};
|
|
147
|
+
|
|
132
148
|
template <typename ElementOp>
|
|
133
149
|
void fvec_op_ny_sve_d1(float* dis, const float* x, const float* y, size_t ny) {
|
|
134
150
|
const size_t lanes = svcntw();
|
|
@@ -513,13 +529,87 @@ void fvec_L2sqr_ny<SIMDLevel::ARM_SVE>(
|
|
|
513
529
|
const float* y,
|
|
514
530
|
size_t d,
|
|
515
531
|
size_t ny) {
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
532
|
+
const size_t lanes = static_cast<size_t>(svcntw());
|
|
533
|
+
|
|
534
|
+
switch (d) {
|
|
535
|
+
case 1:
|
|
536
|
+
fvec_op_ny_sve_d1<ElementOpL2>(dis, x, y, ny);
|
|
537
|
+
break;
|
|
538
|
+
|
|
539
|
+
case 2:
|
|
540
|
+
fvec_op_ny_sve_d2<ElementOpL2>(dis, x, y, ny);
|
|
541
|
+
break;
|
|
542
|
+
|
|
543
|
+
case 4:
|
|
544
|
+
fvec_op_ny_sve_d4<ElementOpL2>(dis, x, y, ny);
|
|
545
|
+
break;
|
|
546
|
+
|
|
547
|
+
case 8:
|
|
548
|
+
fvec_op_ny_sve_d8<ElementOpL2>(dis, x, y, ny);
|
|
549
|
+
break;
|
|
550
|
+
|
|
551
|
+
default:
|
|
552
|
+
if (d == lanes)
|
|
553
|
+
fvec_op_ny_sve_lanes1<ElementOpL2>(dis, x, y, ny);
|
|
554
|
+
else if (d == lanes * 2)
|
|
555
|
+
fvec_op_ny_sve_lanes2<ElementOpL2>(dis, x, y, ny);
|
|
556
|
+
else if (d == lanes * 3)
|
|
557
|
+
fvec_op_ny_sve_lanes3<ElementOpL2>(dis, x, y, ny);
|
|
558
|
+
else if (d == lanes * 4)
|
|
559
|
+
fvec_op_ny_sve_lanes4<ElementOpL2>(dis, x, y, ny);
|
|
560
|
+
else {
|
|
561
|
+
// Fallback: use autovectorized L2sqr
|
|
562
|
+
for (size_t i = 0; i < ny; i++) {
|
|
563
|
+
dis[i] = fvec_L2sqr<SIMDLevel::ARM_SVE>(x, y, d);
|
|
564
|
+
y += d;
|
|
565
|
+
}
|
|
566
|
+
}
|
|
567
|
+
break;
|
|
568
|
+
}
|
|
569
|
+
}
|
|
570
|
+
|
|
571
|
+
namespace {
|
|
572
|
+
|
|
573
|
+
/// Low-dimensional L2sqr nearest (D in {2,4,8}). The data is row-major
|
|
574
|
+
/// (centroid c is y[c*D .. c*D+D]). Each SVE lane tracks one centroid across
|
|
575
|
+
/// batches, keeping the lane-local minimum and index in registers. The scratch
|
|
576
|
+
/// buffer is not written, matching the AVX2/AVX512 D2/D4/D8 implementations.
|
|
577
|
+
template <int D>
|
|
578
|
+
size_t fvec_L2sqr_ny_nearest_lowdim(
|
|
579
|
+
float* /*distances_tmp_buffer*/,
|
|
580
|
+
const float* x,
|
|
581
|
+
const float* y,
|
|
582
|
+
size_t ny) {
|
|
583
|
+
const size_t lanes = svcntw();
|
|
584
|
+
svfloat32_t global_mins = svdup_n_f32(HUGE_VALF);
|
|
585
|
+
svuint32_t global_ids = svdup_n_u32(0);
|
|
586
|
+
svuint32_t current_ids = svindex_u32(0, 1);
|
|
587
|
+
|
|
588
|
+
for (size_t c = 0; c < ny; c += lanes) {
|
|
589
|
+
const svbool_t pg = svwhilelt_b32_u64(c, ny);
|
|
590
|
+
svfloat32_t distances = svdup_n_f32(0.0f);
|
|
591
|
+
for (uint32_t j = 0; j < D; ++j) {
|
|
592
|
+
const svuint32_t offsets = svindex_u32(j, D);
|
|
593
|
+
const svfloat32_t yv =
|
|
594
|
+
svld1_gather_u32index_f32(pg, y + c * D, offsets);
|
|
595
|
+
const svfloat32_t diff = svsub_n_f32_x(pg, yv, x[j]);
|
|
596
|
+
distances = svmla_f32_m(pg, distances, diff, diff);
|
|
597
|
+
}
|
|
598
|
+
|
|
599
|
+
const svbool_t closer = svcmplt_f32(pg, distances, global_mins);
|
|
600
|
+
global_mins = svsel_f32(closer, distances, global_mins);
|
|
601
|
+
global_ids = svsel_u32(closer, current_ids, global_ids);
|
|
602
|
+
current_ids =
|
|
603
|
+
svadd_n_u32_x(pg, current_ids, static_cast<uint32_t>(lanes));
|
|
520
604
|
}
|
|
605
|
+
|
|
606
|
+
const svbool_t all = svptrue_b32();
|
|
607
|
+
const float global_min = svminv_f32(all, global_mins);
|
|
608
|
+
return svminv_u32(svcmpeq_n_f32(all, global_mins, global_min), global_ids);
|
|
521
609
|
}
|
|
522
610
|
|
|
611
|
+
} // namespace
|
|
612
|
+
|
|
523
613
|
template <>
|
|
524
614
|
size_t fvec_L2sqr_ny_nearest<SIMDLevel::ARM_SVE>(
|
|
525
615
|
float* distances_tmp_buffer,
|
|
@@ -527,14 +617,52 @@ size_t fvec_L2sqr_ny_nearest<SIMDLevel::ARM_SVE>(
|
|
|
527
617
|
const float* y,
|
|
528
618
|
size_t d,
|
|
529
619
|
size_t ny) {
|
|
530
|
-
|
|
620
|
+
switch (d) {
|
|
621
|
+
case 2:
|
|
622
|
+
return fvec_L2sqr_ny_nearest_lowdim<2>(
|
|
623
|
+
distances_tmp_buffer, x, y, ny);
|
|
624
|
+
case 4:
|
|
625
|
+
return fvec_L2sqr_ny_nearest_lowdim<4>(
|
|
626
|
+
distances_tmp_buffer, x, y, ny);
|
|
627
|
+
case 8:
|
|
628
|
+
return fvec_L2sqr_ny_nearest_lowdim<8>(
|
|
629
|
+
distances_tmp_buffer, x, y, ny);
|
|
630
|
+
}
|
|
631
|
+
|
|
632
|
+
const size_t lanes = static_cast<size_t>(svcntw());
|
|
531
633
|
|
|
532
634
|
size_t nearest_idx = 0;
|
|
533
635
|
float min_dis = HUGE_VALF;
|
|
534
636
|
|
|
535
|
-
for (size_t i = 0; i < ny; i
|
|
536
|
-
|
|
537
|
-
|
|
637
|
+
for (size_t i = 0; i < ny; ++i) {
|
|
638
|
+
const float* yi = y + i * d;
|
|
639
|
+
size_t j = 0;
|
|
640
|
+
|
|
641
|
+
svfloat32_t accv = svdup_n_f32(0.0f);
|
|
642
|
+
|
|
643
|
+
for (; j + lanes <= d; j += lanes) {
|
|
644
|
+
const svbool_t pg = svptrue_b32();
|
|
645
|
+
const svfloat32_t xv = svld1_f32(pg, x + j);
|
|
646
|
+
const svfloat32_t yv = svld1_f32(pg, yi + j);
|
|
647
|
+
const svfloat32_t diff = svsub_f32_x(pg, xv, yv);
|
|
648
|
+
accv = svmla_f32_x(pg, accv, diff, diff);
|
|
649
|
+
}
|
|
650
|
+
|
|
651
|
+
if (j < d) {
|
|
652
|
+
const svbool_t pg = svwhilelt_b32_u64(j, d);
|
|
653
|
+
const svfloat32_t xv = svld1_f32(pg, x + j);
|
|
654
|
+
const svfloat32_t yv = svld1_f32(pg, yi + j);
|
|
655
|
+
const svfloat32_t diff = svsub_f32_x(pg, xv, yv);
|
|
656
|
+
// Merging predication: lanes outside the tail keep the partial
|
|
657
|
+
// sums accumulated above, which the reduction below still adds in.
|
|
658
|
+
// `_x` would leave them unspecified.
|
|
659
|
+
accv = svmla_f32_m(pg, accv, diff, diff);
|
|
660
|
+
}
|
|
661
|
+
|
|
662
|
+
const float dist = svaddv_f32(svptrue_b32(), accv);
|
|
663
|
+
distances_tmp_buffer[i] = dist;
|
|
664
|
+
if (dist < min_dis) {
|
|
665
|
+
min_dis = dist;
|
|
538
666
|
nearest_idx = i;
|
|
539
667
|
}
|
|
540
668
|
}
|
|
@@ -542,7 +670,6 @@ size_t fvec_L2sqr_ny_nearest<SIMDLevel::ARM_SVE>(
|
|
|
542
670
|
return nearest_idx;
|
|
543
671
|
}
|
|
544
672
|
|
|
545
|
-
FAISS_PRAGMA_IMPRECISE_FUNCTION_BEGIN
|
|
546
673
|
template <>
|
|
547
674
|
void fvec_L2sqr_ny_transposed<SIMDLevel::ARM_SVE>(
|
|
548
675
|
float* dis,
|
|
@@ -552,22 +679,26 @@ void fvec_L2sqr_ny_transposed<SIMDLevel::ARM_SVE>(
|
|
|
552
679
|
size_t d,
|
|
553
680
|
size_t d_offset,
|
|
554
681
|
size_t ny) {
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
682
|
+
const size_t lanes = static_cast<size_t>(svcntw());
|
|
683
|
+
const float x_sq = fvec_norm_L2sqr(x, d);
|
|
684
|
+
|
|
685
|
+
for (size_t k = 0; k < ny; k += lanes) {
|
|
686
|
+
svbool_t pg = svwhilelt_b32_u64(k, ny);
|
|
687
|
+
svfloat32_t acc = svdup_n_f32(0.0f);
|
|
560
688
|
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
dp += x[j] * y[i + j * d_offset];
|
|
689
|
+
for (size_t j = 0; j < d; ++j) {
|
|
690
|
+
svfloat32_t ychunk = svld1_f32(pg, y + j * d_offset + k);
|
|
691
|
+
svfloat32_t xj = svdup_n_f32(x[j]);
|
|
692
|
+
acc = svmla_f32_x(pg, acc, xj, ychunk);
|
|
566
693
|
}
|
|
567
|
-
|
|
694
|
+
|
|
695
|
+
svfloat32_t ysq = svld1_f32(pg, y_sqlen + k);
|
|
696
|
+
svfloat32_t two_acc = svmul_f32_x(pg, acc, svdup_n_f32(2.0f));
|
|
697
|
+
svfloat32_t sum = svadd_f32_x(pg, svdup_n_f32(x_sq), ysq);
|
|
698
|
+
svfloat32_t res = svsub_f32_x(pg, sum, two_acc);
|
|
699
|
+
svst1_f32(pg, dis + k, res);
|
|
568
700
|
}
|
|
569
701
|
}
|
|
570
|
-
FAISS_PRAGMA_IMPRECISE_FUNCTION_END
|
|
571
702
|
|
|
572
703
|
template <>
|
|
573
704
|
size_t fvec_L2sqr_ny_nearest_y_transposed<SIMDLevel::ARM_SVE>(
|
|
@@ -578,20 +709,53 @@ size_t fvec_L2sqr_ny_nearest_y_transposed<SIMDLevel::ARM_SVE>(
|
|
|
578
709
|
size_t d,
|
|
579
710
|
size_t d_offset,
|
|
580
711
|
size_t ny) {
|
|
581
|
-
|
|
582
|
-
|
|
712
|
+
const size_t lanes = svcntw();
|
|
713
|
+
const float x_sq = fvec_norm_L2sqr(x, d);
|
|
583
714
|
|
|
584
|
-
size_t
|
|
585
|
-
|
|
715
|
+
size_t current_min_idx = 0;
|
|
716
|
+
svfloat32_t current_min_v = svdup_n_f32(HUGE_VALF);
|
|
586
717
|
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
718
|
+
// Max SVE vector length is 2048 bits → 64 fp32 lanes
|
|
719
|
+
float tmp_buf[64];
|
|
720
|
+
|
|
721
|
+
for (size_t k = 0; k < ny; k += lanes) {
|
|
722
|
+
svbool_t pg = svwhilelt_b32_u64(k, ny);
|
|
723
|
+
svfloat32_t acc = svdup_n_f32(0.0f);
|
|
724
|
+
|
|
725
|
+
for (size_t j = 0; j < d; ++j) {
|
|
726
|
+
svfloat32_t ychunk = svld1_f32(pg, y + j * d_offset + k);
|
|
727
|
+
svfloat32_t xj = svdup_n_f32(x[j]);
|
|
728
|
+
acc = svmla_f32_x(pg, acc, xj, ychunk);
|
|
729
|
+
}
|
|
730
|
+
|
|
731
|
+
svfloat32_t ysq = svld1_f32(pg, y_sqlen + k);
|
|
732
|
+
svfloat32_t two_acc = svmul_f32_x(pg, acc, svdup_n_f32(2.0f));
|
|
733
|
+
svfloat32_t sum = svadd_f32_x(pg, svdup_n_f32(x_sq), ysq);
|
|
734
|
+
svfloat32_t res = svsub_f32_x(pg, sum, two_acc);
|
|
735
|
+
|
|
736
|
+
svst1_f32(pg, distances_tmp_buffer + k, res);
|
|
737
|
+
|
|
738
|
+
svbool_t less_mask = svcmplt_f32(pg, res, current_min_v);
|
|
739
|
+
|
|
740
|
+
if (svptest_any(pg, less_mask)) {
|
|
741
|
+
float vec_min = svminv_f32(pg, res);
|
|
742
|
+
|
|
743
|
+
current_min_v =
|
|
744
|
+
svmin_f32_x(pg, current_min_v, svdup_n_f32(vec_min));
|
|
745
|
+
|
|
746
|
+
svst1_f32(pg, tmp_buf, res);
|
|
747
|
+
|
|
748
|
+
size_t cnt = (size_t)svcntw();
|
|
749
|
+
for (size_t lane = 0; lane < cnt && (k + lane) < ny; ++lane) {
|
|
750
|
+
if (tmp_buf[lane] == vec_min) {
|
|
751
|
+
current_min_idx = k + lane;
|
|
752
|
+
break;
|
|
753
|
+
}
|
|
754
|
+
}
|
|
591
755
|
}
|
|
592
756
|
}
|
|
593
757
|
|
|
594
|
-
return
|
|
758
|
+
return current_min_idx;
|
|
595
759
|
}
|
|
596
760
|
|
|
597
761
|
template <>
|
|
@@ -9,6 +9,35 @@
|
|
|
9
9
|
|
|
10
10
|
#include <immintrin.h>
|
|
11
11
|
|
|
12
|
+
#include <memory>
|
|
13
|
+
|
|
14
|
+
#include <faiss/impl/AuxIndexStructures.h>
|
|
15
|
+
#include <faiss/impl/ResultHandler.h>
|
|
16
|
+
#include <faiss/utils/distances_fused/distances_fused.h>
|
|
17
|
+
#include <faiss/utils/simd_impl/exhaustive_L2sqr_blas_cmax.h>
|
|
18
|
+
|
|
19
|
+
#ifndef FINTEGER
|
|
20
|
+
#define FINTEGER long
|
|
21
|
+
#endif
|
|
22
|
+
|
|
23
|
+
extern "C" {
|
|
24
|
+
|
|
25
|
+
int sgemm_(
|
|
26
|
+
const char* transa,
|
|
27
|
+
const char* transb,
|
|
28
|
+
FINTEGER* m,
|
|
29
|
+
FINTEGER* n,
|
|
30
|
+
FINTEGER* k,
|
|
31
|
+
const float* alpha,
|
|
32
|
+
const float* a,
|
|
33
|
+
FINTEGER* lda,
|
|
34
|
+
const float* b,
|
|
35
|
+
FINTEGER* ldb,
|
|
36
|
+
float* beta,
|
|
37
|
+
float* c,
|
|
38
|
+
FINTEGER* ldc);
|
|
39
|
+
}
|
|
40
|
+
|
|
12
41
|
#define THE_SIMD_LEVEL SIMDLevel::AVX512
|
|
13
42
|
#include <faiss/utils/simd_impl/distances_autovec-inl.h>
|
|
14
43
|
// NOLINTNEXTLINE(facebook-hte-InlineHeader)
|
|
@@ -954,21 +983,6 @@ size_t fvec_L2sqr_ny_nearest<SIMDLevel::AVX512>(
|
|
|
954
983
|
&fvec_L2sqr_ny_nearest_D8<SIMDLevel::AVX512>);
|
|
955
984
|
}
|
|
956
985
|
|
|
957
|
-
template <>
|
|
958
|
-
size_t fvec_L2sqr_ny_nearest_y_transposed<SIMDLevel::AVX512>(
|
|
959
|
-
float* distances_tmp_buffer,
|
|
960
|
-
const float* x,
|
|
961
|
-
const float* y,
|
|
962
|
-
const float* y_sqlen,
|
|
963
|
-
size_t d,
|
|
964
|
-
size_t d_offset,
|
|
965
|
-
size_t ny) {
|
|
966
|
-
return fvec_L2sqr_ny_nearest_y_transposed<SIMDLevel::NONE>(
|
|
967
|
-
distances_tmp_buffer, x, y, y_sqlen, d, d_offset, ny);
|
|
968
|
-
}
|
|
969
|
-
|
|
970
|
-
// TODO: Following functions are not used in the current codebase. Check AVX2 ,
|
|
971
|
-
// respective implementation has been used
|
|
972
986
|
template <size_t DIM>
|
|
973
987
|
size_t fvec_L2sqr_ny_nearest_y_transposed_D(
|
|
974
988
|
float* /* distances_tmp_buffer */,
|
|
@@ -1082,6 +1096,33 @@ size_t fvec_L2sqr_ny_nearest_y_transposed_D(
|
|
|
1082
1096
|
return current_min_index;
|
|
1083
1097
|
}
|
|
1084
1098
|
|
|
1099
|
+
template <>
|
|
1100
|
+
size_t fvec_L2sqr_ny_nearest_y_transposed<SIMDLevel::AVX512>(
|
|
1101
|
+
float* distances_tmp_buffer,
|
|
1102
|
+
const float* x,
|
|
1103
|
+
const float* y,
|
|
1104
|
+
const float* y_sqlen,
|
|
1105
|
+
size_t d,
|
|
1106
|
+
size_t d_offset,
|
|
1107
|
+
size_t ny) {
|
|
1108
|
+
// optimized for a few special cases
|
|
1109
|
+
#define DISPATCH(dval) \
|
|
1110
|
+
case dval: \
|
|
1111
|
+
return fvec_L2sqr_ny_nearest_y_transposed_D<dval>( \
|
|
1112
|
+
distances_tmp_buffer, x, y, y_sqlen, d_offset, ny);
|
|
1113
|
+
|
|
1114
|
+
switch (d) {
|
|
1115
|
+
DISPATCH(1)
|
|
1116
|
+
DISPATCH(2)
|
|
1117
|
+
DISPATCH(4)
|
|
1118
|
+
DISPATCH(8)
|
|
1119
|
+
default:
|
|
1120
|
+
return fvec_L2sqr_ny_nearest_y_transposed<SIMDLevel::NONE>(
|
|
1121
|
+
distances_tmp_buffer, x, y, y_sqlen, d, d_offset, ny);
|
|
1122
|
+
}
|
|
1123
|
+
#undef DISPATCH
|
|
1124
|
+
}
|
|
1125
|
+
|
|
1085
1126
|
template <>
|
|
1086
1127
|
int fvec_madd_and_argmin<SIMDLevel::AVX512>(
|
|
1087
1128
|
size_t n,
|
|
@@ -1092,4 +1133,211 @@ int fvec_madd_and_argmin<SIMDLevel::AVX512>(
|
|
|
1092
1133
|
return fvec_madd_and_argmin_sse(n, a, bf, b, c);
|
|
1093
1134
|
}
|
|
1094
1135
|
|
|
1136
|
+
template <>
|
|
1137
|
+
void exhaustive_L2sqr_blas_cmax<SIMDLevel::AVX512>(
|
|
1138
|
+
const float* x,
|
|
1139
|
+
const float* y,
|
|
1140
|
+
size_t d,
|
|
1141
|
+
size_t nx,
|
|
1142
|
+
size_t ny,
|
|
1143
|
+
Top1BlockResultHandler<CMax<float, int64_t>>& res,
|
|
1144
|
+
const float* y_norms) {
|
|
1145
|
+
// BLAS does not like empty matrices
|
|
1146
|
+
if (nx == 0 || ny == 0) {
|
|
1147
|
+
return;
|
|
1148
|
+
}
|
|
1149
|
+
|
|
1150
|
+
/* block sizes */
|
|
1151
|
+
const size_t bs_x = distance_compute_blas_query_bs;
|
|
1152
|
+
const size_t bs_y = distance_compute_blas_database_bs;
|
|
1153
|
+
std::unique_ptr<float[]> ip_block(new float[bs_x * bs_y]);
|
|
1154
|
+
std::unique_ptr<float[]> x_norms(new float[nx]);
|
|
1155
|
+
std::unique_ptr<float[]> del2;
|
|
1156
|
+
|
|
1157
|
+
fvec_norms_L2sqr(x_norms.get(), x, d, nx);
|
|
1158
|
+
|
|
1159
|
+
if (!y_norms) {
|
|
1160
|
+
float* y_norms2 = new float[ny];
|
|
1161
|
+
del2.reset(y_norms2);
|
|
1162
|
+
fvec_norms_L2sqr(y_norms2, y, d, ny);
|
|
1163
|
+
y_norms = y_norms2;
|
|
1164
|
+
}
|
|
1165
|
+
|
|
1166
|
+
for (size_t i0 = 0; i0 < nx; i0 += bs_x) {
|
|
1167
|
+
size_t i1 = i0 + bs_x;
|
|
1168
|
+
if (i1 > nx) {
|
|
1169
|
+
i1 = nx;
|
|
1170
|
+
}
|
|
1171
|
+
|
|
1172
|
+
res.begin_multiple(i0, i1);
|
|
1173
|
+
|
|
1174
|
+
for (size_t j0 = 0; j0 < ny; j0 += bs_y) {
|
|
1175
|
+
size_t j1 = j0 + bs_y;
|
|
1176
|
+
if (j1 > ny) {
|
|
1177
|
+
j1 = ny;
|
|
1178
|
+
}
|
|
1179
|
+
/* compute the actual dot products */
|
|
1180
|
+
{
|
|
1181
|
+
float one = 1, zero = 0;
|
|
1182
|
+
FINTEGER nyi = j1 - j0, nxi = i1 - i0, di = d;
|
|
1183
|
+
sgemm_("Transpose",
|
|
1184
|
+
"Not transpose",
|
|
1185
|
+
&nyi,
|
|
1186
|
+
&nxi,
|
|
1187
|
+
&di,
|
|
1188
|
+
&one,
|
|
1189
|
+
y + j0 * d,
|
|
1190
|
+
&di,
|
|
1191
|
+
x + i0 * d,
|
|
1192
|
+
&di,
|
|
1193
|
+
&zero,
|
|
1194
|
+
ip_block.get(),
|
|
1195
|
+
&nyi);
|
|
1196
|
+
}
|
|
1197
|
+
#pragma omp parallel for schedule(static) if ((i1 - i0) >= 16)
|
|
1198
|
+
for (int64_t i = static_cast<int64_t>(i0);
|
|
1199
|
+
i < static_cast<int64_t>(i1);
|
|
1200
|
+
i++) {
|
|
1201
|
+
float* ip_line = ip_block.get() + (i - i0) * (j1 - j0);
|
|
1202
|
+
|
|
1203
|
+
_mm_prefetch((const char*)ip_line, _MM_HINT_NTA);
|
|
1204
|
+
_mm_prefetch((const char*)(ip_line + 16), _MM_HINT_NTA);
|
|
1205
|
+
|
|
1206
|
+
// constant
|
|
1207
|
+
const __m512 mul_minus2 = _mm512_set1_ps(-2);
|
|
1208
|
+
|
|
1209
|
+
// Track 16 min distances + 16 min indices.
|
|
1210
|
+
// All the distances tracked do not take x_norms[i]
|
|
1211
|
+
// into account in order to get rid of extra
|
|
1212
|
+
// _mm512_add_ps(x_norms[i], ...) instructions
|
|
1213
|
+
// in distance computations.
|
|
1214
|
+
__m512 min_distances =
|
|
1215
|
+
_mm512_set1_ps(res.dis_tab[i] - x_norms[i]);
|
|
1216
|
+
|
|
1217
|
+
// these indices are local and are relative to j0.
|
|
1218
|
+
// so, value 0 means j0.
|
|
1219
|
+
__m512i min_indices = _mm512_set1_epi32(0);
|
|
1220
|
+
|
|
1221
|
+
__m512i current_indices = _mm512_setr_epi32(
|
|
1222
|
+
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15);
|
|
1223
|
+
const __m512i indices_delta = _mm512_set1_epi32(16);
|
|
1224
|
+
|
|
1225
|
+
// current j index
|
|
1226
|
+
size_t idx_j = 0;
|
|
1227
|
+
size_t count = j1 - j0;
|
|
1228
|
+
|
|
1229
|
+
// process 32 elements per loop
|
|
1230
|
+
for (; idx_j < (count / 32) * 32; idx_j += 32, ip_line += 32) {
|
|
1231
|
+
_mm_prefetch((const char*)(ip_line + 32), _MM_HINT_NTA);
|
|
1232
|
+
_mm_prefetch((const char*)(ip_line + 48), _MM_HINT_NTA);
|
|
1233
|
+
|
|
1234
|
+
// load values for norms
|
|
1235
|
+
const __m512 y_norm_0 =
|
|
1236
|
+
_mm512_loadu_ps(y_norms + idx_j + j0 + 0);
|
|
1237
|
+
const __m512 y_norm_1 =
|
|
1238
|
+
_mm512_loadu_ps(y_norms + idx_j + j0 + 16);
|
|
1239
|
+
|
|
1240
|
+
// load values for dot products
|
|
1241
|
+
const __m512 ip_0 = _mm512_loadu_ps(ip_line + 0);
|
|
1242
|
+
const __m512 ip_1 = _mm512_loadu_ps(ip_line + 16);
|
|
1243
|
+
|
|
1244
|
+
// compute dis = y_norm[j] - 2 * dot(x_norm[i], y_norm[j]).
|
|
1245
|
+
// x_norm[i] was dropped off because it is a constant for a
|
|
1246
|
+
// given i.
|
|
1247
|
+
__m512 distances_0 =
|
|
1248
|
+
_mm512_fmadd_ps(ip_0, mul_minus2, y_norm_0);
|
|
1249
|
+
__m512 distances_1 =
|
|
1250
|
+
_mm512_fmadd_ps(ip_1, mul_minus2, y_norm_1);
|
|
1251
|
+
|
|
1252
|
+
// compare the new distances to the min distances
|
|
1253
|
+
// for each of the first group of 16 AVX512 components.
|
|
1254
|
+
const __mmask16 comparison_0 = _mm512_cmp_ps_mask(
|
|
1255
|
+
min_distances, distances_0, _CMP_LE_OS);
|
|
1256
|
+
|
|
1257
|
+
// update min distances and indices with closest vectors if
|
|
1258
|
+
// needed.
|
|
1259
|
+
min_distances = _mm512_mask_blend_ps(
|
|
1260
|
+
comparison_0, distances_0, min_distances);
|
|
1261
|
+
min_indices = _mm512_mask_blend_epi32(
|
|
1262
|
+
comparison_0, current_indices, min_indices);
|
|
1263
|
+
current_indices =
|
|
1264
|
+
_mm512_add_epi32(current_indices, indices_delta);
|
|
1265
|
+
|
|
1266
|
+
// compare the new distances to the min distances
|
|
1267
|
+
// for each of the second group of 16 AVX512 components.
|
|
1268
|
+
const __mmask16 comparison_1 = _mm512_cmp_ps_mask(
|
|
1269
|
+
min_distances, distances_1, _CMP_LE_OS);
|
|
1270
|
+
|
|
1271
|
+
// update min distances and indices with closest vectors if
|
|
1272
|
+
// needed.
|
|
1273
|
+
min_distances = _mm512_mask_blend_ps(
|
|
1274
|
+
comparison_1, distances_1, min_distances);
|
|
1275
|
+
min_indices = _mm512_mask_blend_epi32(
|
|
1276
|
+
comparison_1, current_indices, min_indices);
|
|
1277
|
+
current_indices =
|
|
1278
|
+
_mm512_add_epi32(current_indices, indices_delta);
|
|
1279
|
+
}
|
|
1280
|
+
|
|
1281
|
+
// dump values and find the minimum distance / minimum index
|
|
1282
|
+
float min_distances_scalar[16];
|
|
1283
|
+
uint32_t min_indices_scalar[16];
|
|
1284
|
+
_mm512_storeu_ps(min_distances_scalar, min_distances);
|
|
1285
|
+
_mm512_storeu_si512(
|
|
1286
|
+
(__m512i*)(min_indices_scalar), min_indices);
|
|
1287
|
+
|
|
1288
|
+
float current_min_distance = res.dis_tab[i];
|
|
1289
|
+
uint32_t current_min_index = res.ids_tab[i];
|
|
1290
|
+
|
|
1291
|
+
// if two indices are represented with equal distance values,
|
|
1292
|
+
// then the index with the min value is returned.
|
|
1293
|
+
for (size_t jv = 0; jv < 16; jv++) {
|
|
1294
|
+
// add missing x_norms[i]
|
|
1295
|
+
float distance_candidate =
|
|
1296
|
+
min_distances_scalar[jv] + x_norms[i];
|
|
1297
|
+
|
|
1298
|
+
// negative values can occur for identical vectors
|
|
1299
|
+
// due to roundoff errors.
|
|
1300
|
+
if (distance_candidate < 0) {
|
|
1301
|
+
distance_candidate = 0;
|
|
1302
|
+
}
|
|
1303
|
+
|
|
1304
|
+
int64_t index_candidate = min_indices_scalar[jv] + j0;
|
|
1305
|
+
|
|
1306
|
+
if (current_min_distance > distance_candidate) {
|
|
1307
|
+
current_min_distance = distance_candidate;
|
|
1308
|
+
current_min_index = index_candidate;
|
|
1309
|
+
} else if (
|
|
1310
|
+
current_min_distance == distance_candidate &&
|
|
1311
|
+
current_min_index > index_candidate) {
|
|
1312
|
+
current_min_index = index_candidate;
|
|
1313
|
+
}
|
|
1314
|
+
}
|
|
1315
|
+
|
|
1316
|
+
// process leftovers
|
|
1317
|
+
for (; idx_j < count; idx_j++, ip_line++) {
|
|
1318
|
+
float ip = *ip_line;
|
|
1319
|
+
float dis = x_norms[i] + y_norms[idx_j + j0] - 2 * ip;
|
|
1320
|
+
// negative values can occur for identical vectors
|
|
1321
|
+
// due to roundoff errors.
|
|
1322
|
+
if (dis < 0) {
|
|
1323
|
+
dis = 0;
|
|
1324
|
+
}
|
|
1325
|
+
|
|
1326
|
+
if (current_min_distance > dis) {
|
|
1327
|
+
current_min_distance = dis;
|
|
1328
|
+
current_min_index = idx_j + j0;
|
|
1329
|
+
}
|
|
1330
|
+
}
|
|
1331
|
+
|
|
1332
|
+
//
|
|
1333
|
+
res.add_result(i, current_min_distance, current_min_index);
|
|
1334
|
+
}
|
|
1335
|
+
}
|
|
1336
|
+
// Does nothing for SingleBestResultHandler, but
|
|
1337
|
+
// keeping the call for the consistency.
|
|
1338
|
+
res.end_multiple();
|
|
1339
|
+
InterruptCallback::check();
|
|
1340
|
+
}
|
|
1341
|
+
}
|
|
1342
|
+
|
|
1095
1343
|
} // namespace faiss
|