faiss 0.6.1 → 0.6.3

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.
Files changed (186) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +8 -0
  3. data/lib/faiss/version.rb +1 -1
  4. data/vendor/faiss/faiss/AutoTune.cpp +3 -1
  5. data/vendor/faiss/faiss/Clustering.cpp +9 -1
  6. data/vendor/faiss/faiss/IVFlib.cpp +14 -3
  7. data/vendor/faiss/faiss/Index.h +2 -2
  8. data/vendor/faiss/faiss/IndexAdditiveQuantizer.cpp +9 -10
  9. data/vendor/faiss/faiss/IndexAdditiveQuantizerFastScan.cpp +2 -3
  10. data/vendor/faiss/faiss/IndexBinaryFromFloat.cpp +1 -2
  11. data/vendor/faiss/faiss/IndexBinaryHNSW.cpp +10 -12
  12. data/vendor/faiss/faiss/IndexBinaryHash.cpp +5 -9
  13. data/vendor/faiss/faiss/IndexBinaryIVF.cpp +5 -7
  14. data/vendor/faiss/faiss/IndexEDEN.cpp +273 -0
  15. data/vendor/faiss/faiss/IndexEDEN.h +57 -0
  16. data/vendor/faiss/faiss/IndexFastScan.cpp +15 -4
  17. data/vendor/faiss/faiss/IndexFlat.cpp +13 -50
  18. data/vendor/faiss/faiss/IndexHNSW.cpp +177 -148
  19. data/vendor/faiss/faiss/IndexIDMap.cpp +16 -3
  20. data/vendor/faiss/faiss/IndexIDMap.h +2 -0
  21. data/vendor/faiss/faiss/IndexIVF.cpp +19 -8
  22. data/vendor/faiss/faiss/IndexIVFAdditiveQuantizer.cpp +3 -3
  23. data/vendor/faiss/faiss/IndexIVFAdditiveQuantizerFastScan.cpp +3 -4
  24. data/vendor/faiss/faiss/IndexIVFEDEN.cpp +302 -0
  25. data/vendor/faiss/faiss/IndexIVFEDEN.h +70 -0
  26. data/vendor/faiss/faiss/IndexIVFFastScan.cpp +5 -6
  27. data/vendor/faiss/faiss/IndexIVFFlat.cpp +6 -5
  28. data/vendor/faiss/faiss/IndexIVFFlatPanorama.cpp +3 -3
  29. data/vendor/faiss/faiss/IndexIVFIndependentQuantizer.cpp +1 -1
  30. data/vendor/faiss/faiss/IndexIVFPQ.cpp +42 -25
  31. data/vendor/faiss/faiss/IndexIVFPQFastScan.cpp +0 -1
  32. data/vendor/faiss/faiss/IndexIVFPQR.cpp +2 -3
  33. data/vendor/faiss/faiss/IndexIVFRaBitQ.cpp +23 -62
  34. data/vendor/faiss/faiss/IndexIVFRaBitQFastScan.cpp +180 -76
  35. data/vendor/faiss/faiss/IndexIVFRaBitQFastScan.h +5 -4
  36. data/vendor/faiss/faiss/IndexIVFSpectralHash.cpp +8 -6
  37. data/vendor/faiss/faiss/IndexLSH.cpp +2 -3
  38. data/vendor/faiss/faiss/IndexLattice.cpp +5 -0
  39. data/vendor/faiss/faiss/IndexNNDescent.cpp +10 -3
  40. data/vendor/faiss/faiss/IndexNSG.cpp +8 -4
  41. data/vendor/faiss/faiss/IndexPQ.cpp +6 -8
  42. data/vendor/faiss/faiss/IndexPreTransform.cpp +15 -0
  43. data/vendor/faiss/faiss/IndexRaBitQ.cpp +2 -2
  44. data/vendor/faiss/faiss/IndexRaBitQFastScan.cpp +1 -2
  45. data/vendor/faiss/faiss/IndexRaBitQFastScan.h +5 -1
  46. data/vendor/faiss/faiss/IndexRefine.cpp +30 -1
  47. data/vendor/faiss/faiss/IndexReplicas.cpp +1 -2
  48. data/vendor/faiss/faiss/IndexScalarQuantizer.cpp +68 -6
  49. data/vendor/faiss/faiss/IndexScalarQuantizer.h +10 -0
  50. data/vendor/faiss/faiss/IndexShards.cpp +2 -2
  51. data/vendor/faiss/faiss/IndexShardsIVF.cpp +2 -2
  52. data/vendor/faiss/faiss/MetaIndexes.cpp +2 -4
  53. data/vendor/faiss/faiss/SuperKMeans.cpp +256 -240
  54. data/vendor/faiss/faiss/SuperKMeans.h +30 -0
  55. data/vendor/faiss/faiss/VectorTransform.cpp +33 -2
  56. data/vendor/faiss/faiss/clone_index.cpp +5 -0
  57. data/vendor/faiss/faiss/cppcontrib/SaDecodeKernels.h +1 -1
  58. data/vendor/faiss/faiss/cppcontrib/sa_decode/Level2-neon-inl.h +902 -12
  59. data/vendor/faiss/faiss/cppcontrib/sa_decode/PQ-neon-inl.h +702 -10
  60. data/vendor/faiss/faiss/factory_tools.cpp +51 -4
  61. data/vendor/faiss/faiss/gpu/GpuCloner.cpp +11 -11
  62. data/vendor/faiss/faiss/gpu/GpuIndex.h +34 -11
  63. data/vendor/faiss/faiss/gpu/GpuIndexCagra.h +47 -0
  64. data/vendor/faiss/faiss/gpu/GpuIndexIVF.h +17 -0
  65. data/vendor/faiss/faiss/gpu/GpuIndexIVFScalarQuantizer.h +16 -0
  66. data/vendor/faiss/faiss/gpu/GpuResources.h +3 -2
  67. data/vendor/faiss/faiss/gpu/StandardGpuResources.cpp +11 -12
  68. data/vendor/faiss/faiss/gpu/StandardGpuResources.h +3 -3
  69. data/vendor/faiss/faiss/gpu/perf/PerfClustering.cpp +1 -1
  70. data/vendor/faiss/faiss/gpu/perf/PerfIVFPQAdd.cpp +2 -2
  71. data/vendor/faiss/faiss/gpu/test/TestGpuIndexIVFScalarQuantizer.cpp +180 -0
  72. data/vendor/faiss/faiss/gpu_metal/MetalDistance.h +87 -0
  73. data/vendor/faiss/faiss/gpu_metal/MetalIndex.h +7 -0
  74. data/vendor/faiss/faiss/gpu_metal/MetalIndexIVFFlat.h +177 -0
  75. data/vendor/faiss/faiss/gpu_metal/MetalIndexIVFPQ.h +88 -0
  76. data/vendor/faiss/faiss/gpu_metal/MetalKernels.h +48 -3
  77. data/vendor/faiss/faiss/gpu_metal/MetalPythonBridge.h +45 -0
  78. data/vendor/faiss/faiss/gpu_metal/impl/MetalIVFFlat.h +193 -0
  79. data/vendor/faiss/faiss/gpu_metal/impl/MetalIVFPQ.h +134 -0
  80. data/vendor/faiss/faiss/impl/ClusteringInitialization.cpp +2 -2
  81. data/vendor/faiss/faiss/impl/DistanceComputer.h +34 -0
  82. data/vendor/faiss/faiss/impl/EDENQuantizer.h +119 -0
  83. data/vendor/faiss/faiss/impl/HNSW.cpp +658 -344
  84. data/vendor/faiss/faiss/impl/HNSW.h +51 -13
  85. data/vendor/faiss/faiss/impl/LocalSearchQuantizer.cpp +2 -2
  86. data/vendor/faiss/faiss/impl/NSG.cpp +18 -12
  87. data/vendor/faiss/faiss/impl/Panorama.h +20 -7
  88. data/vendor/faiss/faiss/impl/PolysemousTraining.cpp +152 -84
  89. data/vendor/faiss/faiss/impl/ProductQuantizer.cpp +59 -24
  90. data/vendor/faiss/faiss/impl/RaBitQUtils.cpp +45 -37
  91. data/vendor/faiss/faiss/impl/RaBitQUtils.h +35 -0
  92. data/vendor/faiss/faiss/impl/RaBitQuantizer.cpp +175 -68
  93. data/vendor/faiss/faiss/impl/RaBitQuantizer.h +19 -0
  94. data/vendor/faiss/faiss/impl/RaBitQuantizerMultiBit.cpp +2 -11
  95. data/vendor/faiss/faiss/impl/ResultHandler.h +26 -31
  96. data/vendor/faiss/faiss/impl/ScalarQuantizer.cpp +522 -58
  97. data/vendor/faiss/faiss/impl/ScalarQuantizer.h +70 -0
  98. data/vendor/faiss/faiss/impl/ThreadedIndex-inl.h +2 -2
  99. data/vendor/faiss/faiss/impl/VisitedTable.cpp +33 -13
  100. data/vendor/faiss/faiss/impl/VisitedTable.h +88 -33
  101. data/vendor/faiss/faiss/impl/binary_hamming/IndexBinaryIVF_impl.h +1 -1
  102. data/vendor/faiss/faiss/impl/binary_hamming/avx2.cpp +4 -4
  103. data/vendor/faiss/faiss/impl/fast_scan/dispatching.h +38 -3
  104. data/vendor/faiss/faiss/impl/hnsw/LockVector.cpp +1 -1
  105. data/vendor/faiss/faiss/impl/hnsw/MinimaxHeap.cpp +35 -43
  106. data/vendor/faiss/faiss/impl/hnsw/MinimaxHeap.h +64 -15
  107. data/vendor/faiss/faiss/impl/hnsw/avx2.cpp +86 -40
  108. data/vendor/faiss/faiss/impl/hnsw/avx512.cpp +81 -50
  109. data/vendor/faiss/faiss/impl/index_read.cpp +476 -75
  110. data/vendor/faiss/faiss/impl/index_write.cpp +56 -4
  111. data/vendor/faiss/faiss/impl/io_macros.h +25 -0
  112. data/vendor/faiss/faiss/impl/lattice_Zn.cpp +8 -9
  113. data/vendor/faiss/faiss/impl/platform_macros.h +15 -9
  114. data/vendor/faiss/faiss/impl/polysemous_training/avx512.cpp +284 -0
  115. data/vendor/faiss/faiss/impl/polysemous_training/dispatch.h +115 -0
  116. data/vendor/faiss/faiss/impl/pq_code_distance/IVFPQ_QueryTables.cpp +0 -1
  117. data/vendor/faiss/faiss/impl/pq_code_distance/PQDistanceComputer_impl.h +26 -15
  118. data/vendor/faiss/faiss/impl/pq_code_distance/avx2.cpp +6 -4
  119. data/vendor/faiss/faiss/impl/pq_code_distance/avx512.cpp +2 -0
  120. data/vendor/faiss/faiss/impl/pq_code_distance/neon.cpp +2 -0
  121. data/vendor/faiss/faiss/impl/pq_code_distance/pq_code_distance-generic.cpp +20 -0
  122. data/vendor/faiss/faiss/impl/pq_code_distance/pq_code_distance-inl.h +36 -0
  123. data/vendor/faiss/faiss/impl/pq_code_distance/pq_code_distance-sve.cpp +5 -0
  124. data/vendor/faiss/faiss/impl/pq_code_distance/pq_scan_impl.h +105 -0
  125. data/vendor/faiss/faiss/impl/pq_code_distance/rvv.cpp +2 -0
  126. data/vendor/faiss/faiss/impl/result_handler/ResultHandler.cpp +195 -0
  127. data/vendor/faiss/faiss/impl/result_handler/avx2.cpp +133 -0
  128. data/vendor/faiss/faiss/impl/result_handler/avx512.cpp +281 -0
  129. data/vendor/faiss/faiss/impl/scalar_quantizer/EDENQuantizer-avx2.cpp +72 -0
  130. data/vendor/faiss/faiss/impl/scalar_quantizer/EDENQuantizer-avx512.cpp +228 -0
  131. data/vendor/faiss/faiss/impl/scalar_quantizer/EDENQuantizer.cpp +882 -0
  132. data/vendor/faiss/faiss/impl/scalar_quantizer/distance_computers.h +6 -0
  133. data/vendor/faiss/faiss/impl/scalar_quantizer/quantizers.h +336 -26
  134. data/vendor/faiss/faiss/impl/scalar_quantizer/sq-avx2.cpp +331 -32
  135. data/vendor/faiss/faiss/impl/scalar_quantizer/sq-avx512-impl.h +553 -0
  136. data/vendor/faiss/faiss/impl/scalar_quantizer/sq-avx512-spr.cpp +558 -0
  137. data/vendor/faiss/faiss/impl/scalar_quantizer/sq-avx512.cpp +284 -45
  138. data/vendor/faiss/faiss/impl/scalar_quantizer/sq-dispatch.h +502 -3
  139. data/vendor/faiss/faiss/impl/scalar_quantizer/sq-neon.cpp +157 -32
  140. data/vendor/faiss/faiss/impl/scalar_quantizer/sq-rvv.cpp +26 -0
  141. data/vendor/faiss/faiss/impl/simd_dispatch.h +86 -8
  142. data/vendor/faiss/faiss/index_factory.cpp +37 -7
  143. data/vendor/faiss/faiss/index_io.h +16 -0
  144. data/vendor/faiss/faiss/invlists/DirectMap.cpp +5 -2
  145. data/vendor/faiss/faiss/invlists/InvertedLists.cpp +15 -15
  146. data/vendor/faiss/faiss/invlists/InvertedLists.h +2 -2
  147. data/vendor/faiss/faiss/invlists/OnDiskInvertedLists.cpp +19 -4
  148. data/vendor/faiss/faiss/python/python_callbacks.cpp +3 -1
  149. data/vendor/faiss/faiss/svs/IndexSVSFaissUtils.h +60 -0
  150. data/vendor/faiss/faiss/svs/IndexSVSFlat.cpp +26 -1
  151. data/vendor/faiss/faiss/svs/IndexSVSFlat.h +13 -0
  152. data/vendor/faiss/faiss/svs/IndexSVSIVF.cpp +1 -1
  153. data/vendor/faiss/faiss/svs/IndexSVSIVFLeanVec.cpp +1 -1
  154. data/vendor/faiss/faiss/svs/IndexSVSVamana.cpp +150 -23
  155. data/vendor/faiss/faiss/svs/IndexSVSVamana.h +30 -7
  156. data/vendor/faiss/faiss/svs/IndexSVSVamanaLVQ.cpp +3 -2
  157. data/vendor/faiss/faiss/svs/IndexSVSVamanaLVQ.h +2 -1
  158. data/vendor/faiss/faiss/svs/IndexSVSVamanaLeanVec.cpp +65 -25
  159. data/vendor/faiss/faiss/svs/IndexSVSVamanaLeanVec.h +3 -2
  160. data/vendor/faiss/faiss/utils/approx_topk_hamming/approx_topk_hamming.h +1 -1
  161. data/vendor/faiss/faiss/utils/bf16.h +34 -0
  162. data/vendor/faiss/faiss/utils/distances.cpp +14 -2
  163. data/vendor/faiss/faiss/utils/distances_simd.cpp +4 -4
  164. data/vendor/faiss/faiss/utils/extra_distances.cpp +4 -14
  165. data/vendor/faiss/faiss/utils/extra_distances.h +1 -2
  166. data/vendor/faiss/faiss/utils/hamming.cpp +9 -9
  167. data/vendor/faiss/faiss/utils/hamming_distance/hamming_avx2.cpp +2 -1
  168. data/vendor/faiss/faiss/utils/hamming_distance/hamming_avx512_spr.cpp +15 -0
  169. data/vendor/faiss/faiss/utils/hamming_distance/hamming_computer-avx512.h +6 -30
  170. data/vendor/faiss/faiss/utils/hamming_distance/hamming_computer-avx512_spr.h +171 -0
  171. data/vendor/faiss/faiss/utils/partitioning.cpp +0 -2
  172. data/vendor/faiss/faiss/utils/quantize_lut.cpp +29 -8
  173. data/vendor/faiss/faiss/utils/rabitq_simd.h +202 -0
  174. data/vendor/faiss/faiss/utils/simd_impl/distances_avx2.cpp +0 -1
  175. data/vendor/faiss/faiss/utils/simd_impl/distances_avx512.cpp +263 -15
  176. data/vendor/faiss/faiss/utils/simd_impl/distances_rvv.cpp +160 -18
  177. data/vendor/faiss/faiss/utils/simd_impl/partitioning_simdlib256.h +14 -68
  178. data/vendor/faiss/faiss/utils/simd_impl/rabitq_avx2.cpp +245 -0
  179. data/vendor/faiss/faiss/utils/simd_impl/rabitq_avx512.cpp +273 -0
  180. data/vendor/faiss/faiss/utils/simd_impl/rabitq_avx512_spr.cpp +435 -0
  181. data/vendor/faiss/faiss/utils/simd_impl/rabitq_neon.cpp +11 -0
  182. data/vendor/faiss/faiss/utils/simd_impl/rabitq_rvv.cpp +143 -6
  183. data/vendor/faiss/faiss/utils/simd_levels.cpp +56 -2
  184. data/vendor/faiss/faiss/utils/simd_levels.h +14 -0
  185. data/vendor/faiss/faiss/utils/utils.cpp +9 -27
  186. metadata +27 -2
