faiss 0.6.3 → 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 +4 -0
- data/lib/faiss/version.rb +1 -1
- data/vendor/faiss/faiss/Clustering.h +8 -0
- data/vendor/faiss/faiss/Index.h +1 -1
- data/vendor/faiss/faiss/IndexBinaryFromFloat.cpp +23 -2
- data/vendor/faiss/faiss/IndexBinaryHNSW.cpp +12 -140
- data/vendor/faiss/faiss/IndexBinaryHNSW.h +0 -6
- data/vendor/faiss/faiss/IndexBinaryIVF.cpp +6 -14
- data/vendor/faiss/faiss/IndexBinaryIVF.h +8 -1
- data/vendor/faiss/faiss/IndexFlat.cpp +8 -4
- data/vendor/faiss/faiss/IndexFlat.h +2 -2
- data/vendor/faiss/faiss/IndexHNSW.cpp +301 -91
- data/vendor/faiss/faiss/IndexHNSW.h +31 -7
- data/vendor/faiss/faiss/IndexIDMap.cpp +11 -6
- data/vendor/faiss/faiss/IndexIVF.cpp +19 -4
- data/vendor/faiss/faiss/IndexIVFPQ.cpp +9 -1
- data/vendor/faiss/faiss/IndexIVFPQ.h +11 -0
- data/vendor/faiss/faiss/IndexShards.cpp +3 -3
- data/vendor/faiss/faiss/IndexShardsIVF.cpp +4 -3
- data/vendor/faiss/faiss/SuperKMeans.cpp +31 -8
- data/vendor/faiss/faiss/SuperKMeans.h +3 -2
- data/vendor/faiss/faiss/VectorTransform.cpp +38 -0
- data/vendor/faiss/faiss/VectorTransform.h +3 -0
- data/vendor/faiss/faiss/clone_index.cpp +3 -0
- 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 +4 -5
- data/vendor/faiss/faiss/gpu/GpuIndexCagra.h +65 -42
- data/vendor/faiss/faiss/impl/AdditiveQuantizer.cpp +1 -1
- data/vendor/faiss/faiss/impl/ClusteringInitialization.cpp +5 -2
- data/vendor/faiss/faiss/impl/HNSW.cpp +419 -115
- data/vendor/faiss/faiss/impl/HNSW.h +46 -7
- data/vendor/faiss/faiss/impl/IDSelector.h +44 -0
- data/vendor/faiss/faiss/impl/NNDescent.cpp +10 -3
- data/vendor/faiss/faiss/impl/Panorama.h +11 -2
- data/vendor/faiss/faiss/impl/ProductQuantizer.cpp +4 -4
- data/vendor/faiss/faiss/impl/RaBitQUtils.cpp +1 -1
- data/vendor/faiss/faiss/impl/RaBitQuantizer.cpp +74 -8
- data/vendor/faiss/faiss/impl/RaBitQuantizer.h +47 -4
- data/vendor/faiss/faiss/impl/RaBitQuantizerMultiBit.cpp +2 -2
- data/vendor/faiss/faiss/impl/ResultHandler.h +9 -3
- data/vendor/faiss/faiss/impl/ScalarQuantizer.cpp +32 -30
- data/vendor/faiss/faiss/impl/ScalarQuantizer.h +6 -10
- data/vendor/faiss/faiss/impl/binary_hamming/IndexBinaryIVF_impl.h +89 -13
- 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/index_read.cpp +130 -29
- data/vendor/faiss/faiss/impl/index_write.cpp +31 -26
- data/vendor/faiss/faiss/impl/pq_code_distance/IVFPQScanner_impl.h +73 -39
- data/vendor/faiss/faiss/impl/pq_code_distance/pq_code_distance-generic.cpp +4 -4
- data/vendor/faiss/faiss/impl/scalar_quantizer/EDENQuantizer.cpp +6 -1
- data/vendor/faiss/faiss/impl/scalar_quantizer/distance_computers.h +2 -2
- data/vendor/faiss/faiss/impl/scalar_quantizer/sq-avx2.cpp +6 -2
- data/vendor/faiss/faiss/impl/scalar_quantizer/sq-dispatch.h +37 -17
- data/vendor/faiss/faiss/impl/scalar_quantizer/sq-neon.cpp +109 -10
- data/vendor/faiss/faiss/impl/simd_dispatch.h +40 -22
- data/vendor/faiss/faiss/index_factory.cpp +8 -1
- data/vendor/faiss/faiss/invlists/InvertedLists.cpp +7 -4
- data/vendor/faiss/faiss/invlists/OnDiskInvertedLists.cpp +10 -4
- data/vendor/faiss/faiss/svs/IndexSVSVamana.cpp +16 -4
- data/vendor/faiss/faiss/svs/IndexSVSVamana.h +8 -1
- 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 +9 -2
- data/vendor/faiss/faiss/svs/IndexSVSVamanaLeanVec.h +2 -1
- data/vendor/faiss/faiss/utils/distances.cpp +20 -13
- 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/hamming.cpp +15 -9
- 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/simd_impl/distances_arm_sve.cpp +194 -30
- data/vendor/faiss/faiss/utils/simd_impl/distances_rvv.cpp +93 -55
- data/vendor/faiss/faiss/utils/simd_impl/rabitq_avx512.cpp +57 -40
- data/vendor/faiss/faiss/utils/simd_impl/{rabitq_avx512_spr.cpp → rabitq_avx512_vpopcnt.cpp} +21 -24
- 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 +172 -67
- data/vendor/faiss/faiss/utils/simd_levels.h +26 -15
- metadata +6 -5
- data/vendor/faiss/faiss/utils/hamming_distance/hamming_avx512_spr.cpp +0 -15
|
@@ -5,17 +5,19 @@
|
|
|
5
5
|
* LICENSE file in the root directory of this source tree.
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
|
-
#ifndef
|
|
9
|
-
#define
|
|
8
|
+
#ifndef HAMMING_COMPUTER_AVX512_VPOPCNT_H
|
|
9
|
+
#define HAMMING_COMPUTER_AVX512_VPOPCNT_H
|
|
10
10
|
|
|
11
|
-
//
|
|
12
|
-
//
|
|
13
|
-
//
|
|
14
|
-
//
|
|
15
|
-
// without -mavx512vpopcntdq.
|
|
11
|
+
// AVX512_VPOPCNT HammingComputer specializations. The 32/64/Default kernels
|
|
12
|
+
// use VPOPCNTDQ; the batched 20-byte kernel uses AVX512_BITALG. This gives
|
|
13
|
+
// a faster path than the scalar popcount fallback used in the base AVX512
|
|
14
|
+
// specializations when compiled without -mavx512vpopcntdq.
|
|
16
15
|
|
|
17
16
|
#include <cassert>
|
|
18
17
|
#include <cstdint>
|
|
18
|
+
#include <cstring>
|
|
19
|
+
|
|
20
|
+
#include <faiss/utils/popcount.h>
|
|
19
21
|
|
|
20
22
|
#include <faiss/impl/platform_macros.h>
|
|
21
23
|
#include <faiss/utils/hamming_distance/hamming_computer-avx512.h>
|
|
@@ -25,31 +27,90 @@
|
|
|
25
27
|
namespace faiss {
|
|
26
28
|
|
|
27
29
|
/***************************************************************************
|
|
28
|
-
*
|
|
30
|
+
* AVX512_VPOPCNT inheriting specializations without custom VPOPCNT code.
|
|
29
31
|
***************************************************************************/
|
|
30
32
|
|
|
31
|
-
#define
|
|
32
|
-
template <>
|
|
33
|
-
struct Class##
|
|
34
|
-
_tpl<SIMDLevel::
|
|
35
|
-
using Class##_tpl<SIMDLevel::AVX512>::Class##_tpl;
|
|
33
|
+
#define FAISS_INHERIT_HAMMING_VPOPCNT(Class) \
|
|
34
|
+
template <> \
|
|
35
|
+
struct Class## \
|
|
36
|
+
_tpl<SIMDLevel::AVX512_VPOPCNT> : Class##_tpl<SIMDLevel::AVX512> { \
|
|
37
|
+
using Class##_tpl<SIMDLevel::AVX512>::Class##_tpl; \
|
|
36
38
|
}
|
|
37
39
|
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
FAISS_INHERIT_HAMMING_SPR(GenHammingComputerM8);
|
|
40
|
+
FAISS_INHERIT_HAMMING_VPOPCNT(HammingComputer16);
|
|
41
|
+
FAISS_INHERIT_HAMMING_VPOPCNT(GenHammingComputer8);
|
|
42
|
+
FAISS_INHERIT_HAMMING_VPOPCNT(GenHammingComputer16);
|
|
43
|
+
FAISS_INHERIT_HAMMING_VPOPCNT(GenHammingComputer32);
|
|
44
|
+
FAISS_INHERIT_HAMMING_VPOPCNT(GenHammingComputerM8);
|
|
44
45
|
|
|
45
|
-
#undef
|
|
46
|
+
#undef FAISS_INHERIT_HAMMING_VPOPCNT
|
|
46
47
|
|
|
47
48
|
/***************************************************************************
|
|
48
|
-
* Custom
|
|
49
|
+
* Custom AVX512_VPOPCNT specializations using VPOPCNTDQ.
|
|
49
50
|
***************************************************************************/
|
|
50
51
|
|
|
51
52
|
template <>
|
|
52
|
-
struct
|
|
53
|
+
struct HammingComputer20_tpl<SIMDLevel::AVX512_VPOPCNT>
|
|
54
|
+
: HammingComputer20_tpl<SIMDLevel::AVX512> {
|
|
55
|
+
using HammingComputer20_tpl<SIMDLevel::AVX512>::HammingComputer20_tpl;
|
|
56
|
+
|
|
57
|
+
static constexpr size_t batch_size = 8;
|
|
58
|
+
static constexpr size_t kStride = get_code_size();
|
|
59
|
+
// 160 bytes is what the three loads in hamming_batch() cover, and the
|
|
60
|
+
// 16+4 or 4+16 split it applies per lane is written out for 20 bytes:
|
|
61
|
+
// the literal offsets and the two group indices are not derived from
|
|
62
|
+
// kStride, so another width needs the body reworked, not just retuned.
|
|
63
|
+
static_assert(batch_size * kStride == 160);
|
|
64
|
+
static_assert(kStride == 20, "hamming_batch() hardcodes the 16+4 split");
|
|
65
|
+
static constexpr __mmask64 kTailMask = 0xFFFFFFFFull;
|
|
66
|
+
|
|
67
|
+
/// Writes the query repeated batch_size times. The caller owns the buffer,
|
|
68
|
+
/// so a computer used only through hamming() carries no batch state.
|
|
69
|
+
static void build_batch_query(const uint8_t* a8, uint8_t* tile) {
|
|
70
|
+
for (size_t k = 0; k < batch_size; k++) {
|
|
71
|
+
memcpy(tile + k * kStride, a8, kStride);
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
static void hamming_batch(
|
|
76
|
+
const uint8_t* tile,
|
|
77
|
+
const uint8_t* codes,
|
|
78
|
+
int32_t* dis) {
|
|
79
|
+
const __m512i zero = _mm512_setzero_si512();
|
|
80
|
+
const __m512i p0 = _mm512_popcnt_epi8(_mm512_xor_si512(
|
|
81
|
+
_mm512_loadu_si512(codes), _mm512_loadu_si512(tile)));
|
|
82
|
+
const __m512i p1 = _mm512_popcnt_epi8(_mm512_xor_si512(
|
|
83
|
+
_mm512_loadu_si512(codes + 64), _mm512_loadu_si512(tile + 64)));
|
|
84
|
+
const __m512i p2 = _mm512_popcnt_epi8(_mm512_xor_si512(
|
|
85
|
+
_mm512_maskz_loadu_epi8(kTailMask, codes + 128),
|
|
86
|
+
_mm512_maskz_loadu_epi8(kTailMask, tile + 128)));
|
|
87
|
+
|
|
88
|
+
alignas(64) uint64_t grp[24];
|
|
89
|
+
_mm512_store_si512(grp, _mm512_sad_epu8(p0, zero));
|
|
90
|
+
_mm512_store_si512(grp + 8, _mm512_sad_epu8(p1, zero));
|
|
91
|
+
_mm512_store_si512(grp + 16, _mm512_sad_epu8(p2, zero));
|
|
92
|
+
|
|
93
|
+
for (size_t k = 0; k < batch_size; k++) {
|
|
94
|
+
const size_t s = k * kStride;
|
|
95
|
+
const size_t g = s / 8;
|
|
96
|
+
uint32_t xh, qh;
|
|
97
|
+
if (s % 8 == 0) {
|
|
98
|
+
memcpy(&xh, codes + s + 16, 4);
|
|
99
|
+
memcpy(&qh, tile + s + 16, 4);
|
|
100
|
+
dis[k] = static_cast<int32_t>(
|
|
101
|
+
grp[g] + grp[g + 1] + popcount32(xh ^ qh));
|
|
102
|
+
} else {
|
|
103
|
+
memcpy(&xh, codes + s, 4);
|
|
104
|
+
memcpy(&qh, tile + s, 4);
|
|
105
|
+
dis[k] = static_cast<int32_t>(
|
|
106
|
+
popcount32(xh ^ qh) + grp[g + 1] + grp[g + 2]);
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
};
|
|
111
|
+
|
|
112
|
+
template <>
|
|
113
|
+
struct HammingComputer32_tpl<SIMDLevel::AVX512_VPOPCNT> {
|
|
53
114
|
const uint8_t* a8;
|
|
54
115
|
|
|
55
116
|
HammingComputer32_tpl() {}
|
|
@@ -81,7 +142,7 @@ struct HammingComputer32_tpl<SIMDLevel::AVX512_SPR> {
|
|
|
81
142
|
};
|
|
82
143
|
|
|
83
144
|
template <>
|
|
84
|
-
struct HammingComputer64_tpl<SIMDLevel::
|
|
145
|
+
struct HammingComputer64_tpl<SIMDLevel::AVX512_VPOPCNT> {
|
|
85
146
|
const uint8_t* a8;
|
|
86
147
|
|
|
87
148
|
HammingComputer64_tpl() {}
|
|
@@ -108,7 +169,7 @@ struct HammingComputer64_tpl<SIMDLevel::AVX512_SPR> {
|
|
|
108
169
|
};
|
|
109
170
|
|
|
110
171
|
template <>
|
|
111
|
-
struct HammingComputerDefault_tpl<SIMDLevel::
|
|
172
|
+
struct HammingComputerDefault_tpl<SIMDLevel::AVX512_VPOPCNT> {
|
|
112
173
|
const uint8_t* a8;
|
|
113
174
|
int quotient8;
|
|
114
175
|
int remainder8;
|
|
@@ -36,6 +36,7 @@
|
|
|
36
36
|
|
|
37
37
|
#include <algorithm>
|
|
38
38
|
#include <cstdio>
|
|
39
|
+
#include <cstring>
|
|
39
40
|
#include <limits>
|
|
40
41
|
#include <memory>
|
|
41
42
|
#include <vector>
|
|
@@ -148,6 +149,7 @@ void hammings_knn_mc_impl(
|
|
|
148
149
|
std::unique_ptr<int64_t[]> all_ids_per_dis(new int64_t[na * nBuckets * k]);
|
|
149
150
|
|
|
150
151
|
std::vector<HCounterState<HammingComputer>> cs;
|
|
152
|
+
cs.reserve(na);
|
|
151
153
|
for (size_t i = 0; i < na; ++i) {
|
|
152
154
|
cs.push_back(
|
|
153
155
|
HCounterState<HammingComputer>(
|
|
@@ -298,6 +300,132 @@ void generalized_hammings_knn_hc_impl(
|
|
|
298
300
|
}
|
|
299
301
|
}
|
|
300
302
|
|
|
303
|
+
/* Both number of words and remainder are constants, so the compiler flattens
|
|
304
|
+
* the per-chunk loop. The query is copied into qw/qt once, not re-read for
|
|
305
|
+
* every candidate. */
|
|
306
|
+
template <int NW, int REM>
|
|
307
|
+
void hammings_ragged_fixed(
|
|
308
|
+
const uint8_t* __restrict a,
|
|
309
|
+
const uint8_t* __restrict b,
|
|
310
|
+
size_t na,
|
|
311
|
+
size_t nb,
|
|
312
|
+
size_t ncodes,
|
|
313
|
+
hamdis_t* __restrict dis) {
|
|
314
|
+
for (size_t i = 0; i < na; i++) {
|
|
315
|
+
const uint8_t* __restrict ai = a + i * ncodes;
|
|
316
|
+
hamdis_t* __restrict dis_ = dis + i * nb;
|
|
317
|
+
// Read the query once here, not once per candidate.
|
|
318
|
+
uint64_t qw[NW == 0 ? 1 : NW];
|
|
319
|
+
for (int k = 0; k < NW; k++) {
|
|
320
|
+
memcpy(&qw[k], ai + k * 8, 8);
|
|
321
|
+
}
|
|
322
|
+
uint64_t qt = 0;
|
|
323
|
+
if constexpr (REM > 0) {
|
|
324
|
+
memcpy(&qt, ai + NW * 8, REM);
|
|
325
|
+
}
|
|
326
|
+
for (size_t j = 0; j < nb; j++) {
|
|
327
|
+
const uint8_t* __restrict bj = b + j * ncodes;
|
|
328
|
+
hamdis_t h = 0;
|
|
329
|
+
uint64_t y;
|
|
330
|
+
for (int k = 0; k < NW; k++) {
|
|
331
|
+
memcpy(&y, bj + k * 8, 8);
|
|
332
|
+
h += popcount64(qw[k] ^ y);
|
|
333
|
+
}
|
|
334
|
+
if constexpr (REM > 0) {
|
|
335
|
+
y = 0;
|
|
336
|
+
memcpy(&y, bj + NW * 8, REM);
|
|
337
|
+
h += popcount64(qt ^ y);
|
|
338
|
+
}
|
|
339
|
+
dis_[j] = h;
|
|
340
|
+
}
|
|
341
|
+
}
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
/* Same loop with the word count read at run time, for codes long enough that
|
|
345
|
+
* a constant count gives no performance improvement. */
|
|
346
|
+
template <int REM>
|
|
347
|
+
void hammings_ragged_var(
|
|
348
|
+
const uint8_t* __restrict a,
|
|
349
|
+
const uint8_t* __restrict b,
|
|
350
|
+
size_t na,
|
|
351
|
+
size_t nb,
|
|
352
|
+
size_t ncodes,
|
|
353
|
+
hamdis_t* __restrict dis) {
|
|
354
|
+
const size_t nwords = ncodes / 8;
|
|
355
|
+
for (size_t i = 0; i < na; i++) {
|
|
356
|
+
const uint8_t* __restrict ai = a + i * ncodes;
|
|
357
|
+
hamdis_t* __restrict dis_ = dis + i * nb;
|
|
358
|
+
for (size_t j = 0; j < nb; j++) {
|
|
359
|
+
const uint8_t* __restrict bj = b + j * ncodes;
|
|
360
|
+
hamdis_t h = 0;
|
|
361
|
+
uint64_t x, y;
|
|
362
|
+
for (size_t k = 0; k < nwords; k++) {
|
|
363
|
+
memcpy(&x, ai + k * 8, 8);
|
|
364
|
+
memcpy(&y, bj + k * 8, 8);
|
|
365
|
+
h += popcount64(x ^ y);
|
|
366
|
+
}
|
|
367
|
+
if constexpr (REM > 0) {
|
|
368
|
+
x = 0;
|
|
369
|
+
y = 0;
|
|
370
|
+
memcpy(&x, ai + nwords * 8, REM);
|
|
371
|
+
memcpy(&y, bj + nwords * 8, REM);
|
|
372
|
+
h += popcount64(x ^ y);
|
|
373
|
+
}
|
|
374
|
+
dis_[j] = h;
|
|
375
|
+
}
|
|
376
|
+
}
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
/* Makes the word count a constant for short codes when calling
|
|
380
|
+
* hammings_ragged_fixed, worth roughly 2x perf improvement. Longer codes
|
|
381
|
+
* gain nothing from it, so they share hammings_ragged_var. */
|
|
382
|
+
template <int REM>
|
|
383
|
+
void hammings_ragged_by_nwords(
|
|
384
|
+
const uint8_t* __restrict a,
|
|
385
|
+
const uint8_t* __restrict b,
|
|
386
|
+
size_t na,
|
|
387
|
+
size_t nb,
|
|
388
|
+
size_t ncodes,
|
|
389
|
+
hamdis_t* __restrict dis) {
|
|
390
|
+
switch (ncodes / 8) {
|
|
391
|
+
case 0:
|
|
392
|
+
return hammings_ragged_fixed<0, REM>(a, b, na, nb, ncodes, dis);
|
|
393
|
+
case 1:
|
|
394
|
+
return hammings_ragged_fixed<1, REM>(a, b, na, nb, ncodes, dis);
|
|
395
|
+
case 2:
|
|
396
|
+
return hammings_ragged_fixed<2, REM>(a, b, na, nb, ncodes, dis);
|
|
397
|
+
case 3:
|
|
398
|
+
return hammings_ragged_fixed<3, REM>(a, b, na, nb, ncodes, dis);
|
|
399
|
+
default:
|
|
400
|
+
return hammings_ragged_var<REM>(a, b, na, nb, ncodes, dis);
|
|
401
|
+
}
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
void hammings_ragged_dispatch(
|
|
405
|
+
const uint8_t* __restrict a,
|
|
406
|
+
const uint8_t* __restrict b,
|
|
407
|
+
size_t na,
|
|
408
|
+
size_t nb,
|
|
409
|
+
size_t ncodes,
|
|
410
|
+
hamdis_t* __restrict dis) {
|
|
411
|
+
switch (ncodes % 8) {
|
|
412
|
+
case 1:
|
|
413
|
+
return hammings_ragged_by_nwords<1>(a, b, na, nb, ncodes, dis);
|
|
414
|
+
case 2:
|
|
415
|
+
return hammings_ragged_by_nwords<2>(a, b, na, nb, ncodes, dis);
|
|
416
|
+
case 3:
|
|
417
|
+
return hammings_ragged_by_nwords<3>(a, b, na, nb, ncodes, dis);
|
|
418
|
+
case 4:
|
|
419
|
+
return hammings_ragged_by_nwords<4>(a, b, na, nb, ncodes, dis);
|
|
420
|
+
case 5:
|
|
421
|
+
return hammings_ragged_by_nwords<5>(a, b, na, nb, ncodes, dis);
|
|
422
|
+
case 6:
|
|
423
|
+
return hammings_ragged_by_nwords<6>(a, b, na, nb, ncodes, dis);
|
|
424
|
+
default:
|
|
425
|
+
return hammings_ragged_by_nwords<7>(a, b, na, nb, ncodes, dis);
|
|
426
|
+
}
|
|
427
|
+
}
|
|
428
|
+
|
|
301
429
|
} // anonymous namespace
|
|
302
430
|
|
|
303
431
|
/******************************************************************
|
|
@@ -356,6 +484,19 @@ void hamming_range_search_fixSL<THE_SIMD_LEVEL>(
|
|
|
356
484
|
});
|
|
357
485
|
}
|
|
358
486
|
|
|
487
|
+
/* Its own entry point, so the word-multiple kernels keep the code the
|
|
488
|
+
* compiler already generates for them. */
|
|
489
|
+
template <>
|
|
490
|
+
void hammings_ragged_fixSL<THE_SIMD_LEVEL>(
|
|
491
|
+
const uint8_t* a,
|
|
492
|
+
const uint8_t* b,
|
|
493
|
+
size_t na,
|
|
494
|
+
size_t nb,
|
|
495
|
+
size_t ncodes,
|
|
496
|
+
hamdis_t* dis) {
|
|
497
|
+
hammings_ragged_dispatch(a, b, na, nb, ncodes, dis);
|
|
498
|
+
}
|
|
499
|
+
|
|
359
500
|
template <>
|
|
360
501
|
void hammings_fixSL<THE_SIMD_LEVEL>(
|
|
361
502
|
const uint8_t* a,
|
|
@@ -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 <>
|