faiss 0.6.4 → 0.6.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +4 -0
- data/ext/faiss/extconf.rb +1 -1
- data/lib/faiss/version.rb +1 -1
- data/vendor/faiss/faiss/AutoTune.cpp +175 -0
- data/vendor/faiss/faiss/Clustering.cpp +150 -60
- data/vendor/faiss/faiss/Clustering.h +29 -0
- data/vendor/faiss/faiss/IVFlib.cpp +99 -0
- data/vendor/faiss/faiss/IVFlib.h +15 -0
- data/vendor/faiss/faiss/Index.cpp +44 -0
- data/vendor/faiss/faiss/Index.h +19 -14
- data/vendor/faiss/faiss/IndexBinary.cpp +8 -0
- data/vendor/faiss/faiss/IndexBinary.h +8 -0
- data/vendor/faiss/faiss/IndexBinaryHNSW.cpp +82 -4
- data/vendor/faiss/faiss/IndexBinaryHNSW.h +19 -0
- data/vendor/faiss/faiss/IndexBinaryIVF.cpp +34 -0
- data/vendor/faiss/faiss/IndexFastScan.cpp +33 -17
- data/vendor/faiss/faiss/IndexFastScan.h +6 -3
- data/vendor/faiss/faiss/IndexFlat.cpp +56 -0
- data/vendor/faiss/faiss/IndexFlat.h +31 -0
- data/vendor/faiss/faiss/IndexFlatCodes.cpp +6 -1
- data/vendor/faiss/faiss/IndexHNSW.cpp +456 -10
- data/vendor/faiss/faiss/IndexHNSW.h +38 -0
- data/vendor/faiss/faiss/IndexIDMap.cpp +32 -0
- data/vendor/faiss/faiss/IndexIDMap.h +7 -0
- data/vendor/faiss/faiss/IndexIVF.cpp +281 -31
- data/vendor/faiss/faiss/IndexIVF.h +53 -1
- data/vendor/faiss/faiss/IndexIVFFastScan.cpp +46 -14
- data/vendor/faiss/faiss/IndexIVFFastScan.h +4 -2
- data/vendor/faiss/faiss/IndexIVFFlat.cpp +33 -0
- data/vendor/faiss/faiss/IndexIVFFlat.h +16 -0
- data/vendor/faiss/faiss/IndexIVFPQFastScan.cpp +1 -0
- data/vendor/faiss/faiss/IndexIVFRaBitQ.cpp +3 -20
- data/vendor/faiss/faiss/IndexIVFRaBitQFastScan.cpp +50 -14
- data/vendor/faiss/faiss/IndexIVFRaBitQFastScan.h +5 -2
- data/vendor/faiss/faiss/IndexIVFSQFastScan.cpp +855 -0
- data/vendor/faiss/faiss/IndexIVFSQFastScan.h +145 -0
- data/vendor/faiss/faiss/IndexNNDescent.cpp +0 -3
- data/vendor/faiss/faiss/IndexPiPNN.cpp +201 -0
- data/vendor/faiss/faiss/IndexPiPNN.h +83 -0
- data/vendor/faiss/faiss/IndexRaBitQFastScan.cpp +6 -3
- data/vendor/faiss/faiss/IndexRaBitQFastScan.h +10 -4
- data/vendor/faiss/faiss/IndexReplicas.cpp +20 -0
- data/vendor/faiss/faiss/IndexReplicas.h +9 -0
- data/vendor/faiss/faiss/IndexSQFastScan.cpp +648 -0
- data/vendor/faiss/faiss/IndexSQFastScan.h +129 -0
- data/vendor/faiss/faiss/IndexShards.cpp +10 -0
- data/vendor/faiss/faiss/IndexShards.h +5 -0
- data/vendor/faiss/faiss/SuperKMeans.cpp +57 -15
- data/vendor/faiss/faiss/SuperKMeans.h +9 -4
- data/vendor/faiss/faiss/VectorTransform.cpp +3 -2
- data/vendor/faiss/faiss/clone_index.cpp +24 -0
- data/vendor/faiss/faiss/gpu/Dataset.h +112 -0
- data/vendor/faiss/faiss/gpu/GpuCloner.cpp +16 -13
- data/vendor/faiss/faiss/gpu/GpuCloner.h +4 -0
- data/vendor/faiss/faiss/gpu/GpuClonerOptions.h +3 -0
- data/vendor/faiss/faiss/gpu/GpuIndex.h +4 -13
- data/vendor/faiss/faiss/gpu/GpuIndexBinaryCagra.h +26 -13
- data/vendor/faiss/faiss/gpu/GpuIndexCagra.h +45 -22
- data/vendor/faiss/faiss/gpu/GpuIndexFlat.h +11 -0
- data/vendor/faiss/faiss/gpu/GpuIndexIVF.h +6 -0
- data/vendor/faiss/faiss/gpu/test/TestGpuDatasetHeader.cpp +18 -0
- data/vendor/faiss/faiss/gpu/test/TestGpuIndexFlat.cpp +215 -0
- data/vendor/faiss/faiss/gpu/test/TestGpuIndexIVFFlat.cpp +98 -0
- data/vendor/faiss/faiss/gpu/utils/DeviceUtils.h +11 -0
- data/vendor/faiss/faiss/gpu/utils/StackDeviceMemory.cpp +1 -1
- data/vendor/faiss/faiss/gpu_metal/MetalCloner.h +11 -0
- data/vendor/faiss/faiss/gpu_metal/MetalDistance.h +92 -0
- data/vendor/faiss/faiss/gpu_metal/MetalIndex.h +4 -0
- data/vendor/faiss/faiss/gpu_metal/MetalIndexBinaryFlat.h +69 -0
- data/vendor/faiss/faiss/gpu_metal/MetalIndexIVFPQ.h +38 -0
- data/vendor/faiss/faiss/gpu_metal/MetalKernels.h +106 -0
- data/vendor/faiss/faiss/gpu_metal/MetalPythonBridge.h +10 -0
- data/vendor/faiss/faiss/impl/AuxIndexStructures.cpp +2 -0
- data/vendor/faiss/faiss/impl/AuxIndexStructures.h +1 -6
- data/vendor/faiss/faiss/impl/ClusteringHelpers.cpp +134 -45
- data/vendor/faiss/faiss/impl/ClusteringHelpers.h +36 -0
- data/vendor/faiss/faiss/impl/ClusteringInitialization.cpp +286 -136
- data/vendor/faiss/faiss/impl/DistanceComputer.cpp +63 -0
- data/vendor/faiss/faiss/impl/DistanceComputer.h +3 -29
- data/vendor/faiss/faiss/impl/HNSW.cpp +46 -10
- data/vendor/faiss/faiss/impl/HNSW.h +72 -0
- data/vendor/faiss/faiss/impl/LocalSearchQuantizer.cpp +6 -19
- data/vendor/faiss/faiss/impl/NNDescent.cpp +2 -1
- data/vendor/faiss/faiss/impl/NSG.cpp +3 -2
- data/vendor/faiss/faiss/impl/Panorama.h +3 -2
- data/vendor/faiss/faiss/impl/PiPNN.cpp +822 -0
- data/vendor/faiss/faiss/impl/PiPNN.h +122 -0
- data/vendor/faiss/faiss/impl/ProductQuantizer.cpp +54 -11
- data/vendor/faiss/faiss/impl/RaBitQUtils.h +1 -1
- data/vendor/faiss/faiss/impl/RaBitQuantizer.cpp +81 -57
- data/vendor/faiss/faiss/impl/RaBitQuantizer.h +15 -0
- data/vendor/faiss/faiss/impl/ScalarQuantizer.cpp +29 -0
- data/vendor/faiss/faiss/impl/ScalarQuantizer.h +1 -6
- data/vendor/faiss/faiss/impl/approx_topk/avx2.cpp +0 -4
- data/vendor/faiss/faiss/impl/approx_topk/neon.cpp +0 -4
- data/vendor/faiss/faiss/impl/binary_hamming/IndexBinaryHash_impl.h +5 -2
- data/vendor/faiss/faiss/impl/binary_hamming/avx2.cpp +0 -4
- data/vendor/faiss/faiss/impl/binary_hamming/avx512.cpp +0 -4
- data/vendor/faiss/faiss/impl/binary_hamming/neon.cpp +0 -4
- data/vendor/faiss/faiss/impl/binary_hamming/rvv.cpp +0 -4
- data/vendor/faiss/faiss/impl/expanded_scanners.h +88 -0
- data/vendor/faiss/faiss/impl/fast_scan/FastScanDistancePostProcessing.h +4 -0
- data/vendor/faiss/faiss/impl/fast_scan/accumulate_loops.h +61 -64
- data/vendor/faiss/faiss/impl/fast_scan/accumulate_loops_512.h +13 -14
- data/vendor/faiss/faiss/impl/fast_scan/decompose_qbs.h +17 -129
- data/vendor/faiss/faiss/impl/fast_scan/dispatching.h +25 -19
- data/vendor/faiss/faiss/impl/fast_scan/fast_scan.cpp +5 -0
- data/vendor/faiss/faiss/impl/fast_scan/impl-avx2.cpp +0 -4
- data/vendor/faiss/faiss/impl/fast_scan/impl-avx512.cpp +0 -4
- data/vendor/faiss/faiss/impl/fast_scan/impl-neon.cpp +0 -88
- data/vendor/faiss/faiss/impl/fast_scan/impl-riscv.cpp +0 -4
- data/vendor/faiss/faiss/impl/fast_scan/kernels_neon.h +155 -0
- data/vendor/faiss/faiss/impl/fast_scan/kernels_simd256.h +22 -1
- data/vendor/faiss/faiss/impl/fast_scan/kernels_simd512.h +3 -2
- data/vendor/faiss/faiss/impl/fast_scan/simd_result_handlers.h +41 -23
- data/vendor/faiss/faiss/impl/hnsw/avx2.cpp +0 -4
- data/vendor/faiss/faiss/impl/hnsw/avx512.cpp +0 -4
- data/vendor/faiss/faiss/impl/index_read.cpp +172 -0
- data/vendor/faiss/faiss/impl/index_write.cpp +95 -0
- data/vendor/faiss/faiss/impl/pipnn/HashPrune.cpp +159 -0
- data/vendor/faiss/faiss/impl/pipnn/HashPrune.h +69 -0
- data/vendor/faiss/faiss/impl/pipnn/Partition.cpp +587 -0
- data/vendor/faiss/faiss/impl/pipnn/Partition.h +93 -0
- data/vendor/faiss/faiss/impl/pipnn/kernels.cpp +326 -0
- data/vendor/faiss/faiss/impl/pipnn/kernels.h +92 -0
- data/vendor/faiss/faiss/impl/platform_macros.h +45 -0
- data/vendor/faiss/faiss/impl/polysemous_training/avx512.cpp +0 -4
- data/vendor/faiss/faiss/impl/pq_code_distance/IVFPQScanner_impl.h +79 -4
- data/vendor/faiss/faiss/impl/pq_code_distance/PQDistanceComputer_impl.h +6 -0
- data/vendor/faiss/faiss/impl/pq_code_distance/avx2.cpp +0 -4
- data/vendor/faiss/faiss/impl/pq_code_distance/avx512.cpp +0 -4
- data/vendor/faiss/faiss/impl/pq_code_distance/neon.cpp +0 -4
- data/vendor/faiss/faiss/impl/pq_code_distance/pq_code_distance-avx2.h +4 -0
- data/vendor/faiss/faiss/impl/pq_code_distance/pq_code_distance-avx512.h +4 -1
- data/vendor/faiss/faiss/impl/pq_code_distance/pq_code_distance-generic.cpp +19 -0
- data/vendor/faiss/faiss/impl/pq_code_distance/pq_code_distance-generic.h +6 -6
- data/vendor/faiss/faiss/impl/pq_code_distance/pq_code_distance-inl.h +131 -6
- data/vendor/faiss/faiss/impl/pq_code_distance/pq_code_distance-sve.cpp +0 -4
- data/vendor/faiss/faiss/impl/pq_code_distance/pq_rvv_kernel.h +89 -0
- data/vendor/faiss/faiss/impl/pq_code_distance/pq_scan_impl.h +21 -10
- data/vendor/faiss/faiss/impl/pq_code_distance/rvv.cpp +45 -18
- data/vendor/faiss/faiss/impl/residual_quantizer_encode_steps.cpp +2 -1
- data/vendor/faiss/faiss/impl/result_handler/avx2.cpp +0 -4
- data/vendor/faiss/faiss/impl/result_handler/avx512.cpp +0 -4
- data/vendor/faiss/faiss/impl/scalar_quantizer/EDENQuantizer-avx2.cpp +0 -4
- data/vendor/faiss/faiss/impl/scalar_quantizer/EDENQuantizer-avx512.cpp +0 -4
- data/vendor/faiss/faiss/impl/scalar_quantizer/distance_computers.h +5 -1
- data/vendor/faiss/faiss/impl/scalar_quantizer/quantizers.h +28 -52
- data/vendor/faiss/faiss/impl/scalar_quantizer/scanners.h +59 -11
- data/vendor/faiss/faiss/impl/scalar_quantizer/sq-avx2.cpp +226 -77
- data/vendor/faiss/faiss/impl/scalar_quantizer/sq-avx512-impl.h +482 -103
- data/vendor/faiss/faiss/impl/scalar_quantizer/sq-avx512-spr.cpp +2 -149
- data/vendor/faiss/faiss/impl/scalar_quantizer/sq-avx512.cpp +3 -698
- data/vendor/faiss/faiss/impl/scalar_quantizer/sq-dispatch.h +66 -57
- data/vendor/faiss/faiss/impl/scalar_quantizer/sq-neon.cpp +101 -96
- data/vendor/faiss/faiss/impl/scalar_quantizer/sq-rvv.cpp +3185 -144
- data/vendor/faiss/faiss/impl/scalar_quantizer/sq8_coefficients.h +48 -0
- data/vendor/faiss/faiss/impl/scalar_quantizer/sq8_rvv_kernel.h +546 -0
- data/vendor/faiss/faiss/impl/scalar_quantizer/training.cpp +2 -2
- data/vendor/faiss/faiss/impl/simd_dispatch.h +7 -0
- data/vendor/faiss/faiss/impl/simdlib/simdlib_avx2.h +14 -0
- data/vendor/faiss/faiss/impl/simdlib/simdlib_dispatch.h +7 -8
- data/vendor/faiss/faiss/impl/sq_fastscan_utils.h +232 -0
- data/vendor/faiss/faiss/index_factory.cpp +76 -0
- data/vendor/faiss/faiss/invlists/InvertedLists.cpp +3 -1
- data/vendor/faiss/faiss/svs/IndexSVSVamana.h +3 -2
- data/vendor/faiss/faiss/utils/AlignedTable.h +6 -1
- data/vendor/faiss/faiss/utils/Heap.cpp +4 -3
- data/vendor/faiss/faiss/utils/approx_topk_hamming/approx_topk_hamming.h +16 -5
- data/vendor/faiss/faiss/utils/bf16.h +36 -0
- data/vendor/faiss/faiss/utils/distances.cpp +226 -15
- data/vendor/faiss/faiss/utils/distances.h +34 -0
- data/vendor/faiss/faiss/utils/distances_dispatch.h +6 -4
- data/vendor/faiss/faiss/utils/distances_fused/avx512.cpp +2 -5
- data/vendor/faiss/faiss/utils/distances_fused/distances_fused.cpp +0 -14
- data/vendor/faiss/faiss/utils/distances_fused/rvv.cpp +25 -0
- data/vendor/faiss/faiss/utils/distances_fused/simdlib_based.cpp +0 -4
- data/vendor/faiss/faiss/utils/distances_fused/simdlib_based_neon.cpp +0 -4
- data/vendor/faiss/faiss/utils/distances_fused/simdlib_kernel-inl.h +2 -1
- data/vendor/faiss/faiss/utils/distances_simd.cpp +9 -0
- data/vendor/faiss/faiss/utils/fp16-fp16c.h +4 -0
- data/vendor/faiss/faiss/utils/fp16-inl.h +26 -52
- data/vendor/faiss/faiss/utils/hamming_distance/hamming_avx2.cpp +0 -4
- data/vendor/faiss/faiss/utils/hamming_distance/hamming_avx512.cpp +0 -4
- data/vendor/faiss/faiss/utils/hamming_distance/hamming_avx512_vpopcnt.cpp +0 -4
- data/vendor/faiss/faiss/utils/hamming_distance/hamming_computer-avx512.h +33 -5
- data/vendor/faiss/faiss/utils/hamming_distance/hamming_computer-avx512_vpopcnt.h +15 -30
- data/vendor/faiss/faiss/utils/hamming_distance/hamming_computer-neon.h +0 -4
- data/vendor/faiss/faiss/utils/hamming_distance/hamming_impl.h +2 -1
- data/vendor/faiss/faiss/utils/hamming_distance/hamming_neon.cpp +0 -4
- data/vendor/faiss/faiss/utils/hamming_distance/hamming_rvv.cpp +0 -4
- data/vendor/faiss/faiss/utils/pq_code_distance.h +8 -4
- data/vendor/faiss/faiss/utils/prefetch.h +1 -26
- data/vendor/faiss/faiss/utils/quantize_lut.cpp +26 -27
- data/vendor/faiss/faiss/utils/quantize_lut.h +11 -0
- data/vendor/faiss/faiss/utils/rabitq_simd.h +51 -16
- data/vendor/faiss/faiss/utils/simd_impl/distances_aarch64.cpp +9 -4
- data/vendor/faiss/faiss/utils/simd_impl/distances_arm_sve.cpp +37 -5
- data/vendor/faiss/faiss/utils/simd_impl/distances_avx2.cpp +64 -5
- data/vendor/faiss/faiss/utils/simd_impl/distances_avx512.cpp +64 -5
- data/vendor/faiss/faiss/utils/simd_impl/distances_rvv.cpp +353 -71
- data/vendor/faiss/faiss/utils/simd_impl/distances_rvv_kernel.h +68 -0
- data/vendor/faiss/faiss/utils/simd_impl/exhaustive_L2sqr_blas_cmax.h +93 -0
- data/vendor/faiss/faiss/utils/simd_impl/fp16_kernels.h +76 -0
- data/vendor/faiss/faiss/utils/simd_impl/fp16_kernels_avx2.cpp +52 -0
- data/vendor/faiss/faiss/utils/simd_impl/fp16_kernels_avx512.cpp +52 -0
- data/vendor/faiss/faiss/utils/simd_impl/fp16_kernels_neon.cpp +48 -0
- data/vendor/faiss/faiss/utils/simd_impl/masked_sum-avx2.h +44 -0
- data/vendor/faiss/faiss/utils/simd_impl/masked_sum-avx512.h +43 -0
- data/vendor/faiss/faiss/utils/simd_impl/masked_sum.h +38 -0
- data/vendor/faiss/faiss/utils/simd_impl/partitioning_avx2.cpp +0 -4
- data/vendor/faiss/faiss/utils/simd_impl/partitioning_neon.cpp +0 -4
- data/vendor/faiss/faiss/utils/simd_impl/rabitq_avx2.cpp +54 -15
- data/vendor/faiss/faiss/utils/simd_impl/rabitq_avx512.cpp +38 -21
- data/vendor/faiss/faiss/utils/simd_impl/rabitq_avx512_vpopcnt.cpp +51 -14
- data/vendor/faiss/faiss/utils/simd_impl/rabitq_neon.cpp +297 -10
- data/vendor/faiss/faiss/utils/simd_impl/rabitq_rvv.cpp +0 -4
- data/vendor/faiss/faiss/utils/simd_impl/super_kmeans_kernels_avx2.cpp +0 -4
- data/vendor/faiss/faiss/utils/simd_impl/super_kmeans_kernels_avx512.cpp +0 -4
- data/vendor/faiss/faiss/utils/simd_impl/super_kmeans_kernels_sve.cpp +0 -4
- data/vendor/faiss/faiss/utils/simd_levels.cpp +19 -3
- data/vendor/faiss/faiss/utils/simd_levels.h +74 -1
- data/vendor/faiss/faiss/utils/transpose/transpose-avx2-inl.h +0 -4
- data/vendor/faiss/faiss/utils/transpose/transpose-avx512-inl.h +0 -4
- data/vendor/faiss/faiss/utils/utils.cpp +80 -0
- data/vendor/faiss/faiss/utils/utils.h +24 -0
- metadata +33 -1
|
@@ -0,0 +1,587 @@
|
|
|
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
|
+
#include <faiss/impl/pipnn/Partition.h>
|
|
9
|
+
|
|
10
|
+
#include <omp.h>
|
|
11
|
+
|
|
12
|
+
#include <algorithm>
|
|
13
|
+
#include <atomic>
|
|
14
|
+
#include <cmath>
|
|
15
|
+
#include <exception>
|
|
16
|
+
#include <iterator>
|
|
17
|
+
#include <limits>
|
|
18
|
+
#include <memory>
|
|
19
|
+
#include <utility>
|
|
20
|
+
|
|
21
|
+
#include <faiss/Index.h>
|
|
22
|
+
#include <faiss/impl/AuxIndexStructures.h>
|
|
23
|
+
#include <faiss/impl/FaissAssert.h>
|
|
24
|
+
#include <faiss/impl/FaissException.h>
|
|
25
|
+
#include <faiss/impl/pipnn/kernels.h>
|
|
26
|
+
#include <faiss/impl/platform_macros.h>
|
|
27
|
+
#include <faiss/utils/AlignedTable.h>
|
|
28
|
+
#include <faiss/utils/distances.h>
|
|
29
|
+
#include <faiss/utils/random.h>
|
|
30
|
+
|
|
31
|
+
namespace faiss {
|
|
32
|
+
namespace pipnn {
|
|
33
|
+
|
|
34
|
+
int PartitionParams::fanout_at(int depth) const {
|
|
35
|
+
FAISS_ASSERT(depth >= 0);
|
|
36
|
+
return static_cast<size_t>(depth) < fanout.size() ? fanout[depth] : 1;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
uint64_t mix_seed(uint64_t a, uint64_t b) {
|
|
40
|
+
return SplitMix64RandomGenerator(int64_t(a ^ (b + (a << 6) + (a >> 2))))
|
|
41
|
+
.next();
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
namespace detail {
|
|
45
|
+
|
|
46
|
+
size_t leader_count(size_t size, const PartitionParams& p, int depth) {
|
|
47
|
+
size_t by_fraction = static_cast<size_t>(std::ceil(
|
|
48
|
+
static_cast<double>(p.leader_fraction) *
|
|
49
|
+
static_cast<double>(size)));
|
|
50
|
+
size_t floor4 = 4 * static_cast<size_t>(p.fanout_at(depth));
|
|
51
|
+
size_t count = std::max(by_fraction, floor4);
|
|
52
|
+
count = std::min(count, static_cast<size_t>(p.leader_cap));
|
|
53
|
+
return std::min(count, size);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
void sample_positions(
|
|
57
|
+
size_t N,
|
|
58
|
+
size_t k,
|
|
59
|
+
uint64_t seed,
|
|
60
|
+
std::vector<size_t>& out) {
|
|
61
|
+
FAISS_ASSERT(k <= N);
|
|
62
|
+
out.clear();
|
|
63
|
+
out.reserve(k);
|
|
64
|
+
SplitMix64RandomGenerator rng(static_cast<int64_t>(seed));
|
|
65
|
+
for (size_t j = N - k; j < N; j++) {
|
|
66
|
+
size_t t = static_cast<size_t>(
|
|
67
|
+
rng.next() % (static_cast<uint64_t>(j) + 1));
|
|
68
|
+
auto it = std::lower_bound(out.begin(), out.end(), t);
|
|
69
|
+
if (it != out.end() && *it == t) {
|
|
70
|
+
out.push_back(j); // every sampled position is < j
|
|
71
|
+
} else {
|
|
72
|
+
out.insert(it, t);
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
Ranking ranking_for(const PartitionParams& p, bool force_l2) {
|
|
78
|
+
if (p.metric == METRIC_L2 || force_l2) {
|
|
79
|
+
return Ranking::L2;
|
|
80
|
+
}
|
|
81
|
+
return p.ip_partition_by_angle ? Ranking::Angle : Ranking::IP;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
} // namespace detail
|
|
85
|
+
|
|
86
|
+
namespace {
|
|
87
|
+
|
|
88
|
+
/// Positions per counting-sort block; unlike kStripe, it does not affect
|
|
89
|
+
/// leaves.
|
|
90
|
+
constexpr size_t kScatterBlock = 65536;
|
|
91
|
+
/// Above any leader index, so the merge-order seed never equals a child's.
|
|
92
|
+
constexpr uint64_t kMergeShuffleIndex = uint64_t(1) << 33;
|
|
93
|
+
|
|
94
|
+
constexpr size_t kNoSplit = std::numeric_limits<size_t>::max();
|
|
95
|
+
|
|
96
|
+
/// `ids` is sorted and owned by a LevelStorage (or the root list).
|
|
97
|
+
struct Node {
|
|
98
|
+
const int32_t* ids;
|
|
99
|
+
size_t size;
|
|
100
|
+
uint64_t seed;
|
|
101
|
+
int depth;
|
|
102
|
+
bool force_l2; // L2 ranking: this or an ancestor was a > 90% IP child
|
|
103
|
+
};
|
|
104
|
+
|
|
105
|
+
/// Owns the id lists of every node of one level.
|
|
106
|
+
struct LevelStorage {
|
|
107
|
+
std::unique_ptr<int32_t[]> child_ids;
|
|
108
|
+
std::vector<std::vector<int32_t>> merge_pools;
|
|
109
|
+
};
|
|
110
|
+
|
|
111
|
+
/// Inputs and stats shared by every level of one partition.
|
|
112
|
+
struct LevelContext {
|
|
113
|
+
const Index& storage;
|
|
114
|
+
const PartitionParams& p;
|
|
115
|
+
const LeafVisitor& on_leaf;
|
|
116
|
+
PartitionStats& stats;
|
|
117
|
+
};
|
|
118
|
+
|
|
119
|
+
/// A leaf (split == kNoSplit) or kStripe points of a split node from `begin`.
|
|
120
|
+
struct WorkItem {
|
|
121
|
+
size_t node;
|
|
122
|
+
size_t split;
|
|
123
|
+
size_t begin;
|
|
124
|
+
};
|
|
125
|
+
|
|
126
|
+
struct SplitInfo {
|
|
127
|
+
size_t node = 0;
|
|
128
|
+
std::vector<int32_t> leaders; // ascending ids
|
|
129
|
+
int fanout = 0;
|
|
130
|
+
Ranking ranking = Ranking::L2;
|
|
131
|
+
size_t offset = 0; // start in the level assignment / child-id buffers
|
|
132
|
+
size_t n_blocks = 0;
|
|
133
|
+
size_t count_offset = 0; // start in the level count buffer
|
|
134
|
+
std::vector<size_t> child_off; // |L| + 1 offsets into the child-id buffer
|
|
135
|
+
};
|
|
136
|
+
|
|
137
|
+
struct LevelPlan {
|
|
138
|
+
std::vector<WorkItem> items;
|
|
139
|
+
std::vector<SplitInfo> splits;
|
|
140
|
+
size_t n_instances = 0; // (point, chosen leader) pairs of all splits
|
|
141
|
+
size_t n_counts = 0; // entries of the count buffer
|
|
142
|
+
};
|
|
143
|
+
|
|
144
|
+
/// Next-level nodes of one split. Merged groups have ids == nullptr until
|
|
145
|
+
/// collect_children points them into their pool.
|
|
146
|
+
struct SplitOutput {
|
|
147
|
+
std::vector<Node> nodes;
|
|
148
|
+
std::vector<size_t> pool_offsets;
|
|
149
|
+
std::vector<int32_t> pool;
|
|
150
|
+
size_t n_slices = 0;
|
|
151
|
+
size_t n_l2 = 0;
|
|
152
|
+
};
|
|
153
|
+
|
|
154
|
+
void check_params(const Index& storage, const PartitionParams& p) {
|
|
155
|
+
FAISS_THROW_IF_NOT_MSG(
|
|
156
|
+
p.c_min >= 1 && p.c_min <= p.c_max,
|
|
157
|
+
"PiPNN partition: need 1 <= c_min <= c_max");
|
|
158
|
+
FAISS_THROW_IF_NOT_FMT(
|
|
159
|
+
p.c_max <= kMaxCMax,
|
|
160
|
+
"PiPNN partition: need c_max <= %d (got %d): each thread holds a "
|
|
161
|
+
"c_max x c_max float matrix",
|
|
162
|
+
kMaxCMax,
|
|
163
|
+
p.c_max);
|
|
164
|
+
FAISS_THROW_IF_NOT_FMT(
|
|
165
|
+
p.leader_cap >= 1 && p.leader_cap <= kMaxLeaderCap,
|
|
166
|
+
"PiPNN partition: need 1 <= leader_cap <= %d",
|
|
167
|
+
kMaxLeaderCap);
|
|
168
|
+
FAISS_THROW_IF_NOT_MSG(
|
|
169
|
+
p.leader_fraction >= 0 && p.leader_fraction <= 1,
|
|
170
|
+
"PiPNN partition: need 0 <= leader_fraction <= 1");
|
|
171
|
+
for (int f : p.fanout) {
|
|
172
|
+
FAISS_THROW_IF_NOT_MSG(
|
|
173
|
+
f >= 1, "PiPNN partition: every fanout must be >= 1");
|
|
174
|
+
}
|
|
175
|
+
FAISS_THROW_IF_NOT_MSG(
|
|
176
|
+
p.metric == METRIC_L2 || p.metric == METRIC_INNER_PRODUCT,
|
|
177
|
+
"PiPNN partition: metric must be L2 or inner product");
|
|
178
|
+
FAISS_THROW_IF_NOT_MSG(
|
|
179
|
+
storage.ntotal >= 0 &&
|
|
180
|
+
storage.ntotal <= std::numeric_limits<int32_t>::max(),
|
|
181
|
+
"PiPNN partition: need 0 <= n <= INT32_MAX");
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
void record_leaf(PartitionStats& stats, size_t size, int depth) {
|
|
185
|
+
stats.n_leaves++;
|
|
186
|
+
stats.n_point_instances += size;
|
|
187
|
+
stats.max_leaf = std::max(stats.max_leaf, size);
|
|
188
|
+
stats.max_depth_seen = std::max(stats.max_depth_seen, depth);
|
|
189
|
+
const size_t bucket = (size - 1) / 64;
|
|
190
|
+
if (stats.leaf_size_histogram.size() <= bucket) {
|
|
191
|
+
stats.leaf_size_histogram.resize(bucket + 1, 0);
|
|
192
|
+
}
|
|
193
|
+
stats.leaf_size_histogram[bucket]++;
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
void push_slices(const Node& child, size_t c_max, SplitOutput& out) {
|
|
197
|
+
for (size_t o = 0; o < child.size; o += c_max) {
|
|
198
|
+
out.nodes.push_back(
|
|
199
|
+
Node{child.ids + o,
|
|
200
|
+
std::min(c_max, child.size - o),
|
|
201
|
+
child.seed,
|
|
202
|
+
child.depth,
|
|
203
|
+
child.force_l2});
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
LevelPlan plan_level(const LevelContext& ctx, const std::vector<Node>& nodes) {
|
|
208
|
+
const PartitionParams& p = ctx.p;
|
|
209
|
+
const size_t c_max = static_cast<size_t>(p.c_max);
|
|
210
|
+
size_t n_items = 0;
|
|
211
|
+
size_t n_splits = 0;
|
|
212
|
+
for (const Node& nd : nodes) {
|
|
213
|
+
if (nd.size <= c_max) {
|
|
214
|
+
n_items++;
|
|
215
|
+
} else {
|
|
216
|
+
n_items += (nd.size + kStripe - 1) / kStripe;
|
|
217
|
+
n_splits++;
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
LevelPlan plan;
|
|
221
|
+
plan.splits.reserve(n_splits);
|
|
222
|
+
plan.items.reserve(n_items);
|
|
223
|
+
for (size_t i = 0; i < nodes.size(); i++) {
|
|
224
|
+
const Node& nd = nodes[i];
|
|
225
|
+
if (nd.size <= c_max) {
|
|
226
|
+
record_leaf(ctx.stats, nd.size, nd.depth);
|
|
227
|
+
plan.items.push_back(WorkItem{i, kNoSplit, 0});
|
|
228
|
+
continue;
|
|
229
|
+
}
|
|
230
|
+
SplitInfo sp;
|
|
231
|
+
sp.node = i;
|
|
232
|
+
const size_t n_leaders = detail::leader_count(nd.size, p, nd.depth);
|
|
233
|
+
std::vector<size_t> positions;
|
|
234
|
+
detail::sample_positions(nd.size, n_leaders, nd.seed, positions);
|
|
235
|
+
sp.leaders.resize(n_leaders);
|
|
236
|
+
for (size_t l = 0; l < n_leaders; l++) {
|
|
237
|
+
sp.leaders[l] = nd.ids[positions[l]];
|
|
238
|
+
}
|
|
239
|
+
sp.fanout =
|
|
240
|
+
std::min(p.fanout_at(nd.depth), static_cast<int>(n_leaders));
|
|
241
|
+
sp.ranking = detail::ranking_for(p, nd.force_l2);
|
|
242
|
+
sp.offset = plan.n_instances;
|
|
243
|
+
plan.n_instances += nd.size * sp.fanout;
|
|
244
|
+
sp.n_blocks = (nd.size + kScatterBlock - 1) / kScatterBlock;
|
|
245
|
+
sp.count_offset = plan.n_counts;
|
|
246
|
+
plan.n_counts += sp.n_blocks * n_leaders;
|
|
247
|
+
for (size_t b = 0; b < nd.size; b += kStripe) {
|
|
248
|
+
plan.items.push_back(WorkItem{i, plan.splits.size(), b});
|
|
249
|
+
}
|
|
250
|
+
plan.splits.push_back(std::move(sp));
|
|
251
|
+
}
|
|
252
|
+
return plan;
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
/// Runs the leaf and stripe items; returns each point's `fanout` nearest
|
|
256
|
+
/// leaders.
|
|
257
|
+
std::unique_ptr<uint16_t[]> assign_leaders(
|
|
258
|
+
const LevelContext& ctx,
|
|
259
|
+
const std::vector<Node>& nodes,
|
|
260
|
+
const LevelPlan& plan) {
|
|
261
|
+
const Index& storage = ctx.storage;
|
|
262
|
+
const size_t d = static_cast<size_t>(storage.d);
|
|
263
|
+
// new T[n], not std::vector: every entry is written first; zeroing is
|
|
264
|
+
// serial.
|
|
265
|
+
std::unique_ptr<uint16_t[]> assign(new uint16_t[plan.n_instances]);
|
|
266
|
+
std::exception_ptr ex;
|
|
267
|
+
std::atomic<bool> interrupt{false};
|
|
268
|
+
#pragma omp parallel
|
|
269
|
+
{
|
|
270
|
+
AlignedTable<float, 64> stripe_vecs, leader_vecs, dist;
|
|
271
|
+
std::vector<float> leader_norms;
|
|
272
|
+
size_t n_done = 0;
|
|
273
|
+
#pragma omp for schedule(FAISS_OMP_DYNAMIC)
|
|
274
|
+
for (int64_t i = 0; i < static_cast<int64_t>(plan.items.size()); i++) {
|
|
275
|
+
if (interrupt.load(std::memory_order_relaxed)) {
|
|
276
|
+
continue;
|
|
277
|
+
}
|
|
278
|
+
try {
|
|
279
|
+
const WorkItem& item = plan.items[i];
|
|
280
|
+
const Node& nd = nodes[item.node];
|
|
281
|
+
if (item.split == kNoSplit) {
|
|
282
|
+
ctx.on_leaf(nd.ids, nd.size);
|
|
283
|
+
} else {
|
|
284
|
+
const SplitInfo& sp = plan.splits[item.split];
|
|
285
|
+
const size_t S = std::min(kStripe, nd.size - item.begin);
|
|
286
|
+
const size_t L = sp.leaders.size();
|
|
287
|
+
ensure_size(stripe_vecs, S * d);
|
|
288
|
+
ensure_size(leader_vecs, L * d);
|
|
289
|
+
ensure_size(dist, S * L);
|
|
290
|
+
leader_norms.resize(L);
|
|
291
|
+
gather_rows(
|
|
292
|
+
storage,
|
|
293
|
+
nd.ids + item.begin,
|
|
294
|
+
S,
|
|
295
|
+
stripe_vecs.data());
|
|
296
|
+
gather_rows(
|
|
297
|
+
storage, sp.leaders.data(), L, leader_vecs.data());
|
|
298
|
+
for (size_t l = 0; l < L; l++) {
|
|
299
|
+
leader_norms[l] =
|
|
300
|
+
fvec_norm_L2sqr(leader_vecs.data() + l * d, d);
|
|
301
|
+
}
|
|
302
|
+
stripe_assign(
|
|
303
|
+
stripe_vecs.data(),
|
|
304
|
+
S,
|
|
305
|
+
leader_vecs.data(),
|
|
306
|
+
leader_norms.data(),
|
|
307
|
+
L,
|
|
308
|
+
d,
|
|
309
|
+
sp.ranking,
|
|
310
|
+
sp.fanout,
|
|
311
|
+
dist.data(),
|
|
312
|
+
assign.get() + sp.offset + item.begin * sp.fanout);
|
|
313
|
+
}
|
|
314
|
+
if (omp_get_thread_num() == 0 && n_done++ % 64 == 0 &&
|
|
315
|
+
InterruptCallback::is_interrupted()) {
|
|
316
|
+
FAISS_THROW_MSG("computation interrupted");
|
|
317
|
+
}
|
|
318
|
+
} catch (...) {
|
|
319
|
+
omp_capture_exception(ex, [&] { interrupt = true; });
|
|
320
|
+
}
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
omp_rethrow_if_exception(ex);
|
|
324
|
+
return assign;
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
/// Counting sort per (split, block): count, prefix sums in (leader, block)
|
|
328
|
+
/// order, scatter. Every child comes out in parent order. Fills child_off and
|
|
329
|
+
/// returns the child ids, owned by next_storage.
|
|
330
|
+
const int32_t* scatter_children(
|
|
331
|
+
const std::vector<Node>& nodes,
|
|
332
|
+
LevelPlan& plan,
|
|
333
|
+
const uint16_t* assign,
|
|
334
|
+
LevelStorage& next_storage) {
|
|
335
|
+
std::vector<SplitInfo>& splits = plan.splits;
|
|
336
|
+
std::vector<std::pair<size_t, size_t>> blocks;
|
|
337
|
+
for (size_t s = 0; s < splits.size(); s++) {
|
|
338
|
+
for (size_t b = 0; b < splits[s].n_blocks; b++) {
|
|
339
|
+
blocks.emplace_back(s, b);
|
|
340
|
+
}
|
|
341
|
+
}
|
|
342
|
+
std::unique_ptr<size_t[]> counts(new size_t[plan.n_counts]);
|
|
343
|
+
#pragma omp parallel for schedule(FAISS_OMP_DYNAMIC)
|
|
344
|
+
for (int64_t i = 0; i < static_cast<int64_t>(blocks.size()); i++) {
|
|
345
|
+
const SplitInfo& sp = splits[blocks[i].first];
|
|
346
|
+
const size_t b = blocks[i].second;
|
|
347
|
+
const Node& nd = nodes[sp.node];
|
|
348
|
+
const size_t L = sp.leaders.size();
|
|
349
|
+
size_t* cnt = counts.get() + sp.count_offset + b * L;
|
|
350
|
+
std::fill(cnt, cnt + L, 0);
|
|
351
|
+
const uint16_t* a = assign + sp.offset;
|
|
352
|
+
const size_t end = std::min(nd.size, (b + 1) * kScatterBlock);
|
|
353
|
+
for (size_t pos = b * kScatterBlock; pos < end; pos++) {
|
|
354
|
+
for (int j = 0; j < sp.fanout; j++) {
|
|
355
|
+
cnt[a[pos * sp.fanout + j]]++;
|
|
356
|
+
}
|
|
357
|
+
}
|
|
358
|
+
}
|
|
359
|
+
for (SplitInfo& sp : splits) {
|
|
360
|
+
const size_t L = sp.leaders.size();
|
|
361
|
+
sp.child_off.resize(L + 1);
|
|
362
|
+
size_t running = sp.offset;
|
|
363
|
+
for (size_t l = 0; l < L; l++) {
|
|
364
|
+
sp.child_off[l] = running;
|
|
365
|
+
for (size_t b = 0; b < sp.n_blocks; b++) {
|
|
366
|
+
size_t& c = counts[sp.count_offset + b * L + l];
|
|
367
|
+
const size_t block_count = c;
|
|
368
|
+
c = running;
|
|
369
|
+
running += block_count;
|
|
370
|
+
}
|
|
371
|
+
}
|
|
372
|
+
sp.child_off[L] = running;
|
|
373
|
+
}
|
|
374
|
+
next_storage.child_ids.reset(new int32_t[plan.n_instances]);
|
|
375
|
+
int32_t* child_ids = next_storage.child_ids.get();
|
|
376
|
+
#pragma omp parallel for schedule(FAISS_OMP_DYNAMIC)
|
|
377
|
+
for (int64_t i = 0; i < static_cast<int64_t>(blocks.size()); i++) {
|
|
378
|
+
const SplitInfo& sp = splits[blocks[i].first];
|
|
379
|
+
const size_t b = blocks[i].second;
|
|
380
|
+
const Node& nd = nodes[sp.node];
|
|
381
|
+
size_t* cursor = counts.get() + sp.count_offset + b * sp.leaders.size();
|
|
382
|
+
const uint16_t* a = assign + sp.offset;
|
|
383
|
+
const size_t end = std::min(nd.size, (b + 1) * kScatterBlock);
|
|
384
|
+
for (size_t pos = b * kScatterBlock; pos < end; pos++) {
|
|
385
|
+
for (int j = 0; j < sp.fanout; j++) {
|
|
386
|
+
child_ids[cursor[a[pos * sp.fanout + j]]++] = nd.ids[pos];
|
|
387
|
+
}
|
|
388
|
+
}
|
|
389
|
+
}
|
|
390
|
+
return child_ids;
|
|
391
|
+
}
|
|
392
|
+
|
|
393
|
+
/// Child rules, in order: below c_min it is merged; up to c_max it is a leaf;
|
|
394
|
+
/// at max_depth it is sliced; an IP-ranked child above 90% of its parent is
|
|
395
|
+
/// re-split by L2; a child equal to its parent is sliced; else it is split.
|
|
396
|
+
void classify_children(
|
|
397
|
+
const PartitionParams& p,
|
|
398
|
+
const Node& nd,
|
|
399
|
+
const SplitInfo& sp,
|
|
400
|
+
const int32_t* child_ids,
|
|
401
|
+
SplitOutput& out) {
|
|
402
|
+
const size_t c_min = static_cast<size_t>(p.c_min);
|
|
403
|
+
const size_t c_max = static_cast<size_t>(p.c_max);
|
|
404
|
+
const int child_depth = nd.depth + 1;
|
|
405
|
+
std::vector<uint32_t> small_children;
|
|
406
|
+
for (size_t l = 0; l < sp.leaders.size(); l++) {
|
|
407
|
+
Node child{
|
|
408
|
+
child_ids + sp.child_off[l],
|
|
409
|
+
sp.child_off[l + 1] - sp.child_off[l],
|
|
410
|
+
mix_seed(nd.seed, l),
|
|
411
|
+
child_depth,
|
|
412
|
+
nd.force_l2};
|
|
413
|
+
if (child.size < c_min) {
|
|
414
|
+
small_children.push_back(static_cast<uint32_t>(l));
|
|
415
|
+
} else if (child.size <= c_max) {
|
|
416
|
+
out.nodes.push_back(child);
|
|
417
|
+
} else if (child_depth >= p.max_depth) {
|
|
418
|
+
push_slices(child, c_max, out);
|
|
419
|
+
out.n_slices++;
|
|
420
|
+
} else if (sp.ranking != Ranking::L2 && 10 * child.size > 9 * nd.size) {
|
|
421
|
+
child.force_l2 = true;
|
|
422
|
+
out.nodes.push_back(child);
|
|
423
|
+
out.n_l2++;
|
|
424
|
+
} else if (child.size == nd.size) {
|
|
425
|
+
push_slices(child, c_max, out);
|
|
426
|
+
out.n_slices++;
|
|
427
|
+
} else {
|
|
428
|
+
out.nodes.push_back(child);
|
|
429
|
+
}
|
|
430
|
+
}
|
|
431
|
+
|
|
432
|
+
std::vector<int> merge_order(small_children.size());
|
|
433
|
+
rand_perm_splitmix64(
|
|
434
|
+
merge_order.data(),
|
|
435
|
+
merge_order.size(),
|
|
436
|
+
static_cast<int64_t>(mix_seed(nd.seed, kMergeShuffleIndex)));
|
|
437
|
+
std::vector<int32_t> group;
|
|
438
|
+
auto close_group = [&]() {
|
|
439
|
+
std::sort(group.begin(), group.end());
|
|
440
|
+
group.erase(std::unique(group.begin(), group.end()), group.end());
|
|
441
|
+
out.pool_offsets.push_back(out.pool.size());
|
|
442
|
+
out.pool.insert(out.pool.end(), group.begin(), group.end());
|
|
443
|
+
// At most c_max points, so it is a leaf and its seed is never used.
|
|
444
|
+
out.nodes.push_back(
|
|
445
|
+
Node{nullptr, group.size(), 0, child_depth, nd.force_l2});
|
|
446
|
+
group.clear();
|
|
447
|
+
};
|
|
448
|
+
for (int i : merge_order) {
|
|
449
|
+
const uint32_t l = small_children[i];
|
|
450
|
+
const int32_t* c = child_ids + sp.child_off[l];
|
|
451
|
+
const size_t size = sp.child_off[l + 1] - sp.child_off[l];
|
|
452
|
+
if (!group.empty() && group.size() + size > c_max) {
|
|
453
|
+
close_group();
|
|
454
|
+
}
|
|
455
|
+
group.insert(group.end(), c, c + size);
|
|
456
|
+
}
|
|
457
|
+
if (!group.empty()) {
|
|
458
|
+
close_group();
|
|
459
|
+
}
|
|
460
|
+
}
|
|
461
|
+
|
|
462
|
+
void collect_children(
|
|
463
|
+
const LevelContext& ctx,
|
|
464
|
+
const std::vector<Node>& nodes,
|
|
465
|
+
const std::vector<SplitInfo>& splits,
|
|
466
|
+
const int32_t* child_ids,
|
|
467
|
+
std::vector<Node>& next,
|
|
468
|
+
LevelStorage& next_storage) {
|
|
469
|
+
std::vector<SplitOutput> outputs(splits.size());
|
|
470
|
+
std::exception_ptr ex;
|
|
471
|
+
#pragma omp parallel for schedule(FAISS_OMP_DYNAMIC)
|
|
472
|
+
for (int64_t s = 0; s < static_cast<int64_t>(splits.size()); s++) {
|
|
473
|
+
try {
|
|
474
|
+
classify_children(
|
|
475
|
+
ctx.p,
|
|
476
|
+
nodes[splits[s].node],
|
|
477
|
+
splits[s],
|
|
478
|
+
child_ids,
|
|
479
|
+
outputs[s]);
|
|
480
|
+
} catch (...) {
|
|
481
|
+
omp_capture_exception(ex);
|
|
482
|
+
}
|
|
483
|
+
}
|
|
484
|
+
omp_rethrow_if_exception(ex);
|
|
485
|
+
size_t n_next = next.size();
|
|
486
|
+
for (const SplitOutput& out : outputs) {
|
|
487
|
+
n_next += out.nodes.size();
|
|
488
|
+
}
|
|
489
|
+
next.reserve(n_next);
|
|
490
|
+
next_storage.merge_pools.reserve(outputs.size());
|
|
491
|
+
for (SplitOutput& out : outputs) {
|
|
492
|
+
ctx.stats.n_slice_fallbacks += out.n_slices;
|
|
493
|
+
ctx.stats.n_l2_repartitions += out.n_l2;
|
|
494
|
+
next_storage.merge_pools.push_back(std::move(out.pool));
|
|
495
|
+
const int32_t* pool = next_storage.merge_pools.back().data();
|
|
496
|
+
size_t k = 0;
|
|
497
|
+
for (Node& child : out.nodes) {
|
|
498
|
+
if (child.ids == nullptr) {
|
|
499
|
+
child.ids = pool + out.pool_offsets[k++];
|
|
500
|
+
}
|
|
501
|
+
}
|
|
502
|
+
next.insert(
|
|
503
|
+
next.end(),
|
|
504
|
+
std::make_move_iterator(out.nodes.begin()),
|
|
505
|
+
std::make_move_iterator(out.nodes.end()));
|
|
506
|
+
std::vector<Node>().swap(out.nodes);
|
|
507
|
+
std::vector<size_t>().swap(out.pool_offsets);
|
|
508
|
+
}
|
|
509
|
+
}
|
|
510
|
+
|
|
511
|
+
void run_level(
|
|
512
|
+
const LevelContext& ctx,
|
|
513
|
+
const std::vector<Node>& nodes,
|
|
514
|
+
std::vector<Node>& next,
|
|
515
|
+
LevelStorage& next_storage) {
|
|
516
|
+
LevelPlan plan = plan_level(ctx, nodes);
|
|
517
|
+
const std::unique_ptr<uint16_t[]> assign = assign_leaders(ctx, nodes, plan);
|
|
518
|
+
if (plan.splits.empty()) {
|
|
519
|
+
return;
|
|
520
|
+
}
|
|
521
|
+
const int32_t* child_ids =
|
|
522
|
+
scatter_children(nodes, plan, assign.get(), next_storage);
|
|
523
|
+
collect_children(ctx, nodes, plan.splits, child_ids, next, next_storage);
|
|
524
|
+
}
|
|
525
|
+
|
|
526
|
+
} // namespace
|
|
527
|
+
|
|
528
|
+
void partition_and_visit(
|
|
529
|
+
const Index& storage,
|
|
530
|
+
const PartitionParams& p,
|
|
531
|
+
uint64_t root_seed,
|
|
532
|
+
const LeafVisitor& on_leaf,
|
|
533
|
+
PartitionStats* stats,
|
|
534
|
+
const std::vector<int32_t>& ids) {
|
|
535
|
+
check_params(storage, p);
|
|
536
|
+
PartitionStats st;
|
|
537
|
+
const LevelContext ctx{storage, p, on_leaf, st};
|
|
538
|
+
const size_t n =
|
|
539
|
+
ids.empty() ? static_cast<size_t>(storage.ntotal) : ids.size();
|
|
540
|
+
if (n > 0) {
|
|
541
|
+
InterruptCallback::check();
|
|
542
|
+
std::unique_ptr<int32_t[]> root_ids(new int32_t[n]);
|
|
543
|
+
for (size_t i = 0; i < n; i++) {
|
|
544
|
+
root_ids[i] = ids.empty() ? static_cast<int32_t>(i) : ids[i];
|
|
545
|
+
}
|
|
546
|
+
std::vector<Node> root_children;
|
|
547
|
+
LevelStorage root_storage;
|
|
548
|
+
run_level(
|
|
549
|
+
ctx,
|
|
550
|
+
{Node{root_ids.get(), n, root_seed, 0, false}},
|
|
551
|
+
root_children,
|
|
552
|
+
root_storage);
|
|
553
|
+
root_ids.reset();
|
|
554
|
+
|
|
555
|
+
size_t w0 = 0;
|
|
556
|
+
while (w0 < root_children.size()) {
|
|
557
|
+
size_t w1 = w0;
|
|
558
|
+
size_t load = 0;
|
|
559
|
+
while (w1 < root_children.size() &&
|
|
560
|
+
(w1 == w0 ||
|
|
561
|
+
load + root_children[w1].size <= p.wave_budget)) {
|
|
562
|
+
load += root_children[w1].size;
|
|
563
|
+
w1++;
|
|
564
|
+
}
|
|
565
|
+
st.n_waves++;
|
|
566
|
+
std::vector<Node> nodes(
|
|
567
|
+
root_children.begin() + w0, root_children.begin() + w1);
|
|
568
|
+
std::unique_ptr<LevelStorage> level_storage;
|
|
569
|
+
while (!nodes.empty()) {
|
|
570
|
+
InterruptCallback::check();
|
|
571
|
+
std::vector<Node> next;
|
|
572
|
+
auto next_storage = std::make_unique<LevelStorage>();
|
|
573
|
+
run_level(ctx, nodes, next, *next_storage);
|
|
574
|
+
nodes = std::move(next);
|
|
575
|
+
level_storage =
|
|
576
|
+
std::move(next_storage); // frees the parent lists
|
|
577
|
+
}
|
|
578
|
+
w0 = w1;
|
|
579
|
+
}
|
|
580
|
+
}
|
|
581
|
+
if (stats) {
|
|
582
|
+
*stats = std::move(st);
|
|
583
|
+
}
|
|
584
|
+
}
|
|
585
|
+
|
|
586
|
+
} // namespace pipnn
|
|
587
|
+
} // namespace faiss
|
|
@@ -0,0 +1,93 @@
|
|
|
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
|
+
#pragma once
|
|
9
|
+
|
|
10
|
+
#include <faiss/MetricType.h>
|
|
11
|
+
#include <cstddef>
|
|
12
|
+
#include <cstdint>
|
|
13
|
+
#include <functional>
|
|
14
|
+
#include <vector>
|
|
15
|
+
|
|
16
|
+
namespace faiss {
|
|
17
|
+
|
|
18
|
+
struct Index;
|
|
19
|
+
|
|
20
|
+
namespace pipnn {
|
|
21
|
+
|
|
22
|
+
enum class Ranking : uint8_t;
|
|
23
|
+
|
|
24
|
+
/// Randomized Ball Carving settings (paper Alg. 5).
|
|
25
|
+
struct PartitionParams {
|
|
26
|
+
int c_max = 1024; // largest leaf
|
|
27
|
+
int c_min = 128; // smaller children are merged
|
|
28
|
+
int leader_cap = 1000; // most leaders per split
|
|
29
|
+
float leader_fraction = 0.005f; // leaders per split, as a fraction of it
|
|
30
|
+
std::vector<int> fanout = {10, 3}; // leaders joined per depth, then 1
|
|
31
|
+
MetricType metric = METRIC_L2;
|
|
32
|
+
bool ip_partition_by_angle = false;
|
|
33
|
+
size_t wave_budget = size_t(1) << 30; // point instances per wave
|
|
34
|
+
int max_depth = 32; // children at this depth are cut into id slices
|
|
35
|
+
int fanout_at(int depth) const;
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
/// Each partition thread holds a c_max x c_max float matrix.
|
|
39
|
+
inline constexpr int kMaxCMax = 4096;
|
|
40
|
+
/// Leader indices are stored as uint16_t.
|
|
41
|
+
inline constexpr int kMaxLeaderCap = 65535;
|
|
42
|
+
|
|
43
|
+
struct PartitionStats {
|
|
44
|
+
size_t n_leaves = 0;
|
|
45
|
+
size_t n_point_instances = 0; // sum of leaf sizes
|
|
46
|
+
size_t max_leaf = 0;
|
|
47
|
+
size_t n_slice_fallbacks = 0, n_l2_repartitions = 0;
|
|
48
|
+
int max_depth_seen = 0;
|
|
49
|
+
/// leaf_size_histogram[i] counts leaves of size in [64 i + 1, 64 (i + 1)].
|
|
50
|
+
std::vector<size_t> leaf_size_histogram;
|
|
51
|
+
size_t n_waves = 0;
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
/// Called once per leaf with its sorted, unique ids (s <= c_max), concurrently
|
|
55
|
+
/// from partition_and_visit's OpenMP region: omp_get_thread_num() identifies
|
|
56
|
+
/// the calling thread. `ids` is valid only during the call.
|
|
57
|
+
using LeafVisitor = std::function<void(const int32_t* ids, size_t s)>;
|
|
58
|
+
|
|
59
|
+
/// Randomized Ball Carving with multi-level fanout over the sorted `ids` (all
|
|
60
|
+
/// of storage if empty). Calls `on_leaf` exactly once per leaf and fills
|
|
61
|
+
/// `*stats` (if not null). On an exception or an interrupt it rethrows with
|
|
62
|
+
/// some leaves unvisited and `*stats` unchanged.
|
|
63
|
+
void partition_and_visit(
|
|
64
|
+
const Index& storage,
|
|
65
|
+
const PartitionParams& p,
|
|
66
|
+
uint64_t root_seed,
|
|
67
|
+
const LeafVisitor& on_leaf,
|
|
68
|
+
PartitionStats* stats,
|
|
69
|
+
const std::vector<int32_t>& ids = {});
|
|
70
|
+
|
|
71
|
+
/// Deterministic hash of (a, b) for derived seeds. For a fixed a, distinct b
|
|
72
|
+
/// give distinct results.
|
|
73
|
+
uint64_t mix_seed(uint64_t a, uint64_t b);
|
|
74
|
+
|
|
75
|
+
/// Building blocks of partition_and_visit, exposed for unit tests.
|
|
76
|
+
namespace detail {
|
|
77
|
+
|
|
78
|
+
/// min(max(ceil(leader_fraction * size), 4 * fanout(depth)), leader_cap, size)
|
|
79
|
+
size_t leader_count(size_t size, const PartitionParams& p, int depth);
|
|
80
|
+
|
|
81
|
+
/// k distinct positions of [0, N), ascending (Floyd's algorithm).
|
|
82
|
+
void sample_positions(
|
|
83
|
+
size_t N,
|
|
84
|
+
size_t k,
|
|
85
|
+
uint64_t seed,
|
|
86
|
+
std::vector<size_t>& out);
|
|
87
|
+
|
|
88
|
+
Ranking ranking_for(const PartitionParams& p, bool force_l2);
|
|
89
|
+
|
|
90
|
+
} // namespace detail
|
|
91
|
+
|
|
92
|
+
} // namespace pipnn
|
|
93
|
+
} // namespace faiss
|