@@ -0,0 +1,435 @@
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
+ /**
9
+ * @file rabitq_avx512_spr.cpp
10
+ *
11
+ * RaBitQ SIMD kernels specialized for SIMDLevel::AVX512_SPR.
12
+ *
13
+ * Sapphire Rapids (SPR) and later Intel microarchitectures expose
14
+ * AVX-512 VPOPCNTDQ (vpopcntq), which performs a per-lane 64-bit
15
+ * popcount in a single instruction. This is used here to replace the
16
+ * multi-step shuffle/pshufb-based popcount used by the generic AVX-512
17
+ * specialization in rabitq_avx512.cpp. The popcount-heavy kernels
18
+ * (bitwise_and_dot_product, bitwise_xor_dot_product, popcount) become
19
+ * substantially shorter and faster on SPR+ as a result.
20
+ *
21
+ * Build / dispatch behavior:
22
+ * - faiss_avx512 (AVX-512 only, no SPR features): NOT compiled.
23
+ * The existing AVX512 specialization in rabitq_avx512.cpp is used.
24
+ * - faiss_avx512_spr (statically built for SPR+): compiled. The
25
+ * SINGLE_SIMD_LEVEL is AVX512_SPR, so this specialization is
26
+ * selected by static dispatch.
27
+ * - faiss with FAISS_OPT_LEVEL=dd (dynamic dispatch): compiled with
28
+ * -mavx512vpopcntdq as a per-file flag. Selected at runtime when
29
+ * SIMDConfig::level == SIMDLevel::AVX512_SPR.
30
+ *
31
+ * The floating-point multi-bit inner-product kernel does not benefit
32
+ * from VPOPCNTDQ, so this TU forwards compute_inner_product<SPR> to
33
+ * the AVX512 implementation to avoid duplicating that code path.
34
+ */
35
+
36
+ #ifdef COMPILE_SIMD_AVX512_SPR
37
+
38
+ #include <faiss/utils/popcount.h>
39
+ #include <faiss/utils/rabitq_simd.h>
40
+ #include <immintrin.h>
41
+ #include <cstdint>
42
+
43
+ #if defined(_MSC_VER)
44
+ #include <intrin.h>
45
+ #endif
46
+
47
+ namespace faiss::rabitq {
48
+
49
+ // Forward declarations for the AVX512 specializations defined in
50
+ // rabitq_avx512.cpp. They live in the same TU group on SPR builds, so
51
+ // we can reuse them as a tail handler / fallback. Declaring rather
52
+ // than redefining avoids ODR risk and keeps a single source of truth
53
+ // for the floating-point kernel.
54
+ template <>
55
+ uint64_t bitwise_and_dot_product<SIMDLevel::AVX512>(
56
+ const uint8_t* query,
57
+ const uint8_t* data,
58
+ size_t size,
59
+ size_t qb);
60
+ template <>
61
+ uint64_t bitwise_xor_dot_product<SIMDLevel::AVX512>(
62
+ const uint8_t* query,
63
+ const uint8_t* data,
64
+ size_t size,
65
+ size_t qb);
66
+ template <>
67
+ uint64_t popcount<SIMDLevel::AVX512>(const uint8_t* data, size_t size);
68
+
69
+ namespace {
70
+
71
+ // 512-bit popcount using AVX-512 VPOPCNTDQ (vpopcntq).
72
+ // Single-instruction per-lane popcount on 8x uint64 lanes.
73
+ inline __m512i popcount_512_vpopcntdq(__m512i v) {
74
+ return _mm512_popcnt_epi64(v);
75
+ }
76
+
77
+ // 256-bit popcount using AVX-512VL VPOPCNTDQ.
78
+ // AVX512VL is part of the SPR feature set, so vpopcntq is available
79
+ // on 256-bit registers via _mm256_popcnt_epi64.
80
+ inline __m256i popcount_256_vpopcntdq(__m256i v) {
81
+ return _mm256_popcnt_epi64(v);
82
+ }
83
+
84
+ // 128-bit popcount using AVX-512VL VPOPCNTDQ.
85
+ inline __m128i popcount_128_vpopcntdq(__m128i v) {
86
+ return _mm_popcnt_epi64(v);
87
+ }
88
+
89
+ inline uint64_t reduce_add_256(__m256i v) {
90
+ alignas(32) uint64_t lanes[4];
91
+ _mm256_store_si256(reinterpret_cast<__m256i*>(lanes), v);
92
+ return lanes[0] + lanes[1] + lanes[2] + lanes[3];
93
+ }
94
+
95
+ inline uint64_t reduce_add_128(__m128i v) {
96
+ alignas(16) uint64_t lanes[2];
97
+ _mm_store_si128(reinterpret_cast<__m128i*>(lanes), v);
98
+ return lanes[0] + lanes[1];
99
+ }
100
+
101
+ } // namespace
102
+
103
+ template <>
104
+ uint64_t bitwise_and_dot_product<SIMDLevel::AVX512_SPR>(
105
+ const uint8_t* query,
106
+ const uint8_t* data,
107
+ size_t size,
108
+ size_t qb) {
109
+ uint64_t sum = 0;
110
+ size_t offset = 0;
111
+
112
+ // 512-bit main loop: vpopcntq replaces the shuffle-based popcount,
113
+ // halving the instruction count per iteration relative to AVX512.
114
+ if (size_t step = 512 / 8; offset + step <= size) {
115
+ __m512i sum_512 = _mm512_setzero_si512();
116
+ for (; offset + step <= size; offset += step) {
117
+ __m512i v_x = _mm512_loadu_si512(
118
+ reinterpret_cast<const __m512i*>(data + offset));
119
+ for (size_t j = 0; j < qb; j++) {
120
+ __m512i v_q = _mm512_loadu_si512(
121
+ reinterpret_cast<const __m512i*>(
122
+ query + j * size + offset));
123
+ __m512i v_and = _mm512_and_si512(v_q, v_x);
124
+ __m512i v_popcnt = popcount_512_vpopcntdq(v_and);
125
+ __m512i v_shifted = _mm512_slli_epi64(v_popcnt, j);
126
+ sum_512 = _mm512_add_epi64(sum_512, v_shifted);
127
+ }
128
+ }
129
+ sum += _mm512_reduce_add_epi64(sum_512);
130
+ }
131
+
132
+ // 256-bit tail.
133
+ if (size_t step = 256 / 8; offset + step <= size) {
134
+ __m256i sum_256 = _mm256_setzero_si256();
135
+ for (; offset + step <= size; offset += step) {
136
+ __m256i v_x = _mm256_loadu_si256(
137
+ reinterpret_cast<const __m256i*>(data + offset));
138
+ for (size_t j = 0; j < qb; j++) {
139
+ __m256i v_q = _mm256_loadu_si256(
140
+ reinterpret_cast<const __m256i*>(
141
+ query + j * size + offset));
142
+ __m256i v_and = _mm256_and_si256(v_q, v_x);
143
+ __m256i v_popcnt = popcount_256_vpopcntdq(v_and);
144
+ __m256i v_shifted = _mm256_slli_epi64(v_popcnt, j);
145
+ sum_256 = _mm256_add_epi64(sum_256, v_shifted);
146
+ }
147
+ }
148
+ sum += reduce_add_256(sum_256);
149
+ }
150
+
151
+ // 128-bit tail.
152
+ __m128i sum_128 = _mm_setzero_si128();
153
+ for (size_t step = 128 / 8; offset + step <= size; offset += step) {
154
+ __m128i v_x = _mm_loadu_si128(
155
+ reinterpret_cast<const __m128i*>(data + offset));
156
+ for (size_t j = 0; j < qb; j++) {
157
+ __m128i v_q = _mm_loadu_si128(
158
+ reinterpret_cast<const __m128i*>(
159
+ query + j * size + offset));
160
+ __m128i v_and = _mm_and_si128(v_q, v_x);
161
+ __m128i v_popcnt = popcount_128_vpopcntdq(v_and);
162
+ __m128i v_shifted = _mm_slli_epi64(v_popcnt, j);
163
+ sum_128 = _mm_add_epi64(sum_128, v_shifted);
164
+ }
165
+ }
166
+ sum += reduce_add_128(sum_128);
167
+
168
+ // 64-bit scalar tail.
169
+ for (size_t step = 64 / 8; offset + step <= size; offset += step) {
170
+ const auto yv = *reinterpret_cast<const uint64_t*>(data + offset);
171
+ for (size_t j = 0; j < qb; j++) {
172
+ const auto qv = *reinterpret_cast<const uint64_t*>(
173
+ query + j * size + offset);
174
+ sum += static_cast<uint64_t>(popcount64(qv & yv)) << j;
175
+ }
176
+ }
177
+ // Byte tail.
178
+ for (; offset < size; ++offset) {
179
+ const auto yv = *(data + offset);
180
+ for (size_t j = 0; j < qb; j++) {
181
+ const auto qv = *(query + j * size + offset);
182
+ sum += static_cast<uint64_t>(popcount32(qv & yv)) << j;
183
+ }
184
+ }
185
+ return sum;
186
+ }
187
+
188
+ template <>
189
+ BitwiseAndDotProductResult bitwise_and_dot_product_with_popcount<
190
+ SIMDLevel::AVX512_SPR>(
191
+ const uint8_t* query,
192
+ const uint8_t* data,
193
+ size_t size,
194
+ size_t qb) {
195
+ uint64_t dot_product = 0;
196
+ uint64_t popcount_sum = 0;
197
+ size_t offset = 0;
198
+
199
+ if (size_t step = 512 / 8; offset + step <= size) {
200
+ __m512i dot_512 = _mm512_setzero_si512();
201
+ __m512i pop_512 = _mm512_setzero_si512();
202
+ for (; offset + step <= size; offset += step) {
203
+ __m512i v_x = _mm512_loadu_si512(
204
+ reinterpret_cast<const __m512i*>(data + offset));
205
+ pop_512 = _mm512_add_epi64(pop_512, popcount_512_vpopcntdq(v_x));
206
+ for (size_t j = 0; j < qb; j++) {
207
+ __m512i v_q = _mm512_loadu_si512(
208
+ reinterpret_cast<const __m512i*>(
209
+ query + j * size + offset));
210
+ __m512i v_and = _mm512_and_si512(v_q, v_x);
211
+ __m512i v_popcnt = popcount_512_vpopcntdq(v_and);
212
+ __m512i v_shifted = _mm512_slli_epi64(v_popcnt, j);
213
+ dot_512 = _mm512_add_epi64(dot_512, v_shifted);
214
+ }
215
+ }
216
+ dot_product += _mm512_reduce_add_epi64(dot_512);
217
+ popcount_sum += _mm512_reduce_add_epi64(pop_512);
218
+ }
219
+
220
+ if (size_t step = 256 / 8; offset + step <= size) {
221
+ __m256i dot_256 = _mm256_setzero_si256();
222
+ __m256i pop_256 = _mm256_setzero_si256();
223
+ for (; offset + step <= size; offset += step) {
224
+ __m256i v_x = _mm256_loadu_si256(
225
+ reinterpret_cast<const __m256i*>(data + offset));
226
+ pop_256 = _mm256_add_epi64(pop_256, popcount_256_vpopcntdq(v_x));
227
+ for (size_t j = 0; j < qb; j++) {
228
+ __m256i v_q = _mm256_loadu_si256(
229
+ reinterpret_cast<const __m256i*>(
230
+ query + j * size + offset));
231
+ __m256i v_and = _mm256_and_si256(v_q, v_x);
232
+ __m256i v_popcnt = popcount_256_vpopcntdq(v_and);
233
+ __m256i v_shifted = _mm256_slli_epi64(v_popcnt, j);
234
+ dot_256 = _mm256_add_epi64(dot_256, v_shifted);
235
+ }
236
+ }
237
+ dot_product += reduce_add_256(dot_256);
238
+ popcount_sum += reduce_add_256(pop_256);
239
+ }
240
+
241
+ __m128i dot_128 = _mm_setzero_si128();
242
+ __m128i pop_128 = _mm_setzero_si128();
243
+ for (size_t step = 128 / 8; offset + step <= size; offset += step) {
244
+ __m128i v_x = _mm_loadu_si128(
245
+ reinterpret_cast<const __m128i*>(data + offset));
246
+ pop_128 = _mm_add_epi64(pop_128, popcount_128_vpopcntdq(v_x));
247
+ for (size_t j = 0; j < qb; j++) {
248
+ __m128i v_q = _mm_loadu_si128(
249
+ reinterpret_cast<const __m128i*>(
250
+ query + j * size + offset));
251
+ __m128i v_and = _mm_and_si128(v_q, v_x);
252
+ __m128i v_popcnt = popcount_128_vpopcntdq(v_and);
253
+ __m128i v_shifted = _mm_slli_epi64(v_popcnt, j);
254
+ dot_128 = _mm_add_epi64(dot_128, v_shifted);
255
+ }
256
+ }
257
+ dot_product += reduce_add_128(dot_128);
258
+ popcount_sum += reduce_add_128(pop_128);
259
+
260
+ for (size_t step = 64 / 8; offset + step <= size; offset += step) {
261
+ const auto yv = *reinterpret_cast<const uint64_t*>(data + offset);
262
+ popcount_sum += popcount64(yv);
263
+ for (size_t j = 0; j < qb; j++) {
264
+ const auto qv = *reinterpret_cast<const uint64_t*>(
265
+ query + j * size + offset);
266
+ dot_product += static_cast<uint64_t>(popcount64(qv & yv)) << j;
267
+ }
268
+ }
269
+ for (; offset < size; ++offset) {
270
+ const auto yv = *(data + offset);
271
+ popcount_sum += popcount32(yv);
272
+ for (size_t j = 0; j < qb; j++) {
273
+ const auto qv = *(query + j * size + offset);
274
+ dot_product += static_cast<uint64_t>(popcount32(qv & yv)) << j;
275
+ }
276
+ }
277
+ return {dot_product, popcount_sum};
278
+ }
279
+
280
+ template <>
281
+ uint64_t bitwise_xor_dot_product<SIMDLevel::AVX512_SPR>(
282
+ const uint8_t* query,
283
+ const uint8_t* data,
284
+ size_t size,
285
+ size_t qb) {
286
+ uint64_t sum = 0;
287
+ size_t offset = 0;
288
+
289
+ if (size_t step = 512 / 8; offset + step <= size) {
290
+ __m512i sum_512 = _mm512_setzero_si512();
291
+ for (; offset + step <= size; offset += step) {
292
+ __m512i v_x = _mm512_loadu_si512(
293
+ reinterpret_cast<const __m512i*>(data + offset));
294
+ for (size_t j = 0; j < qb; j++) {
295
+ __m512i v_q = _mm512_loadu_si512(
296
+ reinterpret_cast<const __m512i*>(
297
+ query + j * size + offset));
298
+ __m512i v_xor = _mm512_xor_si512(v_q, v_x);
299
+ __m512i v_popcnt = popcount_512_vpopcntdq(v_xor);
300
+ __m512i v_shifted = _mm512_slli_epi64(v_popcnt, j);
301
+ sum_512 = _mm512_add_epi64(sum_512, v_shifted);
302
+ }
303
+ }
304
+ sum += _mm512_reduce_add_epi64(sum_512);
305
+ }
306
+
307
+ if (size_t step = 256 / 8; offset + step <= size) {
308
+ __m256i sum_256 = _mm256_setzero_si256();
309
+ for (; offset + step <= size; offset += step) {
310
+ __m256i v_x = _mm256_loadu_si256(
311
+ reinterpret_cast<const __m256i*>(data + offset));
312
+ for (size_t j = 0; j < qb; j++) {
313
+ __m256i v_q = _mm256_loadu_si256(
314
+ reinterpret_cast<const __m256i*>(
315
+ query + j * size + offset));
316
+ __m256i v_xor = _mm256_xor_si256(v_q, v_x);
317
+ __m256i v_popcnt = popcount_256_vpopcntdq(v_xor);
318
+ __m256i v_shifted = _mm256_slli_epi64(v_popcnt, j);
319
+ sum_256 = _mm256_add_epi64(sum_256, v_shifted);
320
+ }
321
+ }
322
+ sum += reduce_add_256(sum_256);
323
+ }
324
+
325
+ __m128i sum_128 = _mm_setzero_si128();
326
+ for (size_t step = 128 / 8; offset + step <= size; offset += step) {
327
+ __m128i v_x = _mm_loadu_si128(
328
+ reinterpret_cast<const __m128i*>(data + offset));
329
+ for (size_t j = 0; j < qb; j++) {
330
+ __m128i v_q = _mm_loadu_si128(
331
+ reinterpret_cast<const __m128i*>(
332
+ query + j * size + offset));
333
+ __m128i v_xor = _mm_xor_si128(v_q, v_x);
334
+ __m128i v_popcnt = popcount_128_vpopcntdq(v_xor);
335
+ __m128i v_shifted = _mm_slli_epi64(v_popcnt, j);
336
+ sum_128 = _mm_add_epi64(sum_128, v_shifted);
337
+ }
338
+ }
339
+ sum += reduce_add_128(sum_128);
340
+
341
+ for (size_t step = 64 / 8; offset + step <= size; offset += step) {
342
+ const auto yv = *reinterpret_cast<const uint64_t*>(data + offset);
343
+ for (size_t j = 0; j < qb; j++) {
344
+ const auto qv = *reinterpret_cast<const uint64_t*>(
345
+ query + j * size + offset);
346
+ sum += static_cast<uint64_t>(popcount64(qv ^ yv)) << j;
347
+ }
348
+ }
349
+ for (; offset < size; ++offset) {
350
+ const auto yv = *(data + offset);
351
+ for (size_t j = 0; j < qb; j++) {
352
+ const auto qv = *(query + j * size + offset);
353
+ sum += static_cast<uint64_t>(popcount32(qv ^ yv)) << j;
354
+ }
355
+ }
356
+ return sum;
357
+ }
358
+
359
+ template <>
360
+ uint64_t popcount<SIMDLevel::AVX512_SPR>(const uint8_t* data, size_t size) {
361
+ uint64_t sum = 0;
362
+ size_t offset = 0;
363
+
364
+ if (offset + 512 / 8 <= size) {
365
+ __m512i sum_512 = _mm512_setzero_si512();
366
+ for (size_t end; (end = offset + 512 / 8) <= size; offset = end) {
367
+ __m512i v_x = _mm512_loadu_si512(
368
+ reinterpret_cast<const __m512i*>(data + offset));
369
+ __m512i v_popcnt = popcount_512_vpopcntdq(v_x);
370
+ sum_512 = _mm512_add_epi64(sum_512, v_popcnt);
371
+ }
372
+ sum += _mm512_reduce_add_epi64(sum_512);
373
+ }
374
+
375
+ if (offset + 256 / 8 <= size) {
376
+ __m256i sum_256 = _mm256_setzero_si256();
377
+ for (size_t end; (end = offset + 256 / 8) <= size; offset = end) {
378
+ __m256i v_x = _mm256_loadu_si256(
379
+ reinterpret_cast<const __m256i*>(data + offset));
380
+ __m256i v_popcnt = popcount_256_vpopcntdq(v_x);
381
+ sum_256 = _mm256_add_epi64(sum_256, v_popcnt);
382
+ }
383
+ sum += reduce_add_256(sum_256);
384
+ }
385
+
386
+ __m128i sum_128 = _mm_setzero_si128();
387
+ for (size_t step = 128 / 8; offset + step <= size; offset += step) {
388
+ __m128i v_x = _mm_loadu_si128(
389
+ reinterpret_cast<const __m128i*>(data + offset));
390
+ sum_128 = _mm_add_epi64(sum_128, popcount_128_vpopcntdq(v_x));
391
+ }
392
+ sum += reduce_add_128(sum_128);
393
+
394
+ for (size_t step = 64 / 8; offset + step <= size; offset += step) {
395
+ const auto yv = *reinterpret_cast<const uint64_t*>(data + offset);
396
+ sum += popcount64(yv);
397
+ }
398
+ for (; offset < size; ++offset) {
399
+ const auto yv = *(data + offset);
400
+ sum += popcount32(yv);
401
+ }
402
+ return sum;
403
+ }
404
+
405
+ } // namespace faiss::rabitq
406
+
407
+ namespace faiss::rabitq::multibit {
408
+
409
+ // Forward-declare the AVX512 floating-point inner-product kernel.
410
+ // VPOPCNTDQ does not help this kernel (it operates on FP32), so we
411
+ // reuse the AVX512 implementation rather than duplicate it.
412
+ template <>
413
+ float compute_inner_product<SIMDLevel::AVX512>(
414
+ const uint8_t* __restrict sign_bits,
415
+ const uint8_t* __restrict ex_code,
416
+ const float* __restrict rotated_q,
417
+ size_t d,
418
+ size_t ex_bits,
419
+ float cb);
420
+
421
+ template <>
422
+ float compute_inner_product<SIMDLevel::AVX512_SPR>(
423
+ const uint8_t* __restrict sign_bits,
424
+ const uint8_t* __restrict ex_code,
425
+ const float* __restrict rotated_q,
426
+ size_t d,
427
+ size_t ex_bits,
428
+ float cb) {
429
+ return compute_inner_product<SIMDLevel::AVX512>(
430
+ sign_bits, ex_code, rotated_q, d, ex_bits, cb);
431
+ }
432
+
433
+ } // namespace faiss::rabitq::multibit
434
+
435
+ #endif // COMPILE_SIMD_AVX512_SPR
@@ -20,6 +20,17 @@ uint64_t bitwise_and_dot_product<SIMDLevel::ARM_NEON>(
20
20
  return bitwise_and_dot_product<SIMDLevel::NONE>(query, data, size, qb);
21
21
  }
