usearch 0.1.0

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 (90) hide show
  1. package/.clang-format +10 -0
  2. package/.github/workflows/package.json +82 -0
  3. package/.github/workflows/release.yml +111 -0
  4. package/.gitmodules +6 -0
  5. package/.vscode/launch.json +116 -0
  6. package/.vscode/settings.json +109 -0
  7. package/.vscode/tasks.json +35 -0
  8. package/CMakeLists.txt +84 -0
  9. package/CMakeListsInstructuons.md +109 -0
  10. package/README.md +69 -0
  11. package/VERSION +1 -0
  12. package/assets/unum.png +0 -0
  13. package/binding.gyp +28 -0
  14. package/build.rs +14 -0
  15. package/cargo.toml +15 -0
  16. package/cmake/clipp.cmake +8 -0
  17. package/cmake/jemalloc.cmake +80 -0
  18. package/docs/Makefile +20 -0
  19. package/docs/_static/custom.css +26 -0
  20. package/docs/_static/custom.js +7 -0
  21. package/docs/conf.py +48 -0
  22. package/docs/index.rst +10 -0
  23. package/fp16/CMakeLists.txt +176 -0
  24. package/fp16/LICENSE +11 -0
  25. package/fp16/README.md +20 -0
  26. package/fp16/bench/alt-element.cc +89 -0
  27. package/fp16/bench/from-alt-array.cc +175 -0
  28. package/fp16/bench/from-ieee-array.cc +399 -0
  29. package/fp16/bench/ieee-element.cc +272 -0
  30. package/fp16/bench/to-alt-array.cc +91 -0
  31. package/fp16/bench/to-ieee-array.cc +303 -0
  32. package/fp16/cmake/DownloadGoogleBenchmark.cmake +15 -0
  33. package/fp16/cmake/DownloadGoogleTest.cmake +15 -0
  34. package/fp16/cmake/DownloadPSimd.cmake +15 -0
  35. package/fp16/configure.py +65 -0
  36. package/fp16/confu.yaml +8 -0
  37. package/fp16/include/fp16/__init__.py +0 -0
  38. package/fp16/include/fp16/avx.py +50 -0
  39. package/fp16/include/fp16/avx2.py +53 -0
  40. package/fp16/include/fp16/bitcasts.h +92 -0
  41. package/fp16/include/fp16/fp16.h +451 -0
  42. package/fp16/include/fp16/psimd.h +131 -0
  43. package/fp16/include/fp16.h +11 -0
  44. package/fp16/jni/Android.mk +6 -0
  45. package/fp16/jni/Application.mk +4 -0
  46. package/fp16/test/alt-from-fp32-value.cc +498 -0
  47. package/fp16/test/alt-to-fp32-bits.cc +263 -0
  48. package/fp16/test/alt-to-fp32-psimd.cc +145 -0
  49. package/fp16/test/alt-to-fp32-value.cc +348 -0
  50. package/fp16/test/alt-to-fp32x2-psimd.cc +245 -0
  51. package/fp16/test/bitcasts.cc +56 -0
  52. package/fp16/test/ieee-from-fp32-value.cc +523 -0
  53. package/fp16/test/ieee-to-fp32-bits.cc +333 -0
  54. package/fp16/test/ieee-to-fp32-psimd.cc +312 -0
  55. package/fp16/test/ieee-to-fp32-value.cc +420 -0
  56. package/fp16/test/ieee-to-fp32x2-psimd.cc +541 -0
  57. package/fp16/test/peachpy/alt-xmm-to-fp32-xmm-avx.cc +96 -0
  58. package/fp16/test/peachpy/alt-xmm-to-fp32-ymm-avx2.cc +96 -0
  59. package/fp16/test/peachpy/stubs.py +38 -0
  60. package/fp16/test/tables.cc +530 -0
  61. package/fp16/test/tables.h +13 -0
  62. package/fp16/third-party/THHalf.h +92 -0
  63. package/fp16/third-party/eigen-half.h +170 -0
  64. package/fp16/third-party/float16-compressor.h +80 -0
  65. package/fp16/third-party/half.hpp +2916 -0
  66. package/fp16/third-party/npy-halffloat.h +328 -0
  67. package/include/usearch/usearch.hpp +1345 -0
  68. package/package.json +19 -0
  69. package/pyproject.toml +33 -0
  70. package/setup.py +63 -0
  71. package/simsimd/.clang-format +10 -0
  72. package/simsimd/.vscode/launch.json +24 -0
  73. package/simsimd/.vscode/settings.json +73 -0
  74. package/simsimd/.vscode/tasks.json +17 -0
  75. package/simsimd/CMakeLists.txt +82 -0
  76. package/simsimd/LICENSE +201 -0
  77. package/simsimd/README.md +73 -0
  78. package/simsimd/bench.cxx +82 -0
  79. package/simsimd/include/simsimd/simsimd.h +322 -0
  80. package/simsimd/include/simsimd/simsimd.hpp +443 -0
  81. package/src/advanced.hpp +489 -0
  82. package/src/bench.cpp +415 -0
  83. package/src/bench.py +89 -0
  84. package/src/javascript.cpp +198 -0
  85. package/src/main.rs +42 -0
  86. package/src/python.cpp +226 -0
  87. package/src/rust.cpp +23 -0
  88. package/src/rust.h +22 -0
  89. package/src/test.js +17 -0
  90. package/src/test.py +20 -0
@@ -0,0 +1,82 @@
1
+ #include <benchmark/benchmark.h>
2
+
3
+ #include <simsimd/simsimd.hpp>
4
+
5
+ namespace bm = benchmark;
6
+ using namespace av::simsimd;
7
+
8
+ static constexpr std::size_t threads_k = 64;
9
+ static constexpr std::size_t time_k = 100;
10
+
11
+ template <typename metric_at, typename scalar_at, std::size_t bytes_per_vector_ak,
12
+ std::size_t dimensions_ak = bytes_per_vector_ak / sizeof(scalar_at)> //
13
+ static void measure(bm::State& state) {
14
+
15
+ constexpr std::size_t buffer_size_k = bytes_per_vector_ak / sizeof(scalar_at);
16
+ alignas(64) scalar_at a[buffer_size_k]{};
17
+ alignas(64) scalar_at b[buffer_size_k]{};
18
+ scalar_at c{};
19
+
20
+ std::fill_n(a, buffer_size_k, static_cast<scalar_at>(1));
21
+ std::fill_n(b, buffer_size_k, static_cast<scalar_at>(2));
22
+
23
+ for (auto _ : state)
24
+ bm::DoNotOptimize((c = metric_at{}(a, b, dimensions_ak)));
25
+
26
+ state.SetBytesProcessed(state.iterations() * bytes_per_vector_ak * 2u);
27
+ state.SetItemsProcessed(state.iterations());
28
+ }
29
+
30
+ BENCHMARK_TEMPLATE(measure, euclidean_distance_t, f32_t, 32)->Threads(threads_k)->MinTime(time_k);
31
+ BENCHMARK_TEMPLATE(measure, euclidean_distance_t, f32_t, 256)->Threads(threads_k)->MinTime(time_k);
32
+ BENCHMARK_TEMPLATE(measure, euclidean_distance_t, f16_t, 32)->Threads(threads_k)->MinTime(time_k);
33
+ BENCHMARK_TEMPLATE(measure, euclidean_distance_t, f16_t, 256)->Threads(threads_k)->MinTime(time_k);
34
+
35
+ BENCHMARK_TEMPLATE(measure, cosine_similarity_t, f32_t, 32)->Threads(threads_k)->MinTime(time_k);
36
+ BENCHMARK_TEMPLATE(measure, cosine_similarity_t, f32_t, 256)->Threads(threads_k)->MinTime(time_k);
37
+ BENCHMARK_TEMPLATE(measure, cosine_similarity_f32x4k_t, f32_t, 32)->Threads(threads_k)->MinTime(time_k);
38
+ BENCHMARK_TEMPLATE(measure, cosine_similarity_f32x4k_t, f32_t, 256)->Threads(threads_k)->MinTime(time_k);
39
+
40
+ BENCHMARK_TEMPLATE(measure, hamming_bits_distance_t, u64_t, 32, 256)->Threads(threads_k)->MinTime(time_k);
41
+ BENCHMARK_TEMPLATE(measure, hamming_bits_distance_t, u64_t, 256, 2048)->Threads(threads_k)->MinTime(time_k);
42
+ BENCHMARK_TEMPLATE(measure, hamming_bits_distance_u1x128k_t, u64_t, 32, 256)->Threads(threads_k)->MinTime(time_k);
43
+ BENCHMARK_TEMPLATE(measure, hamming_bits_distance_u1x128k_t, u64_t, 256, 2048)->Threads(threads_k)->MinTime(time_k);
44
+
45
+ int main(int argc, char** argv) {
46
+
47
+ bool compiled_with_sve = false;
48
+ bool compiled_with_neon = false;
49
+ bool compiled_with_avx2 = false;
50
+ bool compiled_with_avx512popcnt = false;
51
+
52
+ #if defined(__ARM_FEATURE_SVE)
53
+ compiled_with_sve = true;
54
+ #endif
55
+ #if defined(__ARM_NEON)
56
+ compiled_with_neon = true;
57
+ #endif
58
+ #if defined(__AVX2__)
59
+ compiled_withavx2 = true;
60
+ #endif
61
+ #if defined(__AVX512VPOPCNTDQ__)
62
+ compiled_with_avx512popcnt = true;
63
+ #endif
64
+
65
+ // Log supported functionality
66
+ char const* flags[2] = {"false", "true"};
67
+ std::printf("Benchmarking Similarity Measures\n");
68
+ std::printf("\n");
69
+ std::printf("- Arm NEON support enabled: %s\n", flags[compiled_with_neon]);
70
+ std::printf("- Arm SVE support enabled: %s\n", flags[compiled_with_sve]);
71
+ std::printf("- x86 AVX2 support enabled: %s\n", flags[compiled_with_avx2]);
72
+ std::printf("- x86 AVX512VPOPCNTDQ support enabled: %s\n", flags[compiled_with_avx512popcnt]);
73
+ std::printf("\n");
74
+
75
+ // Run the benchmarks
76
+ bm::Initialize(&argc, argv);
77
+ if (bm::ReportUnrecognizedArguments(argc, argv))
78
+ return 1;
79
+ bm::RunSpecifiedBenchmarks();
80
+ bm::Shutdown();
81
+ return 0;
82
+ }
@@ -0,0 +1,322 @@
1
+ /**
2
+ * @brief Collection of Similarity Measures, SIMD-accelerated with SSE, AVX, NEON, SVE.
3
+ *
4
+ * @author Ashot Vardanian
5
+ * @date March 14, 2023
6
+ *
7
+ * x86 intrinsics: https://www.intel.com/content/www/us/en/docs/intrinsics-guide/
8
+ * Arm intrinsics: https://developer.arm.com/architectures/instruction-sets/intrinsics/
9
+ * Detecting target CPU features at compile time: https://stackoverflow.com/a/28939692/2766161
10
+ */
11
+
12
+ #pragma once
13
+ #include <math.h> // `sqrt`
14
+
15
+ #if defined(__AVX2__)
16
+ #include <immintrin.h>
17
+ #endif
18
+
19
+ #if defined(__ARM_NEON)
20
+ #include <arm_neon.h>
21
+ #endif
22
+
23
+ #if defined(__ARM_FEATURE_SVE)
24
+ #include <arm_sve.h>
25
+ #endif
26
+
27
+ #ifdef __cplusplus
28
+ extern "C" {
29
+ #endif
30
+
31
+ typedef float simsimd_f32_t;
32
+ typedef double simsimd_f64_t;
33
+ typedef int16_t simsimd_f16_t;
34
+
35
+ union simsimd_f32i32_t {
36
+ int32_t i;
37
+ float f;
38
+ };
39
+
40
+ inline static simsimd_f32_t simsimd_dot_f32sve(simsimd_f32_t const* a, simsimd_f32_t const* b, size_t d) {
41
+ #if defined(__ARM_FEATURE_SVE)
42
+ size_t i = 0;
43
+ svfloat32_t ab_vec = svdupq_n_f32(0.f, 0.f, 0.f, 0.f);
44
+ svbool_t pg_vec = svwhilelt_b32(i, d);
45
+ do {
46
+ svfloat32_t a_vec = svld1_f32(pg_vec, a + i);
47
+ svfloat32_t b_vec = svld1_f32(pg_vec, b + i);
48
+ ab_vec = svmla_f32_x(pg_vec, ab_vec, a_vec, b_vec);
49
+ i += svcntw();
50
+ pg_vec = svwhilelt_b32(i, d);
51
+ } while (svptest_any(svptrue_b32(), pg_vec));
52
+ return svaddv_f32(svptrue_b32(), ab_vec);
53
+ #else
54
+ (void)a, (void)b, (void)d;
55
+ return 0;
56
+ #endif
57
+ }
58
+
59
+ inline static simsimd_f16_t simsimd_dot_f16sve(simsimd_f16_t const* a_enum, simsimd_f16_t const* b_enum, size_t d) {
60
+ #if defined(__ARM_FEATURE_SVE)
61
+ size_t i = 0;
62
+ svfloat16_t ab_vec = svdupq_n_f16(0, 0, 0, 0, 0, 0, 0, 0);
63
+ svbool_t pg_vec = svwhilelt_b16(i, d);
64
+ simsimd_f16_t const* a = (simsimd_f16_t const*)(a_enum);
65
+ simsimd_f16_t const* b = (simsimd_f16_t const*)(b_enum);
66
+ do {
67
+ svfloat16_t a_vec = svld1_f16(pg_vec, (float16_t const*)a + i);
68
+ svfloat16_t b_vec = svld1_f16(pg_vec, (float16_t const*)b + i);
69
+ ab_vec = svmla_f16_x(pg_vec, ab_vec, a_vec, b_vec);
70
+ i += svcnth();
71
+ pg_vec = svwhilelt_b16(i, d);
72
+ } while (svptest_any(svptrue_b16(), pg_vec));
73
+ return static_cast<simsimd_f16_t>(svaddv_f16(svptrue_b16(), ab_vec));
74
+ #else
75
+ (void)a_enum, (void)b_enum, (void)d;
76
+ return 0;
77
+ #endif
78
+ }
79
+
80
+ inline static simsimd_f32_t simsimd_cos_f32sve(simsimd_f32_t const* a, simsimd_f32_t const* b, size_t d) {
81
+ #if defined(__ARM_FEATURE_SVE)
82
+ size_t i = 0;
83
+ svfloat32_t ab_vec = svdupq_n_f32(0.f, 0.f, 0.f, 0.f);
84
+ svfloat32_t a2_vec = svdupq_n_f32(0.f, 0.f, 0.f, 0.f);
85
+ svfloat32_t b2_vec = svdupq_n_f32(0.f, 0.f, 0.f, 0.f);
86
+ svbool_t pg_vec = svwhilelt_b32(i, d);
87
+ do {
88
+ svfloat32_t a_vec = svld1_f32(pg_vec, a + i);
89
+ svfloat32_t b_vec = svld1_f32(pg_vec, b + i);
90
+ ab_vec = svmla_f32_x(pg_vec, ab_vec, a_vec, b_vec);
91
+ a2_vec = svmla_f32_x(pg_vec, a2_vec, a_vec, a_vec);
92
+ b2_vec = svmla_f32_x(pg_vec, b2_vec, b_vec, b_vec);
93
+ i += svcntw();
94
+ pg_vec = svwhilelt_b32(i, d);
95
+ } while (svptest_any(svptrue_b32(), pg_vec));
96
+ simsimd_f32_t ab = svaddv_f32(svptrue_b32(), ab_vec);
97
+ simsimd_f32_t a2 = svaddv_f32(svptrue_b32(), a2_vec);
98
+ simsimd_f32_t b2 = svaddv_f32(svptrue_b32(), b2_vec);
99
+ return ab / (sqrt(a2) * sqrt(b2));
100
+ #else
101
+ (void)a, (void)b, (void)d;
102
+ return 0;
103
+ #endif
104
+ }
105
+
106
+ inline static simsimd_f32_t simsimd_euclidean_f32sve(simsimd_f32_t const* a, simsimd_f32_t const* b, size_t d) {
107
+ #if defined(__ARM_FEATURE_SVE)
108
+ size_t i = 0;
109
+ svfloat32_t d2_vec = svdupq_n_f32(0.f, 0.f, 0.f, 0.f);
110
+ svbool_t pg_vec = svwhilelt_b32(i, d);
111
+ do {
112
+ svfloat32_t a_vec = svld1_f32(pg_vec, a + i);
113
+ svfloat32_t b_vec = svld1_f32(pg_vec, b + i);
114
+ svfloat32_t a_minus_b_vec = svsub_f32_x(pg_vec, a_vec, b_vec);
115
+ d2_vec = svmla_f32_x(pg_vec, d2_vec, a_minus_b_vec, a_minus_b_vec);
116
+ i += svcntw();
117
+ pg_vec = svwhilelt_b32(i, d);
118
+ } while (svptest_any(svptrue_b32(), pg_vec));
119
+ simsimd_f32_t d2 = svaddv_f32(svptrue_b32(), d2_vec);
120
+ return sqrt(d2);
121
+ #else
122
+ (void)a, (void)b, (void)d;
123
+ return 0;
124
+ #endif
125
+ }
126
+
127
+ inline static simsimd_f16_t simsimd_euclidean_f16sve( //
128
+ simsimd_f16_t const* a_enum, simsimd_f16_t const* b_enum, size_t d) {
129
+ #if defined(__ARM_FEATURE_SVE)
130
+ size_t i = 0;
131
+ svfloat16_t d2_vec = svdupq_n_f16(0, 0, 0, 0, 0, 0, 0, 0);
132
+ svbool_t pg_vec = svwhilelt_b16(i, d);
133
+ simsimd_f16_t const* a = (simsimd_f16_t const*)(a_enum);
134
+ simsimd_f16_t const* b = (simsimd_f16_t const*)(b_enum);
135
+ do {
136
+ svfloat16_t a_vec = svld1_f16(pg_vec, (float16_t const*)a + i);
137
+ svfloat16_t b_vec = svld1_f16(pg_vec, (float16_t const*)b + i);
138
+ svfloat16_t a_minus_b_vec = svsub_f16_x(pg_vec, a_vec, b_vec);
139
+ d2_vec = svmla_f16_x(pg_vec, d2_vec, a_minus_b_vec, a_minus_b_vec);
140
+ i += svcnth();
141
+ pg_vec = svwhilelt_b16(i, d);
142
+ } while (svptest_any(svptrue_b16(), pg_vec));
143
+ simsimd_f16_t d2 = svaddv_f16(svptrue_b16(), d2_vec);
144
+ return static_cast<simsimd_f16_t>(sqrt(d2));
145
+ #else
146
+ (void)a_enum, (void)b_enum, (void)d;
147
+ return 0;
148
+ #endif
149
+ }
150
+
151
+ inline static size_t simsimd_hamming_u1x8sve(uint8_t const* a, uint8_t const* b, size_t d) {
152
+ #if defined(__ARM_FEATURE_SVE)
153
+ size_t i = 0;
154
+ svuint8_t d_vec = svdupq_n_u8(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0);
155
+ svbool_t pg_vec = svwhilelt_b8(i, d);
156
+ do {
157
+ svuint8_t a_vec = svld1_u8(pg_vec, a + i);
158
+ svuint8_t b_vec = svld1_u8(pg_vec, b + i);
159
+ svuint8_t a_xor_b_vec = sveor_u8_m(pg_vec, a_vec, b_vec);
160
+ d_vec = svadd_u8_m(pg_vec, d_vec, svcnt_u8_x(pg_vec, a_xor_b_vec));
161
+ i += svcntb() * __CHAR_BIT__;
162
+ pg_vec = svwhilelt_b32(i, d);
163
+ } while (svptest_any(svptrue_b32(), pg_vec));
164
+ return svaddv_u8(svptrue_b32(), d_vec);
165
+ #else
166
+ (void)a, (void)b, (void)d;
167
+ return 0;
168
+ #endif
169
+ }
170
+
171
+ inline static simsimd_f32_t simsimd_dot_f32x4neon(simsimd_f32_t const* a, simsimd_f32_t const* b, size_t d) {
172
+ #if defined(__ARM_NEON)
173
+ float32x4_t ab_vec = vdupq_n_f32(0);
174
+ for (size_t i = 0; i != d; i += 4)
175
+ ab_vec = vmlaq_f32(ab_vec, vld1q_f32(a + i), vld1q_f32(b + i));
176
+ return vaddvq_f32(ab_vec);
177
+ #else
178
+ (void)a, (void)b, (void)d;
179
+ return 0;
180
+ #endif
181
+ }
182
+
183
+ inline static simsimd_f16_t simsimd_dot_f16x8neon(simsimd_f16_t const* a, simsimd_f16_t const* b, size_t d) {
184
+ #if defined(__ARM_NEON)
185
+ // There is no half-precision FMA, only bfloat16, so we must manually multiply and add.
186
+ float16x8_t ab_vec = vdupq_n_f16(0);
187
+ for (size_t i = 0; i != d; i += 8)
188
+ ab_vec = vaddq_f16(ab_vec, vmulq_f16(vld1q_f16((float16_t const*)a + i), vld1q_f16((float16_t const*)b + i)));
189
+ return vaddvq_f32(vcvt_f32_f16(vget_high_f16(ab_vec))) + vaddvq_f32(vcvt_f32_f16(vget_low_f16(ab_vec)));
190
+ #else
191
+ (void)a, (void)b, (void)d;
192
+ return 0;
193
+ #endif
194
+ }
195
+
196
+ inline static simsimd_f32_t simsimd_cos_f32x4neon(simsimd_f32_t const* a, simsimd_f32_t const* b, size_t d) {
197
+ #if defined(__ARM_NEON)
198
+ float32x4_t ab_vec = vdupq_n_f32(0);
199
+ float32x4_t a2_vec = vdupq_n_f32(0);
200
+ float32x4_t b2_vec = vdupq_n_f32(0);
201
+ for (size_t i = 0; i != d; i += 4) {
202
+ float32x4_t a_vec = vld1q_f32(a + i);
203
+ float32x4_t b_vec = vld1q_f32(b + i);
204
+ ab_vec = vmlaq_f32(ab_vec, a_vec, b_vec);
205
+ a2_vec = vmlaq_f32(a2_vec, a_vec, a_vec);
206
+ b2_vec = vmlaq_f32(b2_vec, b_vec, b_vec);
207
+ }
208
+ simsimd_f32_t ab = vaddvq_f32(ab_vec);
209
+ simsimd_f32_t a2 = vaddvq_f32(a2_vec);
210
+ simsimd_f32_t b2 = vaddvq_f32(b2_vec);
211
+ return ab / (sqrt(a2) * sqrt(b2));
212
+ #else
213
+ (void)a, (void)b, (void)d;
214
+ return 0;
215
+ #endif
216
+ }
217
+
218
+ inline static size_t simsimd_hamming_u1x128sve(uint8_t const* a, uint8_t const* b, size_t d) {
219
+ #if defined(__ARM_NEON)
220
+ /// Contains 16x 8-bit integers with running population count sums.
221
+ uint8x16_t d_vec = vdupq_n_u8(0);
222
+ for (size_t i = 0; i != d; i += 16) {
223
+ uint8x16_t a_vec = vld1q_u8(a + i);
224
+ uint8x16_t b_vec = vld1q_u8(b + i);
225
+ uint8x16_t a_xor_b_vec = veorq_u8(a_vec, b_vec);
226
+ d_vec = vaddq_u8(d_vec, vcntq_u8(a_xor_b_vec));
227
+ }
228
+ return vaddvq_u8(d_vec);
229
+ #else
230
+ (void)a, (void)b, (void)d;
231
+ return 0;
232
+ #endif
233
+ }
234
+
235
+ inline static simsimd_f32_t simsimd_dot_f32x4avx2(simsimd_f32_t const* a, simsimd_f32_t const* b, size_t d) {
236
+ #if defined(__AVX2__)
237
+ __m128 ab_vec = _mm_set1_ps(0);
238
+ for (size_t i = 0; i != d; i += 4)
239
+ ab_vec = _mm_fmadd_ps(_mm_loadu_ps(a + i), _mm_loadu_ps(b + i), ab_vec);
240
+ ab_vec = _mm_hadd_ps(ab_vec, ab_vec);
241
+ ab_vec = _mm_hadd_ps(ab_vec, ab_vec);
242
+ simsimd_f32i32_t ab_union;
243
+ ab_union.i = _mm_cvtsi128_si32(_mm_castps_si128(ab_vec));
244
+ return ab_union.f;
245
+ #else
246
+ (void)a, (void)b, (void)d;
247
+ return 0;
248
+ #endif
249
+ }
250
+
251
+ inline static int32_t simsimd_dot_i8x16avx2(int8_t const* a, int8_t const* b, size_t d) {
252
+ #if defined(__AVX2__)
253
+ __m256i ab_vec = _mm256_set1_epi32(0);
254
+ for (size_t i = 0; i != d; i += 8) {
255
+ ab_vec = _mm256_add_epi32( //
256
+ ab_vec, _mm256_madd_epi16( //
257
+ _mm256_cvtepi8_epi16(_mm_loadu_si128((__m128i const*)(a + i))),
258
+ _mm256_cvtepi8_epi16(_mm_loadu_si128((__m128i const*)(b + i)))));
259
+ }
260
+ ab_vec = _mm256_hadd_epi32(ab_vec, ab_vec);
261
+ ab_vec = _mm256_hadd_epi32(ab_vec, ab_vec);
262
+ ab_vec = _mm256_hadd_epi32(ab_vec, ab_vec);
263
+ simsimd_f32i32_t ab_union;
264
+ ab_union.f = _mm256_cvtss_f32(_mm256_castsi256_ps(ab_vec));
265
+ return ab_union.i;
266
+ #else
267
+ (void)a, (void)b, (void)d;
268
+ return 0;
269
+ #endif
270
+ }
271
+
272
+ inline static simsimd_f32_t simsimd_cos_f32x4avx2(simsimd_f32_t const* a, simsimd_f32_t const* b, size_t d) {
273
+ #if defined(__AVX2__)
274
+ __m128 ab_vec = _mm_set1_ps(0);
275
+ __m128 a2_vec = _mm_set1_ps(0);
276
+ __m128 b2_vec = _mm_set1_ps(0);
277
+ for (size_t i = 0; i != d; i += 4) {
278
+ __m128 a_vec = _mm_loadu_ps(a + i);
279
+ __m128 b_vec = _mm_loadu_ps(b + i);
280
+ ab_vec = _mm_fmadd_ps(a_vec, b_vec, ab_vec);
281
+ a2_vec = _mm_fmadd_ps(a_vec, a_vec, a2_vec);
282
+ b2_vec = _mm_fmadd_ps(b_vec, b_vec, b2_vec);
283
+ }
284
+ ab_vec = _mm_hadd_ps(ab_vec, ab_vec);
285
+ ab_vec = _mm_hadd_ps(ab_vec, ab_vec);
286
+ a2_vec = _mm_hadd_ps(a2_vec, a2_vec);
287
+ a2_vec = _mm_hadd_ps(a2_vec, a2_vec);
288
+ b2_vec = _mm_hadd_ps(b2_vec, b2_vec);
289
+ b2_vec = _mm_hadd_ps(b2_vec, b2_vec);
290
+ simsimd_f32i32_t ab_union = {_mm_cvtsi128_si32(_mm_castps_si128(ab_vec))};
291
+ simsimd_f32i32_t a2_union = {_mm_cvtsi128_si32(_mm_castps_si128(a2_vec))};
292
+ simsimd_f32i32_t b2_union = {_mm_cvtsi128_si32(_mm_castps_si128(b2_vec))};
293
+ return ab_union.f / (sqrt(a2_union.f) * sqrt(b2_union.f));
294
+ #else
295
+ (void)a, (void)b, (void)d;
296
+ return 0;
297
+ #endif
298
+ }
299
+
300
+ inline static size_t simsimd_hamming_u1x128avx512(uint8_t const* a, uint8_t const* b, size_t d) {
301
+ #if defined(__AVX512VPOPCNTDQ__)
302
+ size_t words = d / 128;
303
+ uint64_t const* a64 = (uint64_t const*)(a);
304
+ uint64_t const* b64 = (uint64_t const*)(b);
305
+ /// Contains 2x 64-bit integers with running population count sums.
306
+ __m128i d_vec = _mm_set_epi64x(0, 0);
307
+ for (size_t i = 0; i != words; i += 2)
308
+ d_vec = _mm_add_epi64( //
309
+ d_vec, //
310
+ _mm_popcnt_epi64( //
311
+ _mm_xor_si128( //
312
+ _mm_load_si128((__m128i const*)(a64 + i)), _mm_load_si128((__m128i const*)(b64 + i)))));
313
+ return _mm_cvtm64_si64(_mm_movepi64_pi64(d_vec)) + _mm_extract_epi64(d_vec, 1);
314
+ #else
315
+ (void)a, (void)b, (void)d;
316
+ return 0;
317
+ #endif
318
+ }
319
+
320
+ #ifdef __cplusplus
321
+ }
322
+ #endif