faiss 0.6.2 → 0.6.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (178) 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/Clustering.h +8 -0
  7. data/vendor/faiss/faiss/IVFlib.cpp +14 -3
  8. data/vendor/faiss/faiss/Index.h +2 -2
  9. data/vendor/faiss/faiss/IndexAdditiveQuantizer.cpp +9 -10
  10. data/vendor/faiss/faiss/IndexAdditiveQuantizerFastScan.cpp +2 -3
  11. data/vendor/faiss/faiss/IndexBinaryFromFloat.cpp +24 -4
  12. data/vendor/faiss/faiss/IndexBinaryHNSW.cpp +16 -145
  13. data/vendor/faiss/faiss/IndexBinaryHNSW.h +0 -6
  14. data/vendor/faiss/faiss/IndexBinaryHash.cpp +5 -9
  15. data/vendor/faiss/faiss/IndexBinaryIVF.cpp +8 -18
  16. data/vendor/faiss/faiss/IndexBinaryIVF.h +8 -1
  17. data/vendor/faiss/faiss/IndexEDEN.cpp +273 -0
  18. data/vendor/faiss/faiss/IndexEDEN.h +57 -0
  19. data/vendor/faiss/faiss/IndexFastScan.cpp +15 -4
  20. data/vendor/faiss/faiss/IndexFlat.cpp +21 -54
  21. data/vendor/faiss/faiss/IndexFlat.h +2 -2
  22. data/vendor/faiss/faiss/IndexHNSW.cpp +311 -102
  23. data/vendor/faiss/faiss/IndexHNSW.h +31 -7
  24. data/vendor/faiss/faiss/IndexIDMap.cpp +26 -8
  25. data/vendor/faiss/faiss/IndexIDMap.h +2 -0
  26. data/vendor/faiss/faiss/IndexIVF.cpp +36 -10
  27. data/vendor/faiss/faiss/IndexIVFAdditiveQuantizer.cpp +1 -1
  28. data/vendor/faiss/faiss/IndexIVFAdditiveQuantizerFastScan.cpp +3 -4
  29. data/vendor/faiss/faiss/IndexIVFEDEN.cpp +302 -0
  30. data/vendor/faiss/faiss/IndexIVFEDEN.h +70 -0
  31. data/vendor/faiss/faiss/IndexIVFFastScan.cpp +5 -6
  32. data/vendor/faiss/faiss/IndexIVFFlat.cpp +3 -4
  33. data/vendor/faiss/faiss/IndexIVFIndependentQuantizer.cpp +1 -1
  34. data/vendor/faiss/faiss/IndexIVFPQ.cpp +49 -23
  35. data/vendor/faiss/faiss/IndexIVFPQ.h +11 -0
  36. data/vendor/faiss/faiss/IndexIVFPQFastScan.cpp +0 -1
  37. data/vendor/faiss/faiss/IndexIVFRaBitQ.cpp +19 -49
  38. data/vendor/faiss/faiss/IndexIVFRaBitQFastScan.cpp +180 -76
  39. data/vendor/faiss/faiss/IndexIVFRaBitQFastScan.h +5 -4
  40. data/vendor/faiss/faiss/IndexIVFSpectralHash.cpp +8 -6
  41. data/vendor/faiss/faiss/IndexLSH.cpp +2 -3
  42. data/vendor/faiss/faiss/IndexLattice.cpp +5 -0
  43. data/vendor/faiss/faiss/IndexNNDescent.cpp +9 -2
  44. data/vendor/faiss/faiss/IndexNSG.cpp +7 -2
  45. data/vendor/faiss/faiss/IndexPQ.cpp +6 -8
  46. data/vendor/faiss/faiss/IndexPreTransform.cpp +15 -0
  47. data/vendor/faiss/faiss/IndexRaBitQ.cpp +2 -2
  48. data/vendor/faiss/faiss/IndexRaBitQFastScan.cpp +1 -2
  49. data/vendor/faiss/faiss/IndexRaBitQFastScan.h +5 -1
  50. data/vendor/faiss/faiss/IndexRefine.cpp +30 -1
  51. data/vendor/faiss/faiss/IndexReplicas.cpp +1 -2
  52. data/vendor/faiss/faiss/IndexShards.cpp +5 -5
  53. data/vendor/faiss/faiss/IndexShardsIVF.cpp +6 -5
  54. data/vendor/faiss/faiss/MetaIndexes.cpp +2 -4
  55. data/vendor/faiss/faiss/SuperKMeans.cpp +286 -247
  56. data/vendor/faiss/faiss/SuperKMeans.h +33 -2
  57. data/vendor/faiss/faiss/VectorTransform.cpp +71 -2
  58. data/vendor/faiss/faiss/VectorTransform.h +3 -0
  59. data/vendor/faiss/faiss/clone_index.cpp +8 -0
  60. data/vendor/faiss/faiss/factory_tools.cpp +47 -4
  61. data/vendor/faiss/faiss/gpu/GpuCloner.cpp +11 -11
  62. data/vendor/faiss/faiss/gpu/GpuClonerOptions.h +1 -5
  63. data/vendor/faiss/faiss/gpu/GpuDistance.h +2 -5
  64. data/vendor/faiss/faiss/gpu/GpuIndex.h +38 -16
  65. data/vendor/faiss/faiss/gpu/GpuIndexCagra.h +71 -1
  66. data/vendor/faiss/faiss/gpu/GpuIndexIVF.h +17 -0
  67. data/vendor/faiss/faiss/gpu/GpuIndexIVFScalarQuantizer.h +16 -0
  68. data/vendor/faiss/faiss/gpu/perf/PerfClustering.cpp +1 -1
  69. data/vendor/faiss/faiss/gpu/perf/PerfIVFPQAdd.cpp +2 -2
  70. data/vendor/faiss/faiss/gpu/test/TestGpuIndexIVFScalarQuantizer.cpp +180 -0
  71. data/vendor/faiss/faiss/gpu_metal/MetalIndexIVFFlat.h +1 -5
  72. data/vendor/faiss/faiss/gpu_metal/MetalIndexIVFPQ.h +88 -0
  73. data/vendor/faiss/faiss/gpu_metal/impl/MetalIVFPQ.h +134 -0
  74. data/vendor/faiss/faiss/impl/AdditiveQuantizer.cpp +1 -1
  75. data/vendor/faiss/faiss/impl/ClusteringInitialization.cpp +7 -4
  76. data/vendor/faiss/faiss/impl/DistanceComputer.h +34 -0
  77. data/vendor/faiss/faiss/impl/EDENQuantizer.h +119 -0
  78. data/vendor/faiss/faiss/impl/HNSW.cpp +528 -267
  79. data/vendor/faiss/faiss/impl/HNSW.h +46 -7
  80. data/vendor/faiss/faiss/impl/IDSelector.h +44 -0
  81. data/vendor/faiss/faiss/impl/LocalSearchQuantizer.cpp +2 -2
  82. data/vendor/faiss/faiss/impl/NNDescent.cpp +10 -3
  83. data/vendor/faiss/faiss/impl/NSG.cpp +3 -1
  84. data/vendor/faiss/faiss/impl/Panorama.h +20 -9
  85. data/vendor/faiss/faiss/impl/PolysemousTraining.cpp +152 -84
  86. data/vendor/faiss/faiss/impl/ProductQuantizer.cpp +38 -26
  87. data/vendor/faiss/faiss/impl/RaBitQUtils.cpp +45 -37
  88. data/vendor/faiss/faiss/impl/RaBitQUtils.h +35 -0
  89. data/vendor/faiss/faiss/impl/RaBitQuantizer.cpp +239 -72
  90. data/vendor/faiss/faiss/impl/RaBitQuantizer.h +66 -4
  91. data/vendor/faiss/faiss/impl/RaBitQuantizerMultiBit.cpp +4 -13
  92. data/vendor/faiss/faiss/impl/ResultHandler.h +34 -34
  93. data/vendor/faiss/faiss/impl/ScalarQuantizer.cpp +287 -84
  94. data/vendor/faiss/faiss/impl/ScalarQuantizer.h +26 -10
  95. data/vendor/faiss/faiss/impl/ThreadedIndex-inl.h +2 -2
  96. data/vendor/faiss/faiss/impl/VisitedTable.cpp +22 -2
  97. data/vendor/faiss/faiss/impl/VisitedTable.h +20 -0
  98. data/vendor/faiss/faiss/impl/binary_hamming/IndexBinaryIVF_impl.h +90 -14
  99. data/vendor/faiss/faiss/impl/binary_hamming/avx2.cpp +4 -4
  100. data/vendor/faiss/faiss/impl/expanded_scanners.h +5 -1
  101. data/vendor/faiss/faiss/impl/fast_scan/decompose_qbs.h +1 -0
  102. data/vendor/faiss/faiss/impl/fast_scan/dispatching.h +35 -2
  103. data/vendor/faiss/faiss/impl/hnsw/LockVector.cpp +1 -1
  104. data/vendor/faiss/faiss/impl/index_read.cpp +491 -50
  105. data/vendor/faiss/faiss/impl/index_write.cpp +86 -30
  106. data/vendor/faiss/faiss/impl/lattice_Zn.cpp +8 -9
  107. data/vendor/faiss/faiss/impl/platform_macros.h +3 -1
  108. data/vendor/faiss/faiss/impl/polysemous_training/avx512.cpp +284 -0
  109. data/vendor/faiss/faiss/impl/polysemous_training/dispatch.h +115 -0
  110. data/vendor/faiss/faiss/impl/pq_code_distance/IVFPQScanner_impl.h +73 -39
  111. data/vendor/faiss/faiss/impl/pq_code_distance/IVFPQ_QueryTables.cpp +0 -1
  112. data/vendor/faiss/faiss/impl/pq_code_distance/PQDistanceComputer_impl.h +26 -15
  113. data/vendor/faiss/faiss/impl/pq_code_distance/avx2.cpp +4 -4
  114. data/vendor/faiss/faiss/impl/pq_code_distance/pq_code_distance-generic.cpp +4 -4
  115. data/vendor/faiss/faiss/impl/result_handler/ResultHandler.cpp +195 -0
  116. data/vendor/faiss/faiss/impl/result_handler/avx2.cpp +133 -0
  117. data/vendor/faiss/faiss/impl/result_handler/avx512.cpp +281 -0
  118. data/vendor/faiss/faiss/impl/scalar_quantizer/EDENQuantizer-avx2.cpp +72 -0
  119. data/vendor/faiss/faiss/impl/scalar_quantizer/EDENQuantizer-avx512.cpp +228 -0
  120. data/vendor/faiss/faiss/impl/scalar_quantizer/EDENQuantizer.cpp +887 -0
  121. data/vendor/faiss/faiss/impl/scalar_quantizer/distance_computers.h +2 -2
  122. data/vendor/faiss/faiss/impl/scalar_quantizer/quantizers.h +9 -8
  123. data/vendor/faiss/faiss/impl/scalar_quantizer/sq-avx2.cpp +90 -24
  124. data/vendor/faiss/faiss/impl/scalar_quantizer/sq-avx512-impl.h +30 -30
  125. data/vendor/faiss/faiss/impl/scalar_quantizer/sq-avx512-spr.cpp +4 -5
  126. data/vendor/faiss/faiss/impl/scalar_quantizer/sq-avx512.cpp +101 -34
  127. data/vendor/faiss/faiss/impl/scalar_quantizer/sq-dispatch.h +169 -13
  128. data/vendor/faiss/faiss/impl/scalar_quantizer/sq-neon.cpp +125 -26
  129. data/vendor/faiss/faiss/impl/simd_dispatch.h +70 -31
  130. data/vendor/faiss/faiss/index_factory.cpp +40 -7
  131. data/vendor/faiss/faiss/invlists/DirectMap.cpp +1 -1
  132. data/vendor/faiss/faiss/invlists/InvertedLists.cpp +9 -6
  133. data/vendor/faiss/faiss/invlists/OnDiskInvertedLists.cpp +29 -8
  134. data/vendor/faiss/faiss/python/python_callbacks.cpp +3 -1
  135. data/vendor/faiss/faiss/svs/IndexSVSFaissUtils.h +60 -0
  136. data/vendor/faiss/faiss/svs/IndexSVSFlat.cpp +26 -1
  137. data/vendor/faiss/faiss/svs/IndexSVSFlat.h +13 -0
  138. data/vendor/faiss/faiss/svs/IndexSVSIVF.cpp +1 -1
  139. data/vendor/faiss/faiss/svs/IndexSVSIVFLeanVec.cpp +1 -1
  140. data/vendor/faiss/faiss/svs/IndexSVSVamana.cpp +47 -5
  141. data/vendor/faiss/faiss/svs/IndexSVSVamana.h +23 -3
  142. data/vendor/faiss/faiss/svs/IndexSVSVamanaLVQ.cpp +4 -2
  143. data/vendor/faiss/faiss/svs/IndexSVSVamanaLVQ.h +2 -1
  144. data/vendor/faiss/faiss/svs/IndexSVSVamanaLeanVec.cpp +10 -4
  145. data/vendor/faiss/faiss/svs/IndexSVSVamanaLeanVec.h +2 -1
  146. data/vendor/faiss/faiss/utils/approx_topk_hamming/approx_topk_hamming.h +1 -1
  147. data/vendor/faiss/faiss/utils/distances.cpp +30 -11
  148. data/vendor/faiss/faiss/utils/distances_dispatch.h +30 -24
  149. data/vendor/faiss/faiss/utils/distances_fused/distances_fused.cpp +1 -1
  150. data/vendor/faiss/faiss/utils/distances_simd.cpp +4 -3
  151. data/vendor/faiss/faiss/utils/extra_distances.cpp +4 -14
  152. data/vendor/faiss/faiss/utils/extra_distances.h +1 -2
  153. data/vendor/faiss/faiss/utils/hamming.cpp +16 -10
  154. data/vendor/faiss/faiss/utils/hamming.h +10 -1
  155. data/vendor/faiss/faiss/utils/hamming_distance/common.h +14 -3
  156. data/vendor/faiss/faiss/utils/hamming_distance/hamming_avx512_vpopcnt.cpp +24 -0
  157. data/vendor/faiss/faiss/utils/hamming_distance/hamming_computer-avx512.h +1 -1
  158. data/vendor/faiss/faiss/utils/hamming_distance/{hamming_computer-avx512_spr.h → hamming_computer-avx512_vpopcnt.h} +85 -24
  159. data/vendor/faiss/faiss/utils/hamming_distance/hamming_impl.h +141 -0
  160. data/vendor/faiss/faiss/utils/quantize_lut.cpp +29 -8
  161. data/vendor/faiss/faiss/utils/rabitq_simd.h +202 -0
  162. data/vendor/faiss/faiss/utils/simd_impl/distances_arm_sve.cpp +194 -30
  163. data/vendor/faiss/faiss/utils/simd_impl/distances_avx2.cpp +0 -1
  164. data/vendor/faiss/faiss/utils/simd_impl/distances_avx512.cpp +263 -15
  165. data/vendor/faiss/faiss/utils/simd_impl/distances_rvv.cpp +198 -18
  166. data/vendor/faiss/faiss/utils/simd_impl/rabitq_avx2.cpp +245 -0
  167. data/vendor/faiss/faiss/utils/simd_impl/rabitq_avx512.cpp +330 -40
  168. data/vendor/faiss/faiss/utils/simd_impl/{rabitq_avx512_spr.cpp → rabitq_avx512_vpopcnt.cpp} +112 -23
  169. data/vendor/faiss/faiss/utils/simd_impl/rabitq_neon.cpp +11 -0
  170. data/vendor/faiss/faiss/utils/simd_impl/rabitq_rvv.cpp +143 -6
  171. data/vendor/faiss/faiss/utils/simd_impl/super_kmeans_dispatch.h +2 -7
  172. data/vendor/faiss/faiss/utils/simd_impl/super_kmeans_kernels.h +6 -1
  173. data/vendor/faiss/faiss/utils/simd_impl/super_kmeans_kernels_sve.cpp +34 -0
  174. data/vendor/faiss/faiss/utils/simd_levels.cpp +196 -47
  175. data/vendor/faiss/faiss/utils/simd_levels.h +33 -8
  176. data/vendor/faiss/faiss/utils/utils.cpp +9 -27
  177. metadata +21 -5
  178. data/vendor/faiss/faiss/utils/hamming_distance/hamming_avx512_spr.cpp +0 -15
