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
|
@@ -28,6 +28,14 @@ namespace scalar_quantizer {
|
|
|
28
28
|
// Define SL as alias for THE_LEVEL_TO_DISPATCH for use in this file
|
|
29
29
|
constexpr SIMDLevel SL = THE_LEVEL_TO_DISPATCH;
|
|
30
30
|
|
|
31
|
+
// The SPR scalar-quantizer implementation uses VPOPCNTDQ for its RaBitQ
|
|
32
|
+
// popcount helpers, but those helpers belong to the narrower VPOPCNT
|
|
33
|
+
// capability. Keep the SQ implementation at AVX512_SPR while explicitly
|
|
34
|
+
// reusing the independently dispatched VPOPCNT kernels.
|
|
35
|
+
template <SIMDLevel SL0>
|
|
36
|
+
inline constexpr SIMDLevel rabitq_popcount_level =
|
|
37
|
+
SL0 == SIMDLevel::AVX512_SPR ? SIMDLevel::AVX512_VPOPCNT : SL0;
|
|
38
|
+
|
|
31
39
|
/*******************************************************************
|
|
32
40
|
* TurboQuant SIMD kernel: masked_sum
|
|
33
41
|
* Compute sum of arr[j] where bit j of the bitmask is set.
|
|
@@ -259,9 +267,11 @@ struct DCTurboQuantFull : ScalarQuantizer::TurboQuantRefine::DistanceComputer {
|
|
|
259
267
|
if (qb > 0) {
|
|
260
268
|
// Integer popcount path for 1-bit MSE
|
|
261
269
|
size_t byte_size = (d + 7) / 8;
|
|
262
|
-
uint64_t and_result = rabitq::bitwise_and_dot_product<
|
|
270
|
+
uint64_t and_result = rabitq::bitwise_and_dot_product<
|
|
271
|
+
rabitq_popcount_level<SL2>>(
|
|
263
272
|
rearranged_q.data(), code, byte_size, qb);
|
|
264
|
-
uint64_t pop = rabitq::popcount<SL2
|
|
273
|
+
uint64_t pop = rabitq::popcount<rabitq_popcount_level<SL2>>(
|
|
274
|
+
code, byte_size);
|
|
265
275
|
mse_dot = mse_base +
|
|
266
276
|
mse_int_scale * static_cast<float>(and_result) +
|
|
267
277
|
mse_popcnt_scale * static_cast<float>(pop);
|
|
@@ -316,9 +326,11 @@ struct DCTurboQuantFull : ScalarQuantizer::TurboQuantRefine::DistanceComputer {
|
|
|
316
326
|
float qjl_dot;
|
|
317
327
|
if (qb > 0 && int_qjl) {
|
|
318
328
|
size_t byte_size = (d + 7) / 8;
|
|
319
|
-
uint64_t and_result =
|
|
320
|
-
|
|
321
|
-
|
|
329
|
+
uint64_t and_result =
|
|
330
|
+
rabitq::bitwise_and_dot_product<rabitq_popcount_level<SL2>>(
|
|
331
|
+
rearranged_qproj.data(), qjl_code, byte_size, qb);
|
|
332
|
+
uint64_t pop = rabitq::popcount<rabitq_popcount_level<SL2>>(
|
|
333
|
+
qjl_code, byte_size);
|
|
322
334
|
float pos_sum = qjl_popcnt_scale * static_cast<float>(pop) +
|
|
323
335
|
qjl_int_scale * static_cast<float>(and_result);
|
|
324
336
|
qjl_dot = qjl_coeff * gamma * (2.0f * pos_sum - total_qproj_sum);
|
|
@@ -342,10 +354,17 @@ struct DCTurboQuantFull : ScalarQuantizer::TurboQuantRefine::DistanceComputer {
|
|
|
342
354
|
}
|
|
343
355
|
};
|
|
344
356
|
|
|
357
|
+
// True for every level that runs the 512-bit kernels. AVX512_VPOPCNT is
|
|
358
|
+
// unreachable here until an SQ translation unit is compiled at that level;
|
|
359
|
+
// naming it keeps the 512-bit alignment rule correct when one is.
|
|
360
|
+
template <SIMDLevel SL2>
|
|
361
|
+
constexpr bool is_avx512_family = SL2 == SIMDLevel::AVX512 ||
|
|
362
|
+
SL2 == SIMDLevel::AVX512_VPOPCNT || SL2 == SIMDLevel::AVX512_SPR;
|
|
363
|
+
|
|
345
364
|
// Returns true if dimension d is compatible with the given SIMD level
|
|
346
365
|
template <SIMDLevel SL2>
|
|
347
366
|
constexpr bool is_dimension_compatible(size_t d) {
|
|
348
|
-
if constexpr (SL2
|
|
367
|
+
if constexpr (is_avx512_family<SL2>) {
|
|
349
368
|
return d % 16 == 0;
|
|
350
369
|
} else if constexpr (SL2 == SIMDLevel::AVX2 || SL2 == SIMDLevel::ARM_NEON) {
|
|
351
370
|
return d % 8 == 0;
|
|
@@ -416,6 +435,34 @@ ScalarQuantizer::SQuantizer* sq_select_quantizer<THE_LEVEL_TO_DISPATCH>(
|
|
|
416
435
|
return new QuantizerTurboQuantMSE<4, SL>(d, trained);
|
|
417
436
|
case ScalarQuantizer::QT_8bit_tqmse:
|
|
418
437
|
return new QuantizerTurboQuantMSE<8, SL>(d, trained);
|
|
438
|
+
case ScalarQuantizer::QT_1bit_eden:
|
|
439
|
+
return new QuantizerLloydMax<1, SL>(d, trained);
|
|
440
|
+
case ScalarQuantizer::QT_2bit_eden:
|
|
441
|
+
return new QuantizerLloydMax<2, SL>(d, trained);
|
|
442
|
+
case ScalarQuantizer::QT_3bit_eden:
|
|
443
|
+
return new QuantizerLloydMax<3, SL>(d, trained);
|
|
444
|
+
case ScalarQuantizer::QT_4bit_eden:
|
|
445
|
+
return new QuantizerLloydMax<4, SL>(d, trained);
|
|
446
|
+
case ScalarQuantizer::QT_5bit_eden:
|
|
447
|
+
if constexpr (SL != SIMDLevel::NONE) {
|
|
448
|
+
return nullptr;
|
|
449
|
+
} else {
|
|
450
|
+
return new QuantizerLloydMax<5, SL>(d, trained);
|
|
451
|
+
}
|
|
452
|
+
case ScalarQuantizer::QT_6bit_eden:
|
|
453
|
+
if constexpr (SL != SIMDLevel::NONE) {
|
|
454
|
+
return nullptr;
|
|
455
|
+
} else {
|
|
456
|
+
return new QuantizerLloydMax<6, SL>(d, trained);
|
|
457
|
+
}
|
|
458
|
+
case ScalarQuantizer::QT_7bit_eden:
|
|
459
|
+
if constexpr (SL != SIMDLevel::NONE) {
|
|
460
|
+
return nullptr;
|
|
461
|
+
} else {
|
|
462
|
+
return new QuantizerLloydMax<7, SL>(d, trained);
|
|
463
|
+
}
|
|
464
|
+
case ScalarQuantizer::QT_8bit_eden:
|
|
465
|
+
return new QuantizerLloydMax<8, SL>(d, trained);
|
|
419
466
|
case ScalarQuantizer::QT_2bit_tq:
|
|
420
467
|
return new QuantizerTurboQuantFull<2, SL>(d, trained);
|
|
421
468
|
case ScalarQuantizer::QT_3bit_tq:
|
|
@@ -497,13 +544,13 @@ SQDistanceComputer* select_distance_computer_body(
|
|
|
497
544
|
return new DCTemplate<QuantizerBF16<SL2>, Sim, SL2>(d, trained);
|
|
498
545
|
|
|
499
546
|
case ScalarQuantizer::QT_8bit_direct:
|
|
500
|
-
if constexpr (
|
|
501
|
-
SL2 == SIMDLevel::AVX512 || SL2 == SIMDLevel::AVX512_SPR) {
|
|
547
|
+
if constexpr (is_avx512_family<SL2>) {
|
|
502
548
|
if (d % 32 == 0) {
|
|
503
549
|
return new DistanceComputerByte<Sim, SL2>(
|
|
504
550
|
static_cast<int>(d), trained);
|
|
505
551
|
}
|
|
506
|
-
} else if constexpr (
|
|
552
|
+
} else if constexpr (
|
|
553
|
+
SL2 == SIMDLevel::AVX2 || SL2 == SIMDLevel::ARM_NEON) {
|
|
507
554
|
if (d % 16 == 0) {
|
|
508
555
|
return new DistanceComputerByte<Sim, SL2>(
|
|
509
556
|
static_cast<int>(d), trained);
|
|
@@ -518,6 +565,17 @@ SQDistanceComputer* select_distance_computer_body(
|
|
|
518
565
|
return new DistanceComputerByteSigned<Sim, SL2>(
|
|
519
566
|
static_cast<int>(d), trained);
|
|
520
567
|
}
|
|
568
|
+
} else if constexpr (is_avx512_family<SL2>) {
|
|
569
|
+
if (d % 32 == 0) {
|
|
570
|
+
return new DistanceComputerByteSigned<Sim, SL2>(
|
|
571
|
+
static_cast<int>(d), trained);
|
|
572
|
+
}
|
|
573
|
+
} else if constexpr (
|
|
574
|
+
SL2 == SIMDLevel::AVX2 || SL2 == SIMDLevel::ARM_NEON) {
|
|
575
|
+
if (d % 16 == 0) {
|
|
576
|
+
return new DistanceComputerByteSigned<Sim, SL2>(
|
|
577
|
+
static_cast<int>(d), trained);
|
|
578
|
+
}
|
|
521
579
|
}
|
|
522
580
|
return new DCTemplate<Quantizer8bitDirectSigned<SL2>, Sim, SL2>(
|
|
523
581
|
d, trained);
|
|
@@ -539,6 +597,42 @@ SQDistanceComputer* select_distance_computer_body(
|
|
|
539
597
|
case ScalarQuantizer::QT_8bit_tqmse:
|
|
540
598
|
return new DCTemplate<QuantizerTurboQuantMSE<8, SL2>, Sim, SL2>(
|
|
541
599
|
d, trained);
|
|
600
|
+
case ScalarQuantizer::QT_1bit_eden:
|
|
601
|
+
return new DCTemplate<QuantizerLloydMax<1, SL2>, Sim, SL2>(
|
|
602
|
+
d, trained);
|
|
603
|
+
case ScalarQuantizer::QT_2bit_eden:
|
|
604
|
+
return new DCTemplate<QuantizerLloydMax<2, SL2>, Sim, SL2>(
|
|
605
|
+
d, trained);
|
|
606
|
+
case ScalarQuantizer::QT_3bit_eden:
|
|
607
|
+
return new DCTemplate<QuantizerLloydMax<3, SL2>, Sim, SL2>(
|
|
608
|
+
d, trained);
|
|
609
|
+
case ScalarQuantizer::QT_4bit_eden:
|
|
610
|
+
return new DCTemplate<QuantizerLloydMax<4, SL2>, Sim, SL2>(
|
|
611
|
+
d, trained);
|
|
612
|
+
case ScalarQuantizer::QT_5bit_eden:
|
|
613
|
+
if constexpr (SL2 != SIMDLevel::NONE) {
|
|
614
|
+
return nullptr;
|
|
615
|
+
} else {
|
|
616
|
+
return new DCTemplate<QuantizerLloydMax<5, SL2>, Sim, SL2>(
|
|
617
|
+
d, trained);
|
|
618
|
+
}
|
|
619
|
+
case ScalarQuantizer::QT_6bit_eden:
|
|
620
|
+
if constexpr (SL2 != SIMDLevel::NONE) {
|
|
621
|
+
return nullptr;
|
|
622
|
+
} else {
|
|
623
|
+
return new DCTemplate<QuantizerLloydMax<6, SL2>, Sim, SL2>(
|
|
624
|
+
d, trained);
|
|
625
|
+
}
|
|
626
|
+
case ScalarQuantizer::QT_7bit_eden:
|
|
627
|
+
if constexpr (SL2 != SIMDLevel::NONE) {
|
|
628
|
+
return nullptr;
|
|
629
|
+
} else {
|
|
630
|
+
return new DCTemplate<QuantizerLloydMax<7, SL2>, Sim, SL2>(
|
|
631
|
+
d, trained);
|
|
632
|
+
}
|
|
633
|
+
case ScalarQuantizer::QT_8bit_eden:
|
|
634
|
+
return new DCTemplate<QuantizerLloydMax<8, SL2>, Sim, SL2>(
|
|
635
|
+
d, trained);
|
|
542
636
|
case ScalarQuantizer::QT_2bit_tq:
|
|
543
637
|
// FRICTION: bypasses DCTemplate entirely — custom DC
|
|
544
638
|
// that doesn't fit the Quantizer+Similarity decomposition
|
|
@@ -663,14 +757,13 @@ InvertedListScanner* sq_select_InvertedListScanner<THE_LEVEL_TO_DISPATCH>(
|
|
|
663
757
|
return scan.template
|
|
664
758
|
operator()<DCTemplate<QuantizerBF16<SL2>, Similarity, SL2>>();
|
|
665
759
|
case ScalarQuantizer::QT_8bit_direct:
|
|
666
|
-
if constexpr (
|
|
667
|
-
SL2 == SIMDLevel::AVX512 ||
|
|
668
|
-
SL2 == SIMDLevel::AVX512_SPR) {
|
|
760
|
+
if constexpr (is_avx512_family<SL2>) {
|
|
669
761
|
if (d % 32 == 0) {
|
|
670
762
|
return scan.template
|
|
671
763
|
operator()<DistanceComputerByte<Similarity, SL2>>();
|
|
672
764
|
}
|
|
673
|
-
} else if constexpr (
|
|
765
|
+
} else if constexpr (
|
|
766
|
+
SL2 == SIMDLevel::AVX2 || SL2 == SIMDLevel::ARM_NEON) {
|
|
674
767
|
if (d % 16 == 0) {
|
|
675
768
|
return scan.template
|
|
676
769
|
operator()<DistanceComputerByte<Similarity, SL2>>();
|
|
@@ -686,6 +779,17 @@ InvertedListScanner* sq_select_InvertedListScanner<THE_LEVEL_TO_DISPATCH>(
|
|
|
686
779
|
return scan.template operator()<
|
|
687
780
|
DistanceComputerByteSigned<Similarity, SL2>>();
|
|
688
781
|
}
|
|
782
|
+
} else if constexpr (is_avx512_family<SL2>) {
|
|
783
|
+
if (d % 32 == 0) {
|
|
784
|
+
return scan.template operator()<
|
|
785
|
+
DistanceComputerByteSigned<Similarity, SL2>>();
|
|
786
|
+
}
|
|
787
|
+
} else if constexpr (
|
|
788
|
+
SL2 == SIMDLevel::AVX2 || SL2 == SIMDLevel::ARM_NEON) {
|
|
789
|
+
if (d % 16 == 0) {
|
|
790
|
+
return scan.template operator()<
|
|
791
|
+
DistanceComputerByteSigned<Similarity, SL2>>();
|
|
792
|
+
}
|
|
689
793
|
}
|
|
690
794
|
return scan.template operator()<DCTemplate<
|
|
691
795
|
Quantizer8bitDirectSigned<SL2>,
|
|
@@ -719,6 +823,58 @@ InvertedListScanner* sq_select_InvertedListScanner<THE_LEVEL_TO_DISPATCH>(
|
|
|
719
823
|
QuantizerTurboQuantMSE<8, SL2>,
|
|
720
824
|
Similarity,
|
|
721
825
|
SL2>>();
|
|
826
|
+
case ScalarQuantizer::QT_1bit_eden:
|
|
827
|
+
return scan.template operator()<DCTemplate<
|
|
828
|
+
QuantizerLloydMax<1, SL2>,
|
|
829
|
+
Similarity,
|
|
830
|
+
SL2>>();
|
|
831
|
+
case ScalarQuantizer::QT_2bit_eden:
|
|
832
|
+
return scan.template operator()<DCTemplate<
|
|
833
|
+
QuantizerLloydMax<2, SL2>,
|
|
834
|
+
Similarity,
|
|
835
|
+
SL2>>();
|
|
836
|
+
case ScalarQuantizer::QT_3bit_eden:
|
|
837
|
+
return scan.template operator()<DCTemplate<
|
|
838
|
+
QuantizerLloydMax<3, SL2>,
|
|
839
|
+
Similarity,
|
|
840
|
+
SL2>>();
|
|
841
|
+
case ScalarQuantizer::QT_4bit_eden:
|
|
842
|
+
return scan.template operator()<DCTemplate<
|
|
843
|
+
QuantizerLloydMax<4, SL2>,
|
|
844
|
+
Similarity,
|
|
845
|
+
SL2>>();
|
|
846
|
+
case ScalarQuantizer::QT_5bit_eden:
|
|
847
|
+
if constexpr (SL2 != SIMDLevel::NONE) {
|
|
848
|
+
return nullptr;
|
|
849
|
+
} else {
|
|
850
|
+
return scan.template operator()<DCTemplate<
|
|
851
|
+
QuantizerLloydMax<5, SL2>,
|
|
852
|
+
Similarity,
|
|
853
|
+
SL2>>();
|
|
854
|
+
}
|
|
855
|
+
case ScalarQuantizer::QT_6bit_eden:
|
|
856
|
+
if constexpr (SL2 != SIMDLevel::NONE) {
|
|
857
|
+
return nullptr;
|
|
858
|
+
} else {
|
|
859
|
+
return scan.template operator()<DCTemplate<
|
|
860
|
+
QuantizerLloydMax<6, SL2>,
|
|
861
|
+
Similarity,
|
|
862
|
+
SL2>>();
|
|
863
|
+
}
|
|
864
|
+
case ScalarQuantizer::QT_7bit_eden:
|
|
865
|
+
if constexpr (SL2 != SIMDLevel::NONE) {
|
|
866
|
+
return nullptr;
|
|
867
|
+
} else {
|
|
868
|
+
return scan.template operator()<DCTemplate<
|
|
869
|
+
QuantizerLloydMax<7, SL2>,
|
|
870
|
+
Similarity,
|
|
871
|
+
SL2>>();
|
|
872
|
+
}
|
|
873
|
+
case ScalarQuantizer::QT_8bit_eden:
|
|
874
|
+
return scan.template operator()<DCTemplate<
|
|
875
|
+
QuantizerLloydMax<8, SL2>,
|
|
876
|
+
Similarity,
|
|
877
|
+
SL2>>();
|
|
722
878
|
case ScalarQuantizer::QT_2bit_tq:
|
|
723
879
|
return scan.template
|
|
724
880
|
operator()<DCTurboQuantFull<2, Similarity, SL2>>();
|
|
@@ -223,18 +223,18 @@ struct QuantizerTemplate<
|
|
|
223
223
|
};
|
|
224
224
|
|
|
225
225
|
/**********************************************************
|
|
226
|
-
*
|
|
226
|
+
* Lloyd-Max scalar quantizer
|
|
227
227
|
**********************************************************/
|
|
228
228
|
|
|
229
|
-
// NEON
|
|
229
|
+
// NEON Lloyd-Max: decode via gather, encode stays scalar.
|
|
230
230
|
// NEON doesn't have movemask so 1-bit encode is also scalar.
|
|
231
|
-
#define
|
|
231
|
+
#define DEFINE_LLOYD_MAX_NEON_SPECIALIZATION(NBITS, UNPACK_FN) \
|
|
232
232
|
template <> \
|
|
233
|
-
struct
|
|
234
|
-
:
|
|
235
|
-
using Base =
|
|
233
|
+
struct QuantizerLloydMax<NBITS, SIMDLevel::ARM_NEON> \
|
|
234
|
+
: QuantizerLloydMax<NBITS, SIMDLevel::NONE> { \
|
|
235
|
+
using Base = QuantizerLloydMax<NBITS, SIMDLevel::NONE>; \
|
|
236
236
|
\
|
|
237
|
-
|
|
237
|
+
QuantizerLloydMax(size_t d, const std::vector<float>& trained) \
|
|
238
238
|
: Base(d, trained) { \
|
|
239
239
|
assert(d % 8 == 0); \
|
|
240
240
|
} \
|
|
@@ -256,19 +256,19 @@ struct QuantizerTemplate<
|
|
|
256
256
|
} \
|
|
257
257
|
}
|
|
258
258
|
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
259
|
+
DEFINE_LLOYD_MAX_NEON_SPECIALIZATION(1, unpack_8x1bit_to_u8);
|
|
260
|
+
DEFINE_LLOYD_MAX_NEON_SPECIALIZATION(2, unpack_8x2bit_to_u8);
|
|
261
|
+
DEFINE_LLOYD_MAX_NEON_SPECIALIZATION(3, unpack_8x3bit_to_u8);
|
|
262
|
+
DEFINE_LLOYD_MAX_NEON_SPECIALIZATION(4, unpack_8x4bit_to_u8);
|
|
263
263
|
|
|
264
|
-
#undef
|
|
264
|
+
#undef DEFINE_LLOYD_MAX_NEON_SPECIALIZATION
|
|
265
265
|
|
|
266
266
|
template <>
|
|
267
|
-
struct
|
|
268
|
-
:
|
|
269
|
-
using Base =
|
|
267
|
+
struct QuantizerLloydMax<8, SIMDLevel::ARM_NEON>
|
|
268
|
+
: QuantizerLloydMax<8, SIMDLevel::NONE> {
|
|
269
|
+
using Base = QuantizerLloydMax<8, SIMDLevel::NONE>;
|
|
270
270
|
|
|
271
|
-
|
|
271
|
+
QuantizerLloydMax(size_t d, const std::vector<float>& trained)
|
|
272
272
|
: Base(d, trained) {
|
|
273
273
|
assert(d % 8 == 0);
|
|
274
274
|
}
|
|
@@ -618,6 +618,64 @@ struct DCTemplate<Quantizer, Similarity, SIMDLevel::ARM_NEON>
|
|
|
618
618
|
}
|
|
619
619
|
};
|
|
620
620
|
|
|
621
|
+
// Byte-domain kernels for QT_8bit_direct{,_signed}. The dispatch only
|
|
622
|
+
// selects them when d % 16 == 0, so no loop needs a tail.
|
|
623
|
+
|
|
624
|
+
namespace {
|
|
625
|
+
|
|
626
|
+
// The accumulator stays unsigned: a vmull_u8 square reaches 255*255 = 65025,
|
|
627
|
+
// which an int16 lane would read as negative.
|
|
628
|
+
FAISS_ALWAYS_INLINE int neon_byte_l2sqr(
|
|
629
|
+
const uint8_t* code1,
|
|
630
|
+
const uint8_t* code2,
|
|
631
|
+
int d) {
|
|
632
|
+
uint32x4_t accu = vdupq_n_u32(0);
|
|
633
|
+
for (int i = 0; i < d; i += 16) {
|
|
634
|
+
const uint8x16_t diff =
|
|
635
|
+
vabdq_u8(vld1q_u8(code1 + i), vld1q_u8(code2 + i));
|
|
636
|
+
accu = vpadalq_u16(
|
|
637
|
+
accu, vmull_u8(vget_low_u8(diff), vget_low_u8(diff)));
|
|
638
|
+
accu = vpadalq_u16(
|
|
639
|
+
accu, vmull_u8(vget_high_u8(diff), vget_high_u8(diff)));
|
|
640
|
+
}
|
|
641
|
+
return static_cast<int>(vaddvq_u32(accu));
|
|
642
|
+
}
|
|
643
|
+
|
|
644
|
+
FAISS_ALWAYS_INLINE int neon_byte_ip(
|
|
645
|
+
const uint8_t* code1,
|
|
646
|
+
const uint8_t* code2,
|
|
647
|
+
int d) {
|
|
648
|
+
uint32x4_t accu = vdupq_n_u32(0);
|
|
649
|
+
for (int i = 0; i < d; i += 16) {
|
|
650
|
+
const uint8x16_t c1 = vld1q_u8(code1 + i);
|
|
651
|
+
const uint8x16_t c2 = vld1q_u8(code2 + i);
|
|
652
|
+
accu = vpadalq_u16(accu, vmull_u8(vget_low_u8(c1), vget_low_u8(c2)));
|
|
653
|
+
accu = vpadalq_u16(accu, vmull_u8(vget_high_u8(c1), vget_high_u8(c2)));
|
|
654
|
+
}
|
|
655
|
+
return static_cast<int>(vaddvq_u32(accu));
|
|
656
|
+
}
|
|
657
|
+
|
|
658
|
+
// The codes store value + 128. For x in 0 to 255, x ^ 0x80 read as int8 is
|
|
659
|
+
// exactly x - 128, which is how the bias comes off before vmull_s8.
|
|
660
|
+
FAISS_ALWAYS_INLINE int neon_byte_ip_unbias(
|
|
661
|
+
const uint8_t* code1,
|
|
662
|
+
const uint8_t* code2,
|
|
663
|
+
int d) {
|
|
664
|
+
const uint8x16_t bias = vdupq_n_u8(0x80);
|
|
665
|
+
int32x4_t accu = vdupq_n_s32(0);
|
|
666
|
+
for (int i = 0; i < d; i += 16) {
|
|
667
|
+
const int8x16_t c1 =
|
|
668
|
+
vreinterpretq_s8_u8(veorq_u8(vld1q_u8(code1 + i), bias));
|
|
669
|
+
const int8x16_t c2 =
|
|
670
|
+
vreinterpretq_s8_u8(veorq_u8(vld1q_u8(code2 + i), bias));
|
|
671
|
+
accu = vpadalq_s16(accu, vmull_s8(vget_low_s8(c1), vget_low_s8(c2)));
|
|
672
|
+
accu = vpadalq_s16(accu, vmull_s8(vget_high_s8(c1), vget_high_s8(c2)));
|
|
673
|
+
}
|
|
674
|
+
return static_cast<int>(vaddvq_s32(accu));
|
|
675
|
+
}
|
|
676
|
+
|
|
677
|
+
} // namespace
|
|
678
|
+
|
|
621
679
|
template <class Similarity>
|
|
622
680
|
struct DistanceComputerByte<Similarity, SIMDLevel::ARM_NEON>
|
|
623
681
|
: SQDistanceComputer {
|
|
@@ -626,20 +684,17 @@ struct DistanceComputerByte<Similarity, SIMDLevel::ARM_NEON>
|
|
|
626
684
|
int d;
|
|
627
685
|
std::vector<uint8_t> tmp;
|
|
628
686
|
|
|
629
|
-
DistanceComputerByte(int d, const std::vector<float>&) : d(d), tmp(d) {
|
|
687
|
+
DistanceComputerByte(int d, const std::vector<float>&) : d(d), tmp(d) {
|
|
688
|
+
FAISS_THROW_IF_NOT(d % 16 == 0);
|
|
689
|
+
}
|
|
630
690
|
|
|
631
691
|
int compute_code_distance(const uint8_t* code1, const uint8_t* code2)
|
|
632
692
|
const {
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
} else {
|
|
638
|
-
int diff = int(code1[i]) - code2[i];
|
|
639
|
-
accu += diff * diff;
|
|
640
|
-
}
|
|
693
|
+
if constexpr (Sim::metric_type == METRIC_INNER_PRODUCT) {
|
|
694
|
+
return neon_byte_ip(code1, code2, d);
|
|
695
|
+
} else {
|
|
696
|
+
return neon_byte_l2sqr(code1, code2, d);
|
|
641
697
|
}
|
|
642
|
-
return accu;
|
|
643
698
|
}
|
|
644
699
|
|
|
645
700
|
void set_query(const float* x) final {
|
|
@@ -663,6 +718,50 @@ struct DistanceComputerByte<Similarity, SIMDLevel::ARM_NEON>
|
|
|
663
718
|
}
|
|
664
719
|
};
|
|
665
720
|
|
|
721
|
+
template <class Similarity>
|
|
722
|
+
struct DistanceComputerByteSigned<Similarity, SIMDLevel::ARM_NEON>
|
|
723
|
+
: SQDistanceComputer {
|
|
724
|
+
using Sim = Similarity;
|
|
725
|
+
|
|
726
|
+
int d;
|
|
727
|
+
std::vector<uint8_t> tmp;
|
|
728
|
+
|
|
729
|
+
DistanceComputerByteSigned(int d, const std::vector<float>&)
|
|
730
|
+
: d(d), tmp(d) {
|
|
731
|
+
FAISS_THROW_IF_NOT(d % 16 == 0);
|
|
732
|
+
}
|
|
733
|
+
|
|
734
|
+
int compute_code_distance(const uint8_t* code1, const uint8_t* code2)
|
|
735
|
+
const {
|
|
736
|
+
if constexpr (Sim::metric_type == METRIC_INNER_PRODUCT) {
|
|
737
|
+
return neon_byte_ip_unbias(code1, code2, d);
|
|
738
|
+
} else {
|
|
739
|
+
// The bias cancels in the difference.
|
|
740
|
+
return neon_byte_l2sqr(code1, code2, d);
|
|
741
|
+
}
|
|
742
|
+
}
|
|
743
|
+
|
|
744
|
+
void set_query(const float* x) final {
|
|
745
|
+
for (int i = 0; i < d; i++) {
|
|
746
|
+
tmp[i] = uint8_t(int(x[i]) + 128);
|
|
747
|
+
}
|
|
748
|
+
}
|
|
749
|
+
|
|
750
|
+
int compute_distance(const float* x, const uint8_t* code) {
|
|
751
|
+
set_query(x);
|
|
752
|
+
return compute_code_distance(tmp.data(), code);
|
|
753
|
+
}
|
|
754
|
+
|
|
755
|
+
float symmetric_dis(idx_t i, idx_t j) override {
|
|
756
|
+
return compute_code_distance(
|
|
757
|
+
codes + i * code_size, codes + j * code_size);
|
|
758
|
+
}
|
|
759
|
+
|
|
760
|
+
float query_to_code(const uint8_t* code) const final {
|
|
761
|
+
return compute_code_distance(tmp.data(), code);
|
|
762
|
+
}
|
|
763
|
+
};
|
|
764
|
+
|
|
666
765
|
/**********************************************************
|
|
667
766
|
* TurboQuant masked_sum NEON specialization (scalar fallback)
|
|
668
767
|
**********************************************************/
|
|
@@ -32,30 +32,32 @@ constexpr int AVAILABLE_SIMD_LEVELS_NONE = (1 << int(SIMDLevel::NONE));
|
|
|
32
32
|
constexpr int AVAILABLE_SIMD_LEVELS_AVX2_NEON = AVAILABLE_SIMD_LEVELS_NONE |
|
|
33
33
|
(1 << int(SIMDLevel::AVX2)) | (1 << int(SIMDLevel::ARM_NEON));
|
|
34
34
|
|
|
35
|
-
//
|
|
36
|
-
constexpr int
|
|
35
|
+
// BASE: AVX2_NEON + AVX512 + RISCV_RVV
|
|
36
|
+
constexpr int AVAILABLE_SIMD_LEVELS_BASE = AVAILABLE_SIMD_LEVELS_AVX2_NEON |
|
|
37
37
|
(1 << int(SIMDLevel::AVX512)) | (1 << int(SIMDLevel::RISCV_RVV));
|
|
38
38
|
|
|
39
|
-
//
|
|
40
|
-
//
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
AVAILABLE_SIMD_LEVELS_A0 | (1 << int(SIMDLevel::AVX512_SPR));
|
|
39
|
+
// BASE_WITH_VPOPCNT: BASE + AVX512_VPOPCNT, for kernels that need only
|
|
40
|
+
// VPOPCNTDQ on top of baseline AVX-512 (Ice Lake, Zen 4, Zen 5).
|
|
41
|
+
constexpr int AVAILABLE_SIMD_LEVELS_BASE_WITH_VPOPCNT =
|
|
42
|
+
AVAILABLE_SIMD_LEVELS_BASE | (1 << int(SIMDLevel::AVX512_VPOPCNT));
|
|
44
43
|
|
|
45
|
-
//
|
|
46
|
-
|
|
47
|
-
|
|
44
|
+
// BASE_WITH_SPR: BASE + AVX512_SPR, for kernels that need the whole SPR
|
|
45
|
+
// feature set rather than VPOPCNTDQ alone.
|
|
46
|
+
constexpr int AVAILABLE_SIMD_LEVELS_BASE_WITH_SPR =
|
|
47
|
+
AVAILABLE_SIMD_LEVELS_BASE | (1 << int(SIMDLevel::AVX512_SPR));
|
|
48
48
|
|
|
49
|
-
//
|
|
50
|
-
// implementations
|
|
51
|
-
constexpr int
|
|
52
|
-
|
|
49
|
+
// BASE_WITH_SVE: BASE + ARM_SVE, for functions with dedicated SVE
|
|
50
|
+
// implementations.
|
|
51
|
+
constexpr int AVAILABLE_SIMD_LEVELS_BASE_WITH_SVE =
|
|
52
|
+
AVAILABLE_SIMD_LEVELS_BASE | (1 << int(SIMDLevel::ARM_SVE));
|
|
53
53
|
|
|
54
54
|
constexpr int AVAILABLE_SIMD_LEVELS_ALL = -1;
|
|
55
55
|
|
|
56
56
|
constexpr SIMDLevel get_simd_fallback(SIMDLevel level) {
|
|
57
57
|
switch (level) {
|
|
58
58
|
case SIMDLevel::AVX512_SPR:
|
|
59
|
+
return SIMDLevel::AVX512_VPOPCNT;
|
|
60
|
+
case SIMDLevel::AVX512_VPOPCNT:
|
|
59
61
|
return SIMDLevel::AVX512;
|
|
60
62
|
case SIMDLevel::AVX512:
|
|
61
63
|
return SIMDLevel::AVX2;
|
|
@@ -84,6 +86,24 @@ inline auto dispatch_with_fallback(LambdaType&& action) {
|
|
|
84
86
|
}
|
|
85
87
|
}
|
|
86
88
|
|
|
89
|
+
/** Run action at current_level; on a null result, retry the next-lower level,
|
|
90
|
+
* down to NONE (terminal). action is called per level tried, so never moved. */
|
|
91
|
+
template <int available_levels, SIMDLevel current_level, typename LambdaType>
|
|
92
|
+
inline auto dispatch_simd_level_or_lower(LambdaType& action) {
|
|
93
|
+
if constexpr (current_level == SIMDLevel::NONE) {
|
|
94
|
+
return action.template operator()<SIMDLevel::NONE>();
|
|
95
|
+
} else {
|
|
96
|
+
if constexpr (available_levels & (1 << int(current_level))) {
|
|
97
|
+
if (auto result = action.template operator()<current_level>()) {
|
|
98
|
+
return result;
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
return dispatch_simd_level_or_lower<
|
|
102
|
+
available_levels,
|
|
103
|
+
get_simd_fallback(current_level)>(action);
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
|
|
87
107
|
/** The complete dispatching function. It takes into account:
|
|
88
108
|
* - the currently selected SIMD level
|
|
89
109
|
* - the compiled in SIMD levels (given by COMPILE_SIMD_XXX)
|
|
@@ -106,6 +126,15 @@ inline auto with_selected_simd_levels(LambdaType&& action) {
|
|
|
106
126
|
[[fallthrough]];
|
|
107
127
|
#endif
|
|
108
128
|
|
|
129
|
+
#ifdef COMPILE_SIMD_AVX512_VPOPCNT
|
|
130
|
+
case SIMDLevel::AVX512_VPOPCNT:
|
|
131
|
+
if constexpr (
|
|
132
|
+
available_levels & (1 << int(SIMDLevel::AVX512_VPOPCNT))) {
|
|
133
|
+
return action.template operator()<SIMDLevel::AVX512_VPOPCNT>();
|
|
134
|
+
}
|
|
135
|
+
[[fallthrough]];
|
|
136
|
+
#endif
|
|
137
|
+
|
|
109
138
|
#ifdef COMPILE_SIMD_AVX512
|
|
110
139
|
case SIMDLevel::AVX512:
|
|
111
140
|
if constexpr (available_levels & (1 << int(SIMDLevel::AVX512))) {
|
|
@@ -153,7 +182,7 @@ inline auto with_selected_simd_levels(LambdaType&& action) {
|
|
|
153
182
|
// In static mode, SINGLE_SIMD_LEVEL is a constexpr resolved at compile
|
|
154
183
|
// time. We mirror the DD fallthrough behavior at compile time via
|
|
155
184
|
// dispatch_with_fallback, which recursively walks get_simd_fallback:
|
|
156
|
-
// x86: AVX512_SPR -> AVX512 -> AVX2 -> NONE
|
|
185
|
+
// x86: AVX512_SPR -> AVX512_VPOPCNT -> AVX512 -> AVX2 -> NONE
|
|
157
186
|
// ARM: ARM_SVE -> ARM_NEON -> NONE
|
|
158
187
|
// RISCV: RISCV_RVV -> NONE
|
|
159
188
|
// The first level in the chain that appears in available_levels is
|
|
@@ -163,6 +192,18 @@ inline auto with_selected_simd_levels(LambdaType&& action) {
|
|
|
163
192
|
#endif
|
|
164
193
|
}
|
|
165
194
|
|
|
195
|
+
/** Like with_selected_simd_levels, but for factory actions that return null to
|
|
196
|
+
* decline a level (e.g. AVX-512 needing d % 16 == 0). Falls back to the next
|
|
197
|
+
* lower level, down to NONE. */
|
|
198
|
+
template <int available_levels, typename LambdaType>
|
|
199
|
+
inline auto with_simd_level_fallback(const LambdaType& action) {
|
|
200
|
+
return with_selected_simd_levels<available_levels>(
|
|
201
|
+
[&action]<SIMDLevel SL>() {
|
|
202
|
+
return dispatch_simd_level_or_lower<available_levels, SL>(
|
|
203
|
+
action);
|
|
204
|
+
});
|
|
205
|
+
}
|
|
206
|
+
|
|
166
207
|
/**
|
|
167
208
|
* Dispatch to a lambda with SIMDLevel as a compile-time constant.
|
|
168
209
|
*
|
|
@@ -194,16 +235,7 @@ inline auto with_selected_simd_levels(LambdaType&& action) {
|
|
|
194
235
|
*/
|
|
195
236
|
template <typename LambdaType>
|
|
196
237
|
inline auto with_simd_level(LambdaType&& action) {
|
|
197
|
-
return with_selected_simd_levels<
|
|
198
|
-
std::forward<LambdaType>(action));
|
|
199
|
-
}
|
|
200
|
-
|
|
201
|
-
/**
|
|
202
|
-
* Use for functions with AVX512_SPR-specific implementations.
|
|
203
|
-
*/
|
|
204
|
-
template <typename LambdaType>
|
|
205
|
-
inline auto with_simd_level_spr(LambdaType&& action) {
|
|
206
|
-
return with_selected_simd_levels<AVAILABLE_SIMD_LEVELS_A0_SPR>(
|
|
238
|
+
return with_selected_simd_levels<AVAILABLE_SIMD_LEVELS_BASE>(
|
|
207
239
|
std::forward<LambdaType>(action));
|
|
208
240
|
}
|
|
209
241
|
|
|
@@ -217,13 +249,20 @@ inline auto with_simd_level_256bit(LambdaType&& action) {
|
|
|
217
249
|
std::forward<LambdaType>(action));
|
|
218
250
|
}
|
|
219
251
|
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
252
|
+
// Plain with_simd_level() uses BASE, which omits the optional levels below.
|
|
253
|
+
// A call site must opt in, or its specialization is never instantiated.
|
|
254
|
+
|
|
255
|
+
/// BASE + ARM_SVE.
|
|
256
|
+
template <typename LambdaType>
|
|
257
|
+
inline auto with_simd_level_with_sve(LambdaType&& action) {
|
|
258
|
+
return with_selected_simd_levels<AVAILABLE_SIMD_LEVELS_BASE_WITH_SVE>(
|
|
259
|
+
std::forward<LambdaType>(action));
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
/// BASE + AVX512_VPOPCNT.
|
|
224
263
|
template <typename LambdaType>
|
|
225
|
-
inline auto
|
|
226
|
-
return with_selected_simd_levels<
|
|
264
|
+
inline auto with_simd_level_with_vpopcnt(LambdaType&& action) {
|
|
265
|
+
return with_selected_simd_levels<AVAILABLE_SIMD_LEVELS_BASE_WITH_VPOPCNT>(
|
|
227
266
|
std::forward<LambdaType>(action));
|
|
228
267
|
}
|
|
229
268
|
|