usearch 2.1.2 → 2.2.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.
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  * @brief Collection of Similarity Measures, SIMD-accelerated with SSE, AVX, NEON, SVE.
3
3
  *
4
- * @author Ashot Vardanian
4
+ * @author Ash Vardanian
5
5
  * @date March 14, 2023
6
6
  *
7
7
  * x86 intrinsics: https://www.intel.com/content/www/us/en/docs/intrinsics-guide/
@@ -14,23 +14,82 @@
14
14
  #include <stddef.h> // `size_t`
15
15
  #include <stdint.h>
16
16
 
17
- #if defined(__AVX2__)
18
- #include <immintrin.h>
17
+ #ifdef _MSC_VER
18
+ #include <intrin.h>
19
+ #define popcount32 __popcnt
20
+ #define popcount64 __popcnt64
21
+ #else
22
+ #define popcount32 __builtin_popcount
23
+ #define popcount64 __builtin_popcountll
19
24
  #endif
20
25
 
26
+ // Compiling for Arm: SIMSIMD_TARGET_ARM
27
+ #if defined(__aarch64__) || defined(_M_ARM64)
28
+ #define SIMSIMD_TARGET_ARM 1
29
+
30
+ // Compiling for Arm: SIMSIMD_TARGET_ARM_NEON
31
+ #if !defined(SIMSIMD_TARGET_ARM_NEON)
21
32
  #if defined(__ARM_NEON)
22
- #include <arm_neon.h>
33
+ #define SIMSIMD_TARGET_ARM_NEON 1
34
+ #else
35
+ #define SIMSIMD_TARGET_ARM_NEON 0
36
+ #endif
23
37
  #endif
24
38
 
39
+ // Compiling for Arm: SIMSIMD_TARGET_ARM_SVE
40
+ #if !defined(SIMSIMD_TARGET_ARM_SVE)
25
41
  #if defined(__ARM_FEATURE_SVE)
26
- #include <arm_sve.h>
42
+ #define SIMSIMD_TARGET_ARM_SVE 1
43
+ #else
44
+ #define SIMSIMD_TARGET_ARM_SVE 0
45
+ #endif
27
46
  #endif
28
47
 
29
- #ifdef _MSC_VER
30
- #include <intrin.h>
31
- #define popcount32 __popcnt
48
+ #define SIMSIMD_TARGET_X86 0
49
+ #define SIMSIMD_TARGET_X86_AVX2 0
50
+ #define SIMSIMD_TARGET_X86_AVX512 0
51
+
52
+ // Compiling for x86: SIMSIMD_TARGET_X86
53
+ #elif defined(__x86_64__) || defined(_M_X64)
54
+ #define SIMSIMD_TARGET_X86 1
55
+
56
+ // Compiling for x86: SIMSIMD_TARGET_X86_AVX2
57
+ #if !defined(SIMSIMD_TARGET_X86_AVX2)
58
+ #if defined(__AVX2__)
59
+ #define SIMSIMD_TARGET_X86_AVX2 1
32
60
  #else
33
- #define popcount32 __builtin_popcount
61
+ #define SIMSIMD_TARGET_X86_AVX2 0
62
+ #endif
63
+ #endif
64
+
65
+ // Compiling for x86: SIMSIMD_TARGET_X86_AVX512
66
+ #if !defined(SIMSIMD_TARGET_X86_AVX512)
67
+ #if defined(__AVX512F__) || defined(__AVX512VPOPCNTDQ__)
68
+ #define SIMSIMD_TARGET_X86_AVX512 1
69
+ #else
70
+ #define SIMSIMD_TARGET_X86_AVX512 0
71
+ #endif
72
+ #endif
73
+
74
+ #define SIMSIMD_TARGET_ARM 0
75
+ #define SIMSIMD_TARGET_ARM_NEON 0
76
+ #define SIMSIMD_TARGET_ARM_SVE 0
77
+
78
+ // Compiling for unknown hardware architecture
79
+ #else
80
+ #error Unknown hardware architecture!
81
+ #endif
82
+
83
+ #if SIMSIMD_TARGET_ARM_NEON
84
+ #include <arm_neon.h>
85
+ #endif
86
+
87
+ #if SIMSIMD_TARGET_ARM_SVE
88
+ #include <arm_sve.h>
89
+ #endif
90
+
91
+ #if SIMSIMD_TARGET_X86_AVX2 || SIMSIMD_TARGET_X86_AVX512
92
+ #include <immintrin.h>
34
93
  #endif
35
94
 
36
95
  #ifdef __cplusplus
@@ -46,8 +105,12 @@ union simsimd_f32i32_t {
46
105
  float f;
47
106
  };
48
107
 