22
22
 
23
+ template <>
24
+ BitwiseAndDotProductResult bitwise_and_dot_product_with_popcount<
25
+ SIMDLevel::ARM_NEON>(
26
+ const uint8_t* query,
27
+ const uint8_t* data,
28
+ size_t size,
29
+ size_t qb) {
30
+ return bitwise_and_dot_product_with_popcount<SIMDLevel::NONE>(
31
+ query, data, size, qb);
32
+ }
33
+
23
34
  template <>
24
35
  uint64_t bitwise_xor_dot_product<SIMDLevel::ARM_NEON>(
25
36
  const uint8_t* query,
@@ -5,19 +5,107 @@
5
5
  * LICENSE file in the root directory of this source tree.
6
6
  */
7
7
 
8
+ // -*- c++ -*-
9
+
8
10
  #include <faiss/utils/rabitq_simd.h>
9
11
 
10
12
  #ifdef COMPILE_SIMD_RISCV_RVV
11
13
 
14
+ #include <riscv_vector.h>
15
+
12
16
  namespace faiss::rabitq {
13
17
 
18
+ // SWAR per-byte popcount over a u8m4 group: each output byte holds the
19
+ // population count (0..8) of the corresponding input byte.
20
+ static inline vuint8m4_t popcount_u8m4(vuint8m4_t v, size_t vl) {
21
+ vuint8m4_t t = __riscv_vsrl_vx_u8m4(v, 1, vl);
22
+ t = __riscv_vand_vx_u8m4(t, 0x55, vl);
23
+ v = __riscv_vsub_vv_u8m4(v, t, vl);
24
+ t = __riscv_vsrl_vx_u8m4(v, 2, vl);
25
+ t = __riscv_vand_vx_u8m4(t, 0x33, vl);
26
+ v = __riscv_vand_vx_u8m4(v, 0x33, vl);
27
+ v = __riscv_vadd_vv_u8m4(v, t, vl);
28
+ t = __riscv_vsrl_vx_u8m4(v, 4, vl);
29
+ v = __riscv_vadd_vv_u8m4(v, t, vl);
30
+ return __riscv_vand_vx_u8m4(v, 0x0F, vl);
31
+ }
32
+
33
+ // Shared body for bitwise_{and,xor}_dot_product. @p combine applies the
34
+ // per-element bit op (AND or XOR) between the data and query bit-planes; the
35
+ // popcount of the result for query bit-plane j is weighted by 2^j.
36
+ template <typename Op>
37
+ static inline uint64_t bitwise_dot_product_rvv(
38
+ const uint8_t* query,
39
+ const uint8_t* data,
40
+ size_t size,
41
+ size_t qb,
42
+ Op combine) {
43
+ size_t vlmax = __riscv_vsetvlmax_e16m8();
44
+ vuint16m8_t acc = __riscv_vmv_v_x_u16m8(0, vlmax);
45
+ size_t i = 0;
46
+ while (i < size) {
47
+ size_t vl = __riscv_vsetvl_e8m4(size - i);
48
+ vuint8m4_t vx = __riscv_vle8_v_u8m4(data + i, vl);
49
+ for (size_t j = 0; j < qb; j++) {
50
+ vuint8m4_t vq = __riscv_vle8_v_u8m4(query + j * size + i, vl);
51
+ vuint8m4_t vp = popcount_u8m4(combine(vx, vq, vl), vl);
52
+ vuint16m8_t vw = __riscv_vzext_vf2_u16m8(vp, vl);
53
+ vw = __riscv_vsll_vx_u16m8(vw, j, vl);
54
+ acc = __riscv_vadd_vv_u16m8_tu(acc, acc, vw, vl);
55
+ }
56
+ i += vl;
57
+ }
58
+ vuint32m1_t red = __riscv_vmv_v_x_u32m1(0, 1);
59
+ red = __riscv_vwredsumu_vs_u16m8_u32m1(acc, red, vlmax);
60
+ return __riscv_vmv_x_s_u32m1_u32(red);
61
+ }
62
+
14
63
  template <>
15
64
  uint64_t bitwise_and_dot_product<SIMDLevel::RISCV_RVV>(
16
65
  const uint8_t* query,
17
66
  const uint8_t* data,
18
67
  size_t size,
19
68
  size_t qb) {
20
- return bitwise_and_dot_product<SIMDLevel::NONE>(query, data, size, qb);
69
+ return bitwise_dot_product_rvv(
70
+ query, data, size, qb, [](vuint8m4_t a, vuint8m4_t b, size_t vl) {
71
+ return __riscv_vand_vv_u8m4(a, b, vl);
72
+ });
73
+ }
74
+
75
+ template <>
76
+ BitwiseAndDotProductResult bitwise_and_dot_product_with_popcount<
77
+ SIMDLevel::RISCV_RVV>(
78
+ const uint8_t* query,
79
+ const uint8_t* data,
80
+ size_t size,
81
+ size_t qb) {
82
+ // Fused single pass over @p data: reuse each loaded data group for both the
83
+ // doc-side popcount and the qb AND-dot bit-planes, mirroring the separate
84
+ // popcount<RVV> and bitwise_and_dot_product<RVV> kernels above.
85
+ size_t vlmax = __riscv_vsetvlmax_e16m8();
86
+ vuint16m8_t dot_acc = __riscv_vmv_v_x_u16m8(0, vlmax);
87
+ vuint16m8_t pop_acc = __riscv_vmv_v_x_u16m8(0, vlmax);
88
+ size_t i = 0;
89
+ while (i < size) {
90
+ size_t vl = __riscv_vsetvl_e8m4(size - i);
91
+ vuint8m4_t vx = __riscv_vle8_v_u8m4(data + i, vl);
92
+ vuint16m8_t vxp = __riscv_vzext_vf2_u16m8(popcount_u8m4(vx, vl), vl);
93
+ pop_acc = __riscv_vadd_vv_u16m8_tu(pop_acc, pop_acc, vxp, vl);
94
+ for (size_t j = 0; j < qb; j++) {
95
+ vuint8m4_t vq = __riscv_vle8_v_u8m4(query + j * size + i, vl);
96
+ vuint8m4_t vp = popcount_u8m4(__riscv_vand_vv_u8m4(vx, vq, vl), vl);
97
+ vuint16m8_t vw = __riscv_vzext_vf2_u16m8(vp, vl);
98
+ vw = __riscv_vsll_vx_u16m8(vw, j, vl);
99
+ dot_acc = __riscv_vadd_vv_u16m8_tu(dot_acc, dot_acc, vw, vl);
100
+ }
101
+ i += vl;
102
+ }
103
+ vuint32m1_t dot_red = __riscv_vmv_v_x_u32m1(0, 1);
104
+ dot_red = __riscv_vwredsumu_vs_u16m8_u32m1(dot_acc, dot_red, vlmax);
105
+ vuint32m1_t pop_red = __riscv_vmv_v_x_u32m1(0, 1);
106
+ pop_red = __riscv_vwredsumu_vs_u16m8_u32m1(pop_acc, pop_red, vlmax);
107
+ return {__riscv_vmv_x_s_u32m1_u32(dot_red),
108
+ __riscv_vmv_x_s_u32m1_u32(pop_red)};
21
109
  }
22
110
 
23
111
  template <>
@@ -26,28 +114,77 @@ uint64_t bitwise_xor_dot_product<SIMDLevel::RISCV_RVV>(
26
114
  const uint8_t* data,
27
115
  size_t size,
28
116
  size_t qb) {
29
- return bitwise_xor_dot_product<SIMDLevel::NONE>(query, data, size, qb);
117
+ return bitwise_dot_product_rvv(
118
+ query, data, size, qb, [](vuint8m4_t a, vuint8m4_t b, size_t vl) {
119
+ return __riscv_vxor_vv_u8m4(a, b, vl);
120
+ });
30
121
  }
31
122
 
32
123
  template <>
33
124
  uint64_t popcount<SIMDLevel::RISCV_RVV>(const uint8_t* data, size_t size) {
34
- return popcount<SIMDLevel::NONE>(data, size);
125
+ size_t vlmax = __riscv_vsetvlmax_e16m8();
126
+ vuint16m8_t acc = __riscv_vmv_v_x_u16m8(0, vlmax);
127
+ size_t i = 0;
128
+ while (i < size) {
129
+ size_t vl = __riscv_vsetvl_e8m4(size - i);
130
+ vuint8m4_t v = popcount_u8m4(__riscv_vle8_v_u8m4(data + i, vl), vl);
131
+ vuint16m8_t vw = __riscv_vzext_vf2_u16m8(v, vl);
132
+ acc = __riscv_vadd_vv_u16m8_tu(acc, acc, vw, vl);
133
+ i += vl;
134
+ }
135
+ vuint32m1_t red = __riscv_vmv_v_x_u32m1(0, 1);
136
+ red = __riscv_vwredsumu_vs_u16m8_u32m1(acc, red, vlmax);
137
+ return __riscv_vmv_x_s_u32m1_u32(red);
35
138
  }
36
139
 
37
140
  } // namespace faiss::rabitq
38
141
 
39
142
  namespace faiss::rabitq::multibit {
40
143
 
144
+ static float ip_1exbit_rvv(
145
+ const uint8_t* __restrict sign_bits,
146
+ const uint8_t* __restrict ex_code,
147
+ const float* __restrict rotated_q,
148
+ size_t d,
149
+ float cb) {
150
+ size_t vlmax = __riscv_vsetvlmax_e32m8();
151
+ vfloat32m8_t acc = __riscv_vfmv_v_f_f32m8(0.0f, vlmax);
152
+ size_t i = 0;
153
+ while (i < d) {
154
+ size_t vl = __riscv_vsetvl_e32m8(d - i);
155
+ vbool4_t sb = __riscv_vlm_v_b4(sign_bits + i / 8, vl);
156
+ vbool4_t eb = __riscv_vlm_v_b4(ex_code + i / 8, vl);
157
+ vfloat32m8_t recon = __riscv_vfmv_v_f_f32m8(cb, vl);
158
+ recon = __riscv_vfadd_vf_f32m8_mu(sb, recon, recon, 2.0f, vl);
159
+ recon = __riscv_vfadd_vf_f32m8_mu(eb, recon, recon, 1.0f, vl);
160
+ vfloat32m8_t rq = __riscv_vle32_v_f32m8(rotated_q + i, vl);
161
+ acc = __riscv_vfmacc_vv_f32m8_tu(acc, rq, recon, vl);
162
+ i += vl;
163
+ }
164
+ vfloat32m1_t sum = __riscv_vfmv_s_f_f32m1(0.0f, 1);
165
+ sum = __riscv_vfredusum_vs_f32m8_f32m1(acc, sum, vlmax);
166
+ return __riscv_vfmv_f_s_f32m1_f32(sum);
167
+ }
168
+
169
+ // no-tree-vectorize keeps the ex_bits >= 2 tail on the existing scalar
170
+ // ip_scalar without GCC turning its 64-bit memcpy window into a vluxei64 gather
171
+ // at unaligned addresses, which SIGBUSes on strict-align RVV cores (SpacemiT
172
+ // X60).
41
173
  template <>
42
- float compute_inner_product<SIMDLevel::RISCV_RVV>(
174
+ __attribute__((optimize("no-tree-vectorize", "no-tree-slp-vectorize"))) float
175
+ compute_inner_product<SIMDLevel::RISCV_RVV>(
43
176
  const uint8_t* __restrict sign_bits,
44
177
  const uint8_t* __restrict ex_code,
45
178
  const float* __restrict rotated_q,
46
179
  size_t d,
47
180
  size_t ex_bits,
48
181
  float cb) {
49
- return compute_inner_product<SIMDLevel::NONE>(
50
- sign_bits, ex_code, rotated_q, d, ex_bits, cb);
182
+ if (ex_bits == 1) {
183
+ return ip_1exbit_rvv(sign_bits, ex_code, rotated_q, d, cb);
184
+ }
185
+ // ex_bits >= 2 needs strided bit-plane extraction (PEXT on x86); RVV has no
186
+ // cheap equivalent without Zvbb, so reuse the scalar kernel.
187
+ return ip_scalar(sign_bits, ex_code, rotated_q, 0, d, ex_bits, cb);
51
188
  }
52
189
 
53
190
  } // namespace faiss::rabitq::multibit