@@ -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
@@ -9,13 +9,8 @@
9
9
 
10
10
  // Private dispatch wrapper for SuperKMeans's block_l2. Routes to the
11
11
  // highest available SIMD specialization at runtime (DD mode) or the
12
- // compiled-in level (static mode). aarch64 currently falls through to the
13
- // scalar primary template; adding NEON/SVE means just adding a new
14
- // specialization file alongside the AVX ones.
12
+ // compiled-in level (static mode).
15
13
  //
16
- // Known perf gap: aarch64 (NEON/SVE) specializations are not implemented yet.
17
- // aarch64 falls through to the scalar primary template. Validating SVE requires
18
- // a Graviton-class host; deferred to a focused follow-up.
19
14
 
20
15
  #include <faiss/impl/simd_dispatch.h>
21
16
  #include <faiss/utils/simd_impl/super_kmeans_kernels.h>
@@ -24,7 +19,7 @@ namespace faiss {
24
19
  namespace detail {
25
20
 
26
21
  inline float block_l2_dispatch(const float* x, const float* y, int n) {
27
- return with_selected_simd_levels<AVAILABLE_SIMD_LEVELS_A0>(
22
+ return with_simd_level_with_sve(
28
23
  [&]<SIMDLevel SL>() { return block_l2<SL>(x, y, n); });
29
24
  }
30
25
 
@@ -16,7 +16,7 @@ namespace detail {
16
16
 
17
17
  // Squared L2 over `n` dimensions; n in [1, pdx_block_size].
18
18
  // Primary template is the scalar fallback; SIMDLevels without a dedicated
19
- // specialization (ARM_NEON, ARM_SVE, NONE, ...) use it directly.
19
+ // specialization (ARM_NEON, NONE, ...) use it directly.
20
20
  template <SIMDLevel Level>
21
21
  inline float block_l2(const float* x, const float* y, int n) {
22
22
  float s = 0.0f;
@@ -39,5 +39,10 @@ template <>
39
39
  float block_l2<SIMDLevel::AVX512>(const float* x, const float* y, int n);
40
40
  #endif
41
41
 
42
+ #ifdef COMPILE_SIMD_ARM_SVE
43
+ template <>
44
+ float block_l2<SIMDLevel::ARM_SVE>(const float* x, const float* y, int n);
45
+ #endif
46
+
42
47
  } // namespace detail
43
48
  } // namespace faiss
@@ -0,0 +1,34 @@
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
+ #ifdef COMPILE_SIMD_ARM_SVE
9
+
10
+ #include <faiss/utils/simd_impl/super_kmeans_kernels.h>
11
+
12
+ #include <arm_sve.h>
13
+
14
+ namespace faiss {
15
+ namespace detail {
16
+
17
+ template <>
18
+ float block_l2<SIMDLevel::ARM_SVE>(const float* x, const float* y, int n) {
19
+ svfloat32_t acc = svdup_n_f32(0.0f);
20
+ const int lanes = static_cast<int>(svcntw());
21
+ for (int m = 0; m < n; m += lanes) {
22
+ const svbool_t pg = svwhilelt_b32(m, n);
23
+ const svfloat32_t xv = svld1_f32(pg, x + m);
24
+ const svfloat32_t yv = svld1_f32(pg, y + m);
25
+ const svfloat32_t diff = svsub_f32_x(pg, xv, yv);
26
+ acc = svmla_f32_m(pg, acc, diff, diff);
27
+ }
28
+ return svaddv_f32(svptrue_b32(), acc);
29
+ }
30
+
31
+ } // namespace detail
32
+ } // namespace faiss
33
+
34
+ #endif // COMPILE_SIMD_ARM_SVE
@@ -7,8 +7,15 @@
7
7
 
8
8
  #include <faiss/utils/simd_levels.h>
9
9
 
10
+ #include <cstdint>
11
+ #include <cstdio>
10
12
  #include <cstdlib>
11
13
 
14
+ #if defined(_MSC_VER)
15
+ // __cpuidex, _xgetbv
16
+ #include <intrin.h>
17
+ #endif
18
+
12
19
  #include <faiss/impl/FaissAssert.h>
13
20
  #include <faiss/impl/simd_dispatch.h>
14
21
 
@@ -20,6 +27,20 @@ SIMDLevel SIMDConfig::level = SIMDLevel::NONE;
20
27
  // Bitmask of supported SIMD levels (1 << SIMDLevel)
21
28
  uint64_t SIMDConfig::supported_simd_levels = 0;
22
29
 
30
+ // Microarchitecture flags (x86). Default false; set by
31
+ // detect_x86_uarch_flags() at load time.
32
+ bool SIMDConfig::avx512_split = false;
33
+
34
+ // Resolved here rather than in the header so that dependents, which never see
35
+ // FAISS_ENABLE_DD, still get the answer for the faiss they link against.
36
+ bool SIMDConfig::has_dynamic_dispatch() {
37
+ #ifdef FAISS_ENABLE_DD
38
+ return true;
39
+ #else
40
+ return false;
41
+ #endif
42
+ }
43
+
23
44
  // ARM SVE runtime detection
24
45
  #if defined(__aarch64__) || defined(_M_ARM64)
25
46
 
@@ -53,11 +74,110 @@ static bool has_sve() {
53
74
  }
54
75
  #endif
55
76
 
56
- #ifdef FAISS_ENABLE_DD
77
+ namespace {
57
78
 
58
- // =============================================================================
59
- // Dynamic Dispatch (DD) mode implementation
60
- // =============================================================================
79
+ #if defined(__x86_64__) || defined(_M_X64)
80
+
81
+ // MSVC and clang-cl do not support GNU-style
82
+ // 64-bit inline assembly, MSVC defines _M_X64 instead of __x86_64__
83
+
84
+ #if defined(_MSC_VER)
85
+
86
+ [[maybe_unused]] void cpuid_count(
87
+ unsigned int leaf,
88
+ unsigned int subleaf,
89
+ unsigned int regs[4]) {
90
+ int r[4];
91
+ __cpuidex(r, static_cast<int>(leaf), static_cast<int>(subleaf));
92
+ for (int i = 0; i < 4; i++) {
93
+ regs[i] = static_cast<unsigned int>(r[i]);
94
+ }
95
+ }
96
+
97
+ [[maybe_unused]] uint64_t xgetbv0() {
98
+ return static_cast<uint64_t>(_xgetbv(0));
99
+ }
100
+
101
+ #else // GCC / Clang
102
+
103
+ [[maybe_unused]] void cpuid_count(
104
+ unsigned int leaf,
105
+ unsigned int subleaf,
106
+ unsigned int regs[4]) {
107
+ asm volatile("cpuid"
108
+ : "=a"(regs[0]), "=b"(regs[1]), "=c"(regs[2]), "=d"(regs[3])
109
+ : "a"(leaf), "c"(subleaf));
110
+ }
111
+
112
+ [[maybe_unused]] uint64_t xgetbv0() {
113
+ unsigned int eax, edx;
114
+ asm volatile("xgetbv" : "=a"(eax), "=d"(edx) : "c"(0));
115
+ return eax | (static_cast<uint64_t>(edx) << 32);
116
+ }
117
+
118
+ #endif // _MSC_VER
119
+
120
+ // Detect x86 microarchitecture flags used for kernel routing. Uses raw
121
+ // cpuid so it is safe to run on any CPU regardless of compiled SIMD level.
122
+ void detect_x86_uarch_flags() {
123
+ unsigned int regs[4];
124
+
125
+ // Vendor string (CPUID.0): "AuthenticAMD" is EBX="Auth", EDX="enti",
126
+ // ECX="cAMD".
127
+ cpuid_count(0, 0, regs);
128
+ const bool is_amd = regs[1] == 0x68747541u && regs[3] == 0x69746e65u &&
129
+ regs[2] == 0x444d4163u;
130
+
131
+ // Family/model (CPUID.1 EAX).
132
+ cpuid_count(1, 0, regs);
133
+ const unsigned int eax1 = regs[0];
134
+ const unsigned int base_family = (eax1 >> 8) & 0xfu;
135
+ const unsigned int display_family =
136
+ base_family + (base_family == 0xfu ? ((eax1 >> 20) & 0xffu) : 0u);
137
+ // AMD Zen 4 / Zen 4c (Bergamo) is family 0x19 and splits AVX-512.
138
+ // (Zen 5, family 0x1A, has a native 512-bit datapath and is excluded.)
139
+ SIMDConfig::avx512_split = is_amd && display_family == 0x19u;
140
+ }
141
+
142
+ #else // Not x86-64
143
+
144
+ void detect_x86_uarch_flags() {}
145
+
146
+ #endif // defined(__x86_64__) || defined(_M_X64)
147
+
148
+ } // namespace
149
+
150
+ /// Must mirror the case labels in with_selected_simd_levels. A static build
151
+ /// defines a COMPILE_SIMD_* macro for every level whose sources it compiles,
152
+ /// not only for SINGLE_SIMD_LEVEL, so this reports a correct set in both
153
+ /// modes and the result is not a single level.
154
+ uint64_t compiled_simd_levels() {
155
+ uint64_t mask = uint64_t(1) << static_cast<int>(SIMDLevel::NONE);
156
+ #ifdef COMPILE_SIMD_AVX2
157
+ mask |= uint64_t(1) << static_cast<int>(SIMDLevel::AVX2);
158
+ #endif
159
+ #ifdef COMPILE_SIMD_AVX512
160
+ mask |= uint64_t(1) << static_cast<int>(SIMDLevel::AVX512);
161
+ #endif
162
+ #ifdef COMPILE_SIMD_AVX512_VPOPCNT
163
+ mask |= uint64_t(1) << static_cast<int>(SIMDLevel::AVX512_VPOPCNT);
164
+ #endif
165
+ #ifdef COMPILE_SIMD_AVX512_SPR
166
+ mask |= uint64_t(1) << static_cast<int>(SIMDLevel::AVX512_SPR);
167
+ #endif
168
+ #ifdef COMPILE_SIMD_ARM_NEON
169
+ mask |= uint64_t(1) << static_cast<int>(SIMDLevel::ARM_NEON);
170
+ #endif
171
+ #ifdef COMPILE_SIMD_ARM_SVE
172
+ mask |= uint64_t(1) << static_cast<int>(SIMDLevel::ARM_SVE);
173
+ #endif
174
+ #ifdef COMPILE_SIMD_RISCV_RVV
175
+ mask |= uint64_t(1) << static_cast<int>(SIMDLevel::RISCV_RVV);
176
+ #endif
177
+ return mask;
178
+ }
179
+
180
+ #ifdef FAISS_ENABLE_DD
61
181
 
62
182
  // Static initializer to run constructor at load time
63
183
  // NOLINTNEXTLINE(facebook-avoid-non-const-global-variables)
@@ -71,7 +191,22 @@ SIMDConfig::SIMDConfig(const char** faiss_simd_level_env) {
71
191
  if (!env_var) {
72
192
  level = auto_detect_simd_level();
73
193
  } else {
74
- level = to_simd_level(env_var);
194
+ // Forcing a level the CPU lacks is allowed. Forcing one the binary
195
+ // does not hold is not: dispatch would fall to NONE and skip every
196
+ // level between. Walk down to the nearest compiled level instead.
197
+ const uint64_t compiled = compiled_simd_levels();
198
+ const SIMDLevel requested = to_simd_level(env_var);
199
+ level = requested;
200
+ while (((compiled >> static_cast<int>(level)) & 1) == 0) {
201
+ level = get_simd_fallback(level);
202
+ }
203
+ if (level != requested) {
204
+ fprintf(stderr,
205
+ "faiss: FAISS_SIMD_LEVEL=%s is not compiled into this "
206
+ "build, using %s instead\n",
207
+ to_string(requested).c_str(),
208
+ to_string(level).c_str());
209
+ }
75
210
  supported_simd_levels = (1 << static_cast<int>(level));
76
211
  }
77
212
  supported_simd_levels |= (1 << static_cast<int>(SIMDLevel::NONE));
@@ -101,80 +236,86 @@ bool SIMDConfig::is_simd_level_available(SIMDLevel l) {
101
236
  SIMDLevel SIMDConfig::auto_detect_simd_level() {
102
237
  SIMDLevel detected_level = SIMDLevel::NONE;
103
238
 
104
- #if defined(__x86_64__) && \
239
+ detect_x86_uarch_flags();
240
+
241
+ #if (defined(__x86_64__) || defined(_M_X64)) && \
105
242
  (defined(COMPILE_SIMD_AVX2) || defined(COMPILE_SIMD_AVX512))
106
- unsigned int eax, ebx, ecx, edx;
243
+ unsigned int regs[4];
107
244
 
108
- eax = 1;
109
- ecx = 0;
110
- asm volatile("cpuid"
111
- : "=a"(eax), "=b"(ebx), "=c"(ecx), "=d"(edx)
112
- : "a"(eax), "c"(ecx));
245
+ cpuid_count(1, 0, regs);
246
+ unsigned int ecx1 = regs[2];
113
247
 
114
- bool has_avx = (ecx & (1 << 28)) != 0;
248
+ bool has_avx = (ecx1 & (1 << 28)) != 0;
115
249
 
116
250
  bool has_xsave_osxsave =
117
- (ecx & ((1 << 26) | (1 << 27))) == ((1 << 26) | (1 << 27));
251
+ (ecx1 & ((1 << 26) | (1 << 27))) == ((1 << 26) | (1 << 27));
118
252
 
119
253
  bool avx_supported = false;
120
254
  if (has_avx && has_xsave_osxsave) {
121
- unsigned int xcr0;
122
- asm volatile("xgetbv" : "=a"(xcr0), "=d"(edx) : "c"(0));
123
- avx_supported = (xcr0 & 6) == 6;
255
+ avx_supported = (xgetbv0() & 6) == 6;
124
256
  }
125
257
 
126
258
  if (avx_supported) {
127
- eax = 7;
128
- ecx = 0;
129
- asm volatile("cpuid"
130
- : "=a"(eax), "=b"(ebx), "=c"(ecx), "=d"(edx)
131
- : "a"(eax), "c"(ecx));
132
- // Save EDX before xgetbv clobbers it — needed for
133
- // AVX512_FP16 check (bit 23) in the SPR detection below.
134
- unsigned int cpuid7_edx = edx;
135
-
136
- unsigned int xcr0;
137
- asm volatile("xgetbv" : "=a"(xcr0), "=d"(edx) : "c"(0));
259
+ cpuid_count(7, 0, regs);
260
+ unsigned int ebx7 = regs[1];
261
+ // EDX of CPUID leaf 7 subleaf 0 carries AVX512_FP16 (bit 23),
262
+ // needed for the SPR detection below. Kept in a local so a later
263
+ // xgetbv cannot clobber it.
264
+ unsigned int cpuid7_edx = regs[3];
265
+ // Leaf 7 subleaf 0, not leaf 1: leaf 1 ECX holds unrelated bits at
266
+ // these positions.
267
+ unsigned int ecx7 = regs[2];
268
+ [[maybe_unused]] bool has_avx512_vnni = (ecx7 & (1 << 11)) != 0;
269
+ [[maybe_unused]] bool has_avx512_vpopcntdq = (ecx7 & (1 << 14)) != 0;
270
+ // Bit 12 = AVX512_BITALG, needed for the byte-wise popcount kernels.
271
+ [[maybe_unused]] bool has_avx512_bitalg = (ecx7 & (1 << 12)) != 0;
272
+
273
+ uint64_t xcr0 = xgetbv0();
138
274
 
139
275
  #if defined(COMPILE_SIMD_AVX2) || defined(COMPILE_SIMD_AVX512)
140
- bool has_avx2 = (ebx & (1 << 5)) != 0;
276
+ bool has_avx2 = (ebx7 & (1 << 5)) != 0;
141
277
  if (has_avx2) {
142
278
  supported_simd_levels |= (1 << static_cast<int>(SIMDLevel::AVX2));
143
279
  detected_level = SIMDLevel::AVX2;
144
280
  }
145
281
 
146
282
  #if defined(COMPILE_SIMD_AVX512)
147
- bool cpu_has_avx512f = (ebx & (1 << 16)) != 0;
283
+ bool cpu_has_avx512f = (ebx7 & (1 << 16)) != 0;
148
284
  bool os_supports_avx512 = (xcr0 & 0xE0) == 0xE0;
149
285
  bool has_avx512f = cpu_has_avx512f && os_supports_avx512;
150
286
  if (has_avx512f) {
151
- bool has_avx512cd = (ebx & (1 << 28)) != 0;
152
- bool has_avx512vl = (ebx & (1 << 31)) != 0;
153
- bool has_avx512dq = (ebx & (1 << 17)) != 0;
154
- bool has_avx512bw = (ebx & (1 << 30)) != 0;
287
+ bool has_avx512cd = (ebx7 & (1 << 28)) != 0;
288
+ bool has_avx512vl = (ebx7 & (1 << 31)) != 0;
289
+ bool has_avx512dq = (ebx7 & (1 << 17)) != 0;
290
+ bool has_avx512bw = (ebx7 & (1 << 30)) != 0;
155
291
  if (has_avx512bw && has_avx512cd && has_avx512vl && has_avx512dq) {
156
292
  detected_level = SIMDLevel::AVX512;
157
293
  supported_simd_levels |=
158
294
  (1 << static_cast<int>(SIMDLevel::AVX512));
159
295
 
296
+ #if defined(COMPILE_SIMD_AVX512_VPOPCNT)
297
+ if (has_avx512_vpopcntdq && has_avx512_bitalg) {
298
+ detected_level = SIMDLevel::AVX512_VPOPCNT;
299
+ supported_simd_levels |=
300
+ (1 << static_cast<int>(SIMDLevel::AVX512_VPOPCNT));
301
+ }
302
+ #endif
303
+
160
304
  #if defined(COMPILE_SIMD_AVX512_SPR)
161
305
  // Check for Sapphire Rapids features.
162
- // The SPR code path is compiled with -mavx512fp16, so we
163
- // must verify both AVX512_BF16 and AVX512_FP16 before
164
- // dispatching to it. AMD Zen 4 (bergamo) has BF16 but
165
- // not FP16 — using SPR code there causes SIGILL.
306
+ // The SPR code path is compiled with AVX512_VNNI, BF16,
307
+ // FP16 and VPOPCNTDQ, and falls back to the VPOPCNT kernels,
308
+ // which need BITALG. All five features are required.
309
+ // AMD Zen 4 has VPOPCNTDQ and BF16 but not FP16, and must
310
+ // remain on the AVX512_VPOPCNT level.
166
311
  // CPUID EAX=7, ECX=1: EAX bit 5 = AVX512_BF16
167
312
  // CPUID EAX=7, ECX=0: EDX bit 23 = AVX512_FP16
168
313
  // (Linux: X86_FEATURE_AVX512_FP16 = 18*32+23)
169
314
  bool has_avx512_fp16 = (cpuid7_edx & (1 << 23)) != 0;
170
- unsigned int eax1, ebx1, ecx1, edx1;
171
- eax1 = 7;
172
- ecx1 = 1;
173
- asm volatile("cpuid"
174
- : "=a"(eax1), "=b"(ebx1), "=c"(ecx1), "=d"(edx1)
175
- : "a"(eax1), "c"(ecx1));
176
- bool has_avx512_bf16 = (eax1 & (1 << 5)) != 0;
177
- if (has_avx512_bf16 && has_avx512_fp16) {
315
+ cpuid_count(7, 1, regs);
316
+ const bool has_avx512_bf16 = (regs[0] & (1 << 5)) != 0;
317
+ if (has_avx512_vnni && has_avx512_vpopcntdq &&
318
+ has_avx512_bitalg && has_avx512_bf16 && has_avx512_fp16) {
178
319
  detected_level = SIMDLevel::AVX512_SPR;
179
320
  supported_simd_levels |=
180
321
  (1 << static_cast<int>(SIMDLevel::AVX512_SPR));
@@ -185,7 +326,7 @@ SIMDLevel SIMDConfig::auto_detect_simd_level() {
185
326
  #endif // defined(COMPILE_SIMD_AVX512)
186
327
  #endif // defined(COMPILE_SIMD_AVX2) || defined(COMPILE_SIMD_AVX512)
187
328
  }
188
- #endif // defined(__x86_64__) && ...
329
+ #endif // defined(__x86_64__) || defined(_M_X64)
189
330
 
190
331
  #ifdef COMPILE_SIMD_ARM_NEON
191
332
  // ARM NEON is standard on aarch64
@@ -264,9 +405,12 @@ bool SIMDConfig::is_simd_level_available(SIMDLevel l) {
264
405
  }
265
406
 
266
407
  SIMDLevel SIMDConfig::auto_detect_simd_level() {
408
+ detect_x86_uarch_flags();
267
409
  // In static mode, return the compiled-in level
268
410
  #if defined(COMPILE_SIMD_AVX512_SPR)
269
411
  return SIMDLevel::AVX512_SPR;
412
+ #elif defined(COMPILE_SIMD_AVX512_VPOPCNT)
413
+ return SIMDLevel::AVX512_VPOPCNT;
270
414
  #elif defined(COMPILE_SIMD_AVX512)
271
415
  return SIMDLevel::AVX512;
272
416
  #elif defined(COMPILE_SIMD_AVX2)
@@ -301,6 +445,8 @@ std::string to_string(SIMDLevel level) {
301
445
  return "AVX2";
302
446
  case SIMDLevel::AVX512:
303
447
  return "AVX512";
448
+ case SIMDLevel::AVX512_VPOPCNT:
449
+ return "AVX512_VPOPCNT";
304
450
  case SIMDLevel::AVX512_SPR:
305
451
  return "AVX512_SPR";
306
452
  case SIMDLevel::ARM_NEON:
@@ -325,6 +471,9 @@ SIMDLevel to_simd_level(const std::string& level_str) {
325
471
  if (level_str == "AVX512") {
326
472
  return SIMDLevel::AVX512;
327
473
  }
474
+ if (level_str == "AVX512_VPOPCNT") {
475
+ return SIMDLevel::AVX512_VPOPCNT;
476
+ }
328
477
  if (level_str == "AVX512_SPR") {
329
478
  return SIMDLevel::AVX512_SPR;
330
479
  }