49
- inline static simsimd_f32_t simsimd_dot_f32_sve(simsimd_f32_t const* a, simsimd_f32_t const* b, size_t d) {
50
- #if defined(__ARM_FEATURE_SVE)
108
+ #if SIMSIMD_TARGET_ARM
109
+ #if SIMSIMD_TARGET_ARM_SVE
110
+
111
+ __attribute__((target("+sve"))) inline static simsimd_f32_t //
112
+ simsimd_dot_f32_sve(simsimd_f32_t const* a, simsimd_f32_t const* b, size_t d) {
113
+
51
114
  size_t i = 0;
52
115
  svfloat32_t ab_vec = svdupq_n_f32(0.f, 0.f, 0.f, 0.f);
53
116
  svbool_t pg_vec = svwhilelt_b32(i, d);
@@ -59,14 +122,10 @@ inline static simsimd_f32_t simsimd_dot_f32_sve(simsimd_f32_t const* a, simsimd_
59
122
  pg_vec = svwhilelt_b32(i, d);
60
123
  } while (svptest_any(svptrue_b32(), pg_vec));
61
124
  return 1 - svaddv_f32(svptrue_b32(), ab_vec);
62
- #else
63
- (void)a, (void)b, (void)d;
64
- return -1;
65
- #endif
66
125
  }
67
126
 
68
- inline static simsimd_f32_t simsimd_cos_f32_sve(simsimd_f32_t const* a, simsimd_f32_t const* b, size_t d) {
69
- #if defined(__ARM_FEATURE_SVE)
127
+ __attribute__((target("+sve"))) inline static simsimd_f32_t //
128
+ simsimd_cos_f32_sve(simsimd_f32_t const* a, simsimd_f32_t const* b, size_t d) {
70
129
  size_t i = 0;
71
130
  svfloat32_t ab_vec = svdupq_n_f32(0.f, 0.f, 0.f, 0.f);
72
131
  svfloat32_t a2_vec = svdupq_n_f32(0.f, 0.f, 0.f, 0.f);
@@ -85,14 +144,10 @@ inline static simsimd_f32_t simsimd_cos_f32_sve(simsimd_f32_t const* a, simsimd_
85
144
  simsimd_f32_t a2 = svaddv_f32(svptrue_b32(), a2_vec);
86
145
  simsimd_f32_t b2 = svaddv_f32(svptrue_b32(), b2_vec);
87
146
  return 1 - ab / (sqrt(a2) * sqrt(b2));
88
- #else
89
- (void)a, (void)b, (void)d;
90
- return -1;
91
- #endif
92
147
  }
93
148
 
94
- inline static simsimd_f32_t simsimd_l2sq_f32_sve(simsimd_f32_t const* a, simsimd_f32_t const* b, size_t d) {
95
- #if defined(__ARM_FEATURE_SVE)
149
+ __attribute__((target("+sve"))) inline static simsimd_f32_t //
150
+ simsimd_l2sq_f32_sve(simsimd_f32_t const* a, simsimd_f32_t const* b, size_t d) {
96
151
  size_t i = 0;
97
152
  svfloat32_t d2_vec = svdupq_n_f32(0.f, 0.f, 0.f, 0.f);
98
153
  svbool_t pg_vec = svwhilelt_b32(i, d);
@@ -105,15 +160,11 @@ inline static simsimd_f32_t simsimd_l2sq_f32_sve(simsimd_f32_t const* a, simsimd
105
160
  pg_vec = svwhilelt_b32(i, d);
106
161
  } while (svptest_any(svptrue_b32(), pg_vec));
107
162
  simsimd_f32_t d2 = svaddv_f32(svptrue_b32(), d2_vec);
108
- return sqrt(d2);
109
- #else
110
- (void)a, (void)b, (void)d;
111
- return -1;
112
- #endif
163
+ return d2;
113
164
  }
114
165
 
115
- inline static simsimd_f32_t simsimd_cos_f16_sve(simsimd_f16_t const* a_enum, simsimd_f16_t const* b_enum, size_t d) {
116
- #if defined(__ARM_FEATURE_SVE)
166
+ __attribute__((target("+sve+fp16"))) inline static simsimd_f32_t //
167
+ simsimd_cos_f16_sve(simsimd_f16_t const* a_enum, simsimd_f16_t const* b_enum, size_t d) {
117
168
  size_t i = 0;
118
169
  svfloat16_t ab_vec = svdupq_n_f16(0, 0, 0, 0, 0, 0, 0, 0);
119
170
  svfloat16_t a2_vec = svdupq_n_f16(0, 0, 0, 0, 0, 0, 0, 0);
@@ -134,14 +185,10 @@ inline static simsimd_f32_t simsimd_cos_f16_sve(simsimd_f16_t const* a_enum, sim
134
185
  simsimd_f16_t a2 = svaddv_f16(svptrue_b16(), a2_vec);
135
186
  simsimd_f16_t b2 = svaddv_f16(svptrue_b16(), b2_vec);
136
187
  return 1 - ab / (sqrt(a2) * sqrt(b2));
137
- #else
138
- (void)a_enum, (void)b_enum, (void)d;
139
- return -1;
140
- #endif
141
188
  }
142
189
 
143
- inline static simsimd_f32_t simsimd_l2sq_f16_sve(simsimd_f16_t const* a_enum, simsimd_f16_t const* b_enum, size_t d) {
144
- #if defined(__ARM_FEATURE_SVE)
190
+ __attribute__((target("+sve+fp16"))) inline static simsimd_f32_t //
191
+ simsimd_l2sq_f16_sve(simsimd_f16_t const* a_enum, simsimd_f16_t const* b_enum, size_t d) {
145
192
  size_t i = 0;
146
193
  svfloat16_t d2_vec = svdupq_n_f16(0, 0, 0, 0, 0, 0, 0, 0);
147
194
  svbool_t pg_vec = svwhilelt_b16(i, d);
@@ -155,16 +202,12 @@ inline static simsimd_f32_t simsimd_l2sq_f16_sve(simsimd_f16_t const* a_enum, si
155
202
  i += svcnth();
156
203
  pg_vec = svwhilelt_b16(i, d);
157
204
  } while (svptest_any(svptrue_b16(), pg_vec));
158
- float16_t f16 = svaddv_f16(svptrue_b16(), d2_vec);
159
- return 1 - simsimd_f32_t(f16);
160
- #else
161
- (void)a_enum, (void)b_enum, (void)d;
162
- return -1;
163
- #endif
205
+ float16_t d2_f16 = svaddv_f16(svptrue_b16(), d2_vec);
206
+ return 1 - d2_f16;
164
207
  }
165
208
 
166
- inline static simsimd_f32_t simsimd_hamming_b1x8_sve(uint8_t const* a, uint8_t const* b, size_t d) {
167
- #if defined(__ARM_FEATURE_SVE)
209
+ __attribute__((target("+sve"))) inline static simsimd_f32_t //
210
+ simsimd_hamming_b1x8_sve(uint8_t const* a, uint8_t const* b, size_t d) {
168
211
  size_t i = 0;
169
212
  svuint8_t d_vec = svdupq_n_u8(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0);
170
213
  svbool_t pg_vec = svwhilelt_b8(i, d);
@@ -177,26 +220,24 @@ inline static simsimd_f32_t simsimd_hamming_b1x8_sve(uint8_t const* a, uint8_t c
177
220
  pg_vec = svwhilelt_b32(i, d);
178
221
  } while (svptest_any(svptrue_b32(), pg_vec));
179
222
  return 1 - svaddv_u8(svptrue_b32(), d_vec);
180
- #else
181
- (void)a, (void)b, (void)d;
182
- return -1;
183
- #endif
184
223
  }
185
224
 
186
- inline static simsimd_f32_t simsimd_dot_f32x4_neon(simsimd_f32_t const* a, simsimd_f32_t const* b, size_t d) {
187
- #if defined(__ARM_NEON)
225
+ #endif // SIMSIMD_TARGET_ARM_SVE
226
+
227
+ #if SIMSIMD_TARGET_ARM_NEON
228
+
229
+ __attribute__((target("+simd"))) inline static simsimd_f32_t //
230
+ simsimd_dot_f32x4_neon(simsimd_f32_t const* a, simsimd_f32_t const* b, size_t d) {
231
+
188
232
  float32x4_t ab_vec = vdupq_n_f32(0);
189
233
  for (size_t i = 0; i != d; i += 4)
190
234
  ab_vec = vfmaq_f32(ab_vec, vld1q_f32(a + i), vld1q_f32(b + i));
191
235
  return 1 - vaddvq_f32(ab_vec);
192
- #else
193
- (void)a, (void)b, (void)d;
194
- return -1;
195
- #endif
196
236
  }
197
237
 
198
- inline static simsimd_f32_t simsimd_cos_f16x4_neon(simsimd_f16_t const* a, simsimd_f16_t const* b, size_t d) {
199
- #if defined(__ARM_NEON)
238
+ __attribute__((target("+simd+fp16"))) inline static simsimd_f32_t //
239
+ simsimd_cos_f16x4_neon(simsimd_f16_t const* a, simsimd_f16_t const* b, size_t d) {
240
+
200
241
  float32x4_t ab_vec = vdupq_n_f32(0);
201
242
  float32x4_t a2_vec = vdupq_n_f32(0);
202
243
  float32x4_t b2_vec = vdupq_n_f32(0);
@@ -212,41 +253,36 @@ inline static simsimd_f32_t simsimd_cos_f16x4_neon(simsimd_f16_t const* a, simsi
212
253
  simsimd_f32_t a2 = vaddvq_f32(a2_vec);
213
254
  simsimd_f32_t b2 = vaddvq_f32(b2_vec);
214
255
  return 1 - ab / (sqrt(a2) * sqrt(b2));
215
- #else
216
- (void)a, (void)b, (void)d;
217
- return -1;
218
- #endif
219
256
  }
220
257
 
221
- inline static simsimd_f32_t simsimd_cos_i8x16_neon(int8_t const* a, int8_t const* b, size_t d) {
222
- #if defined(__ARM_NEON)
258
+ __attribute__((target("+simd"))) inline static simsimd_f32_t //
259
+ simsimd_cos_i8x16_neon(int8_t const* a, int8_t const* b, size_t d) {
260
+
223
261
  int32x4_t ab_vec = vdupq_n_s32(0);
224
262
  int32x4_t a2_vec = vdupq_n_s32(0);
225
263
  int32x4_t b2_vec = vdupq_n_s32(0);
226
264
 
227
- #if 0 // This 128-bit `vdot_s32` intrinsic is often unavailable, so we use the 64-bit `vdot_s32`.
228
- for (size_t i = 0; i != d; i += 16) {
229
- int8x16_t a_vec = vld1q_s8(a + i);
230
- int8x16_t b_vec = vld1q_s8(b + i);
231
- ab_vec = vdotq_s32(ab_vec, a_vec, b_vec);
232
- a2_vec = vdotq_s32(a2_vec, a_vec, a_vec);
233
- b2_vec = vdotq_s32(b2_vec, b_vec, b_vec);
234
- }
235
- #else
265
+ // This 128-bit `vdot_s32` intrinsic is often unavailable, so we use the 64-bit `vdot_s32`.
266
+ // for (size_t i = 0; i != d; i += 16) {
267
+ // int8x16_t a_vec = vld1q_s8(a + i);
268
+ // int8x16_t b_vec = vld1q_s8(b + i);
269
+ // ab_vec = vdotq_s32(ab_vec, a_vec, b_vec);
270
+ // a2_vec = vdotq_s32(a2_vec, a_vec, a_vec);
271
+ // b2_vec = vdotq_s32(b2_vec, b_vec, b_vec);
272
+ // }
236
273
  for (size_t i = 0; i != d; i += 8) {
237
274
  int16x8_t a_vec = vmovl_s8(vld1_s8(a + i));
238
275
  int16x8_t b_vec = vmovl_s8(vld1_s8(b + i));
239
276
  int16x8_t ab_part_vec = vmulq_s16(a_vec, b_vec);
240
277
  int16x8_t a2_part_vec = vmulq_s16(a_vec, a_vec);
241
278
  int16x8_t b2_part_vec = vmulq_s16(b_vec, b_vec);
242
- ab_vec = //
243
- vaddq_s32(ab_vec, vaddq_s32(vmovl_s16(vget_high_s16(ab_part_vec)), vmovl_s16(vget_low_s16(ab_part_vec))));
244
- a2_vec = //
245
- vaddq_s32(a2_vec, vaddq_s32(vmovl_s16(vget_high_s16(a2_part_vec)), vmovl_s16(vget_low_s16(a2_part_vec))));
246
- b2_vec = //
247
- vaddq_s32(b2_vec, vaddq_s32(vmovl_s16(vget_high_s16(b2_part_vec)), vmovl_s16(vget_low_s16(b2_part_vec))));
279
+ ab_vec = vaddq_s32(ab_vec, vaddq_s32(vmovl_s16(vget_high_s16(ab_part_vec)), //
280
+ vmovl_s16(vget_low_s16(ab_part_vec))));
281
+ a2_vec = vaddq_s32(a2_vec, vaddq_s32(vmovl_s16(vget_high_s16(a2_part_vec)), //
282
+ vmovl_s16(vget_low_s16(a2_part_vec))));
283
+ b2_vec = vaddq_s32(b2_vec, vaddq_s32(vmovl_s16(vget_high_s16(b2_part_vec)), //
284
+ vmovl_s16(vget_low_s16(b2_part_vec))));
248
285
  }
249
- #endif
250
286
 
251
287
  int32x2_t ab_part = vadd_s32(vget_high_s32(ab_vec), vget_low_s32(ab_vec));
252
288
  int32_t ab = vget_lane_s32(vpadd_s32(ab_part, ab_part), 0);
@@ -255,14 +291,29 @@ inline static simsimd_f32_t simsimd_cos_i8x16_neon(int8_t const* a, int8_t const
255
291
  int32x2_t b2_part = vadd_s32(vget_high_s32(b2_vec), vget_low_s32(b2_vec));
256
292
  int32_t b2 = vget_lane_s32(vpadd_s32(b2_part, b2_part), 0);
257
293
  return 1 - ab / (sqrt(a2) * sqrt(b2));
258
- #else
259
- (void)a, (void)b, (void)d;
260
- return -1;
261
- #endif
262
294
  }
263
295
 
264
- inline static simsimd_f32_t simsimd_cos_f32x4_neon(simsimd_f32_t const* a, simsimd_f32_t const* b, size_t d) {
265
- #if defined(__ARM_NEON)
296
+ __attribute__((target("+simd"))) inline static simsimd_f32_t //
297
+ simsimd_l2sq_i8x16_neon(int8_t const* a, int8_t const* b, size_t d) {
298
+
299
+ int32x4_t d2_vec = vdupq_n_s32(0);
300
+ for (size_t i = 0; i != d; i += 8) {
301
+ int16x8_t a_vec = vmovl_s8(vld1_s8(a + i));
302
+ int16x8_t b_vec = vmovl_s8(vld1_s8(b + i));
303
+ int16x8_t d2_part_vec = vsubq_s16(a_vec, b_vec);
304
+ d2_part_vec = vmulq_s16(d2_part_vec, d2_part_vec);
305
+ d2_vec = vaddq_s32(d2_vec, vaddq_s32(vmovl_s16(vget_high_s16(d2_part_vec)), //
306
+ vmovl_s16(vget_low_s16(d2_part_vec))));
307
+ }
308
+
309
+ int32x2_t d2_part = vadd_s32(vget_high_s32(d2_vec), vget_low_s32(d2_vec));
310
+ int32_t d2 = vget_lane_s32(vpadd_s32(d2_part, d2_part), 0);
311
+ return d2;
312
+ }
313
+
314
+ __attribute__((target("+simd"))) inline static simsimd_f32_t //
315
+ simsimd_cos_f32x4_neon(simsimd_f32_t const* a, simsimd_f32_t const* b, size_t d) {
316
+
266
317
  float32x4_t ab_vec = vdupq_n_f32(0);
267
318
  float32x4_t a2_vec = vdupq_n_f32(0);
268
319
  float32x4_t b2_vec = vdupq_n_f32(0);
@@ -277,14 +328,11 @@ inline static simsimd_f32_t simsimd_cos_f32x4_neon(simsimd_f32_t const* a, simsi
277
328
  simsimd_f32_t a2 = vaddvq_f32(a2_vec);
278
329
  simsimd_f32_t b2 = vaddvq_f32(b2_vec);
279
330
  return 1 - ab / (sqrt(a2) * sqrt(b2));
280
- #else
281
- (void)a, (void)b, (void)d;
282
- return -1;
283
- #endif
284
331
  }
285
332
 
286
- inline static simsimd_f32_t simsimd_hamming_b1x128_sve(uint8_t const* a, uint8_t const* b, size_t d) {
287
- #if defined(__ARM_NEON)
333
+ __attribute__((target("+sve"))) inline static simsimd_f32_t //
334
+ simsimd_hamming_b1x128_sve(uint8_t const* a, uint8_t const* b, size_t d) {
335
+
288
336
  /// Contains 16x 8-bit integers with running population count sums.
289
337
  uint8x16_t d_vec = vdupq_n_u8(0);
290
338
  for (size_t i = 0; i != d; i += 16) {
@@ -294,52 +342,30 @@ inline static simsimd_f32_t simsimd_hamming_b1x128_sve(uint8_t const* a, uint8_t
294
342
  d_vec = vaddq_u8(d_vec, vcntq_u8(a_xor_b_vec));
295
343
  }
296
344
  return 1 - vaddvq_u8(d_vec);
297
- #else
298
- (void)a, (void)b, (void)d;
299
- return -1;
300
- #endif
301
345
  }
346
+ #endif // SIMSIMD_TARGET_ARM_NEON
347
+ #endif // SIMSIMD_TARGET_ARM
302
348
 
303
- inline static simsimd_f32_t simsimd_dot_f32x4_avx2(simsimd_f32_t const* a, simsimd_f32_t const* b, size_t d) {
304
- #if defined(__AVX2__)
349
+ #if SIMSIMD_TARGET_X86
350
+ #if SIMSIMD_TARGET_X86_AVX2
351
+
352
+ __attribute__((target("avx2"))) //
353
+ __attribute__((target("fma"))) inline static simsimd_f32_t //
354
+ simsimd_dot_f32x4_avx2(simsimd_f32_t const* a, simsimd_f32_t const* b, size_t d) {
305
355
  __m128 ab_vec = _mm_set1_ps(0);
306
356
  for (size_t i = 0; i != d; i += 4)
307
357
  ab_vec = _mm_fmadd_ps(_mm_loadu_ps(a + i), _mm_loadu_ps(b + i), ab_vec);
308
358
  ab_vec = _mm_hadd_ps(ab_vec, ab_vec);
309
359
  ab_vec = _mm_hadd_ps(ab_vec, ab_vec);
310
- simsimd_f32i32_t ab_union;
360
+ union simsimd_f32i32_t ab_union;
311
361
  ab_union.i = _mm_cvtsi128_si32(_mm_castps_si128(ab_vec));
312
362
  return 1 - ab_union.f;
313
- #else
314
- (void)a, (void)b, (void)d;
315
- return -1;
316
- #endif
317
363
  }
318
364
 
319
- inline static simsimd_f32_t simsimd_cos_f16x16_avx512(simsimd_f16_t const* a, simsimd_f16_t const* b, size_t d) {
320
- #if defined(__AVX512F__)
321
- __m512 ab_vec = _mm512_set1_ps(0);
322
- __m512 a2_vec = _mm512_set1_ps(0);
323
- __m512 b2_vec = _mm512_set1_ps(0);
324
- for (size_t i = 0; i != d; i += 16) {
325
- __m512 a_vec = _mm512_cvtxph_ps(_mm256_loadu_ph(a + i));
326
- __m512 b_vec = _mm512_cvtxph_ps(_mm256_loadu_ph(b + i));
327
- ab_vec = _mm512_fmadd_ps(a_vec, b_vec, ab_vec);
328
- a2_vec = _mm512_fmadd_ps(a_vec, a_vec, a2_vec);
329
- b2_vec = _mm512_fmadd_ps(b_vec, b_vec, b2_vec);
330
- }
331
- simsimd_f32_t ab = _mm512_reduce_add_ps(ab_vec);
332
- simsimd_f32_t a2 = _mm512_reduce_add_ps(a2_vec);
333
- simsimd_f32_t b2 = _mm512_reduce_add_ps(b2_vec);
334
- return 1 - ab / (sqrt(a2) * sqrt(b2));
335
- #else
336
- (void)a, (void)b, (void)d;
337
- return -1;
338
- #endif
339
- }
365
+ __attribute__((target("avx2"))) //
366
+ __attribute__((target("fma"))) inline static simsimd_f32_t //
367
+ simsimd_cos_f32x4_avx2(simsimd_f32_t const* a, simsimd_f32_t const* b, size_t d) {
340
368
 
341
- inline static simsimd_f32_t simsimd_cos_f32x4_avx2(simsimd_f32_t const* a, simsimd_f32_t const* b, size_t d) {
342
- #if defined(__AVX2__)
343
369
  __m128 ab_vec = _mm_set1_ps(0);
344
370
  __m128 a2_vec = _mm_set1_ps(0);
345
371
  __m128 b2_vec = _mm_set1_ps(0);
@@ -356,18 +382,143 @@ inline static simsimd_f32_t simsimd_cos_f32x4_avx2(simsimd_f32_t const* a, simsi
356
382
  a2_vec = _mm_hadd_ps(a2_vec, a2_vec);
357
383
  b2_vec = _mm_hadd_ps(b2_vec, b2_vec);
358
384
  b2_vec = _mm_hadd_ps(b2_vec, b2_vec);
359
- simsimd_f32i32_t ab_union = {_mm_cvtsi128_si32(_mm_castps_si128(ab_vec))};
360
- simsimd_f32i32_t a2_union = {_mm_cvtsi128_si32(_mm_castps_si128(a2_vec))};
361
- simsimd_f32i32_t b2_union = {_mm_cvtsi128_si32(_mm_castps_si128(b2_vec))};
385
+ union simsimd_f32i32_t ab_union = {_mm_cvtsi128_si32(_mm_castps_si128(ab_vec))};
386
+ union simsimd_f32i32_t a2_union = {_mm_cvtsi128_si32(_mm_castps_si128(a2_vec))};
387
+ union simsimd_f32i32_t b2_union = {_mm_cvtsi128_si32(_mm_castps_si128(b2_vec))};
362
388
  return 1 - ab_union.f / (sqrt(a2_union.f) * sqrt(b2_union.f));
363
- #else
364
- (void)a, (void)b, (void)d;
365
- return -1;
366
- #endif
367
389
  }
368
390
 
369
- inline static simsimd_f32_t simsimd_hamming_b1x128_avx512(uint8_t const* a, uint8_t const* b, size_t d) {
370
- #if defined(__AVX512VPOPCNTDQ__)
391
+ __attribute__((target("avx2"))) //
392
+ __attribute__((target("fma"))) inline static simsimd_f32_t //
393
+ simsimd_l2sq_f32x4_avx2(simsimd_f32_t const* a, simsimd_f32_t const* b, size_t d) {
394
+ __m128 sum_vec = _mm_set1_ps(0);
395
+ for (size_t i = 0; i != d; i += 4) {
396
+ __m128 diff_vec = _mm_sub_ps(_mm_loadu_ps(a + i), _mm_loadu_ps(b + i));
397
+ sum_vec = _mm_fmadd_ps(diff_vec, diff_vec, sum_vec);
398
+ }
399
+ sum_vec = _mm_hadd_ps(sum_vec, sum_vec);
400
+ sum_vec = _mm_hadd_ps(sum_vec, sum_vec);
401
+ float result[1];
402
+ _mm_store_ss(result, sum_vec);
403
+ return result[0];
404
+ }
405
+
406
+ __attribute__((target("avx2"))) //
407
+ __attribute__((target("f16c"))) //
408
+ __attribute__((target("fma"))) inline static simsimd_f32_t //
409
+ simsimd_dot_f16x8_avx2(simsimd_f16_t const* a, simsimd_f16_t const* b, size_t d) {
410
+ __m128 ab_vec = _mm_set1_ps(0);
411
+ for (size_t i = 0; i != d; i += 8) {
412
+ __m128 a_vec = _mm_cvtph_ps(_mm_loadu_si128((__m128i const*)(a + i)));
413
+ __m128 b_vec = _mm_cvtph_ps(_mm_loadu_si128((__m128i const*)(b + i)));
414
+ ab_vec = _mm_fmadd_ps(a_vec, b_vec, ab_vec);
415
+ }
416
+ ab_vec = _mm_hadd_ps(ab_vec, ab_vec);
417
+ ab_vec = _mm_hadd_ps(ab_vec, ab_vec);
418
+ float result[1];
419
+ _mm_store_ss(result, ab_vec);
420
+ return 1 - result[0];
421
+ }
422
+
423
+ __attribute__((target("avx2"))) //
424
+ __attribute__((target("f16c"))) //
425
+ __attribute__((target("fma"))) inline static simsimd_f32_t //
426
+ simsimd_cos_f16x8_avx2(simsimd_f16_t const* a, simsimd_f16_t const* b, size_t d) {
427
+ __m128 ab_vec = _mm_set1_ps(0);
428
+ __m128 a2_vec = _mm_set1_ps(0);
429
+ __m128 b2_vec = _mm_set1_ps(0);
430
+ for (size_t i = 0; i != d; i += 8) {
431
+ __m128 a_vec = _mm_cvtph_ps(_mm_loadu_si128((__m128i const*)(a + i)));
432
+ __m128 b_vec = _mm_cvtph_ps(_mm_loadu_si128((__m128i const*)(b + i)));
433
+ ab_vec = _mm_fmadd_ps(a_vec, b_vec, ab_vec);
434
+ a2_vec = _mm_fmadd_ps(a_vec, a_vec, a2_vec);
435
+ b2_vec = _mm_fmadd_ps(b_vec, b_vec, b2_vec);
436
+ }
437
+ ab_vec = _mm_hadd_ps(ab_vec, ab_vec);
438
+ ab_vec = _mm_hadd_ps(ab_vec, ab_vec);
439
+ a2_vec = _mm_hadd_ps(a2_vec, a2_vec);
440
+ a2_vec = _mm_hadd_ps(a2_vec, a2_vec);
441
+ b2_vec = _mm_hadd_ps(b2_vec, b2_vec);
442
+ b2_vec = _mm_hadd_ps(b2_vec, b2_vec);
443
+ float ab_result[1], a2_result[1], b2_result[1];
444
+ _mm_store_ss(ab_result, ab_vec);
445
+ _mm_store_ss(a2_result, a2_vec);
446
+ _mm_store_ss(b2_result, b2_vec);
447
+ return 1 - ab_result[0] / (sqrt(a2_result[0]) * sqrt(b2_result[0]));
448
+ }
449
+
450
+ __attribute__((target("avx2"))) //
451
+ __attribute__((target("f16c"))) //
452
+ __attribute__((target("fma"))) inline static simsimd_f32_t //
453
+ simsimd_l2sq_f16x8_avx2(simsimd_f16_t const* a, simsimd_f16_t const* b, size_t d) {
454
+ __m128 sum_vec = _mm_set1_ps(0);
455
+ for (size_t i = 0; i != d; i += 8) {
456
+ __m128 a_vec = _mm_cvtph_ps(_mm_loadu_si128((__m128i const*)(a + i)));
457
+ __m128 b_vec = _mm_cvtph_ps(_mm_loadu_si128((__m128i const*)(b + i)));
458
+ __m128 diff_vec = _mm_sub_ps(a_vec, b_vec);
459
+ sum_vec = _mm_fmadd_ps(diff_vec, diff_vec, sum_vec);
460
+ }
461
+ sum_vec = _mm_hadd_ps(sum_vec, sum_vec);
462
+ sum_vec = _mm_hadd_ps(sum_vec, sum_vec);
463
+ float result[1];
464
+ _mm_store_ss(result, sum_vec);
465
+ return result[0];
466
+ }
467
+
468
+ #endif // SIMSIMD_TARGET_X86_AVX2
469
+
470
+ #if SIMSIMD_TARGET_X86_AVX512
471
+
472
+ __attribute__((target("avx512fp16"))) //
473
+ __attribute__((target("avx512f"))) inline static simsimd_f32_t //
474
+ simsimd_dot_f16x16_avx512(simsimd_f16_t const* a, simsimd_f16_t const* b, size_t d) {
475
+ __m512 ab_vec = _mm512_set1_ps(0);
476
+ for (size_t i = 0; i != d; i += 16) {
477
+ __m512 a_vec = _mm512_cvtxph_ps(_mm256_loadu_ph(a + i));
478
+ __m512 b_vec = _mm512_cvtxph_ps(_mm256_loadu_ph(b + i));
479
+ ab_vec = _mm512_fmadd_ps(a_vec, b_vec, ab_vec);
480
+ }
481
+ simsimd_f32_t ab = _mm512_reduce_add_ps(ab_vec);
482
+ return 1 - ab;
483
+ }
484
+
485
+ __attribute__((target("avx512fp16"))) //
486
+ __attribute__((target("avx512f"))) inline static simsimd_f32_t //
487
+ simsimd_cos_f16x16_avx512(simsimd_f16_t const* a, simsimd_f16_t const* b, size_t d) {
488
+ __m512 ab_vec = _mm512_set1_ps(0);
489
+ __m512 a2_vec = _mm512_set1_ps(0);
490
+ __m512 b2_vec = _mm512_set1_ps(0);
491
+ for (size_t i = 0; i != d; i += 16) {
492
+ __m512 a_vec = _mm512_cvtxph_ps(_mm256_loadu_ph(a + i));
493
+ __m512 b_vec = _mm512_cvtxph_ps(_mm256_loadu_ph(b + i));
494
+ ab_vec = _mm512_fmadd_ps(a_vec, b_vec, ab_vec);
495
+ a2_vec = _mm512_fmadd_ps(a_vec, a_vec, a2_vec);
496
+ b2_vec = _mm512_fmadd_ps(b_vec, b_vec, b2_vec);
497
+ }
498
+ simsimd_f32_t ab = _mm512_reduce_add_ps(ab_vec);
499
+ simsimd_f32_t a2 = _mm512_reduce_add_ps(a2_vec);
500
+ simsimd_f32_t b2 = _mm512_reduce_add_ps(b2_vec);
501
+ return 1 - ab / (sqrt(a2) * sqrt(b2));
502
+ }
503
+
504
+ __attribute__((target("avx512fp16"))) //
505
+ __attribute__((target("avx512f"))) inline static simsimd_f32_t //
506
+ simsimd_l2sq_f16x16_avx512(simsimd_f16_t const* a, simsimd_f16_t const* b, size_t d) {
507
+ __m512 sum_vec = _mm512_set1_ps(0);
508
+ for (size_t i = 0; i != d; i += 16) {
509
+ __m512 a_vec = _mm512_cvtxph_ps(_mm256_loadu_ph(a + i));
510
+ __m512 b_vec = _mm512_cvtxph_ps(_mm256_loadu_ph(b + i));
511
+ __m512 diff_vec = _mm512_sub_ps(a_vec, b_vec);
512
+ sum_vec = _mm512_fmadd_ps(diff_vec, diff_vec, sum_vec);
513
+ }
514
+ simsimd_f32_t sum = _mm512_reduce_add_ps(sum_vec);
515
+ return sum;
516
+ }
517
+
518
+ __attribute__((target("avx512vpopcntdq"))) //
519
+ __attribute__((target("avx512vl"))) //
520
+ __attribute__((target("avx512f"))) inline static simsimd_f32_t //
521
+ simsimd_hamming_b1x128_avx512(uint8_t const* a, uint8_t const* b, size_t d) {
371
522
  size_t words = d / 128;
372
523
  uint64_t const* a64 = (uint64_t const*)(a);
373
524
  uint64_t const* b64 = (uint64_t const*)(b);
@@ -380,20 +531,29 @@ inline static simsimd_f32_t simsimd_hamming_b1x128_avx512(uint8_t const* a, uint
380
531
  _mm_xor_si128( //
381
532
  _mm_load_si128((__m128i const*)(a64 + i)), _mm_load_si128((__m128i const*)(b64 + i)))));
382
533
  return _mm_cvtm64_si64(_mm_movepi64_pi64(d_vec)) + _mm_extract_epi64(d_vec, 1);
383
- #else
384
- (void)a, (void)b, (void)d;
385
- return -1;
386
- #endif
387
534
  }
388
535
 
389
- inline static simsimd_f32_t simsimd_tanimoto_b1x8_naive(uint8_t const* a, uint8_t const* b, size_t d) {
536
+ #endif // SIMSIMD_TARGET_X86_AVX512
537
+ #endif // SIMSIMD_TARGET_X86
538
+
539
+ inline static simsimd_f32_t //
540
+ simsimd_tanimoto_b1x8_naive(uint8_t const* a, uint8_t const* b, size_t d) {
390
541
  size_t and_count = 0, or_count = 0;
391
- for (size_t i = 0; i != d; ++i)
392
- and_count += popcount32(a[i] & b[i]), or_count += popcount32(a[i] | b[i]);
542
+ uint8_t const* a_end = a + d / 8;
543
+ // Misaligned prefix
544
+ for (; a != a_end && (size_t)(a) % 8 != 0; ++a, ++b)
545
+ and_count += popcount32(*a & *b), or_count += popcount32(*a | *b);
546
+ // Properly aligned body
547
+ for (; a + 8 <= a_end; a += 8, b += 8)
548
+ and_count += popcount64(*(uint64_t*)a & *(uint64_t*)b), or_count += popcount64(*(uint64_t*)a | *(uint64_t*)b);
549
+ // Misaligned suffix
550
+ for (; a != a_end; ++a, ++b)
551
+ and_count += popcount32(*a & *b), or_count += popcount32(*a | *b);
393
552
  return 1 - (simsimd_f32_t)(and_count) / or_count;
394
553
  }
395
554
 
396
555
  #undef popcount32
556
+ #undef popcount64
397
557
 
398
558
  #ifdef __cplusplus
399
559
  }
@@ -0,0 +1,54 @@
1
+ /**
2
+ * @brief Collection of Biology-oriented Similarity Measures, SIMD-accelerated with SSE, AVX, NEON, SVE.
3
+ *
4
+ * @author Ash Vardanian
5
+ * @date August 8, 2023
6
+ */
7
+
8
+ #pragma once
9
+
10
+ #include <simsimd/simsimd_chem.h>
11
+
12
+ #ifdef _MSC_VER
13
+ #define hash32
14
+ #define hash64
15
+ #else
16
+ #define hash32
17
+ #define hash64
18
+ #endif
19
+
20
+ #ifdef __cplusplus
21
+ extern "C" {
22
+ #endif
23
+
24
+ /**
25
+ * @brief Used to compute the similarity of two DNA or RNA sequences in "TwoBit" form.
26
+ *
27
+ * By design uses a separate bit-set for every string, expanding the compressed
28
+ * representation into bitsets, and then computes the tanimoto distance on those.
29
+ *
30
+ * Optimal `length_hash` should be derived from `max(a_length, b_length)`, as
31
+ */
32
+ inline static simsimd_f32_t simsimd_tanimoto_twobit( //
33
+ uint8_t const* a_chars, uint8_t const* b_chars, //
34
+ size_t a_length, size_t b_length, //
35
+ uint8_t* a_hash, uint8_t* b_hash, size_t length_hash) {
36
+
37
+ // uint64 -> 32-gram
38
+ // uint32 -> 16-gram
39
+ uint64_t last_word = 0;
40
+ for (size_t i = 0; i + 8 <= a_length; i += 8) {
41
+ uint64_t a_word;
42
+ memcpy(&a_word, a_chars, 8);
43
+ }
44
+
45
+ size_t b_words = (b_length + 7) / 8;
46
+
47
+ return simsimd_tanimoto_b1x8_naive(a_hash, b_hash, length_hash);
48
+ }
49
+
50
+ #undef popcount64
51
+
52
+ #ifdef __cplusplus
53
+ }
54
+ #endif