usearch 2.16.8 → 2.16.9

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.
package/simsimd/c/lib.c CHANGED
@@ -61,9 +61,10 @@ extern "C" {
61
61
  // If no metric is found, it returns NaN. We can obtain NaN by dividing 0.0 by 0.0, but that annoys
62
62
  // the MSVC compiler. Instead we can directly write-in the signaling NaN (0x7FF0000000000001)
63
63
  // or the qNaN (0x7FF8000000000000).
64
- #define SIMSIMD_DECLARATION_DENSE(name, extension, type) \
65
- SIMSIMD_DYNAMIC void simsimd_##name##_##extension(simsimd_##type##_t const *a, simsimd_##type##_t const *b, \
66
- simsimd_size_t n, simsimd_distance_t *results) { \
64
+ #define SIMSIMD_DECLARATION_DENSE(name, extension) \
65
+ SIMSIMD_DYNAMIC void simsimd_##name##_##extension(simsimd_##extension##_t const *a, \
66
+ simsimd_##extension##_t const *b, simsimd_size_t n, \
67
+ simsimd_distance_t *results) { \
67
68
  static simsimd_metric_dense_punned_t metric = 0; \
68
69
  if (metric == 0) { \
69
70
  simsimd_capability_t used_capability; \
@@ -96,129 +97,133 @@ extern "C" {
96
97
  metric(a, b, a_length, b_length, result); \
97
98
  }
98
99
 
99
- #define SIMSIMD_DECLARATION_CURVED(name, extension, type) \
100
- SIMSIMD_DYNAMIC void simsimd_##name##_##extension(simsimd_##type##_t const *a, simsimd_##type##_t const *b, \
101
- simsimd_##type##_t const *c, simsimd_size_t n, \
102
- simsimd_distance_t *result) { \
103
- static simsimd_metric_curved_punned_t metric = 0; \
104
- if (metric == 0) { \
105
- simsimd_capability_t used_capability; \
106
- simsimd_find_kernel_punned(simsimd_metric_##name##_k, simsimd_datatype_##extension##_k, \
107
- simsimd_capabilities(), simsimd_cap_any_k, \
108
- (simsimd_kernel_punned_t *)(&metric), &used_capability); \
109
- if (!metric) { \
110
- *(simsimd_u64_t *)result = 0x7FF0000000000001ull; \
111
- return; \
112
- } \
113
- } \
114
- metric(a, b, c, n, result); \
115
- }
116
-
117
- #define SIMSIMD_DECLARATION_FMA(name, extension, type) \
118
- SIMSIMD_DYNAMIC void simsimd_##name##_##extension( \
119
- simsimd_##type##_t const *a, simsimd_##type##_t const *b, simsimd_##type##_t const *c, simsimd_size_t n, \
120
- simsimd_distance_t alpha, simsimd_distance_t beta, simsimd_##type##_t *result) { \
121
- static simsimd_kernel_fma_punned_t metric = 0; \
122
- if (metric == 0) { \
123
- simsimd_capability_t used_capability; \
124
- simsimd_find_kernel_punned(simsimd_metric_##name##_k, simsimd_datatype_##extension##_k, \
125
- simsimd_capabilities(), simsimd_cap_any_k, \
126
- (simsimd_kernel_punned_t *)(&metric), &used_capability); \
127
- } \
128
- metric(a, b, c, n, alpha, beta, result); \
100
+ #define SIMSIMD_DECLARATION_CURVED(name, extension) \
101
+ SIMSIMD_DYNAMIC void simsimd_##name##_##extension( \
102
+ simsimd_##extension##_t const *a, simsimd_##extension##_t const *b, simsimd_##extension##_t const *c, \
103
+ simsimd_size_t n, simsimd_distance_t *result) { \
104
+ static simsimd_metric_curved_punned_t metric = 0; \
105
+ if (metric == 0) { \
106
+ simsimd_capability_t used_capability; \
107
+ simsimd_find_kernel_punned(simsimd_metric_##name##_k, simsimd_datatype_##extension##_k, \
108
+ simsimd_capabilities(), simsimd_cap_any_k, \
109
+ (simsimd_kernel_punned_t *)(&metric), &used_capability); \
110
+ if (!metric) { \
111
+ *(simsimd_u64_t *)result = 0x7FF0000000000001ull; \
112
+ return; \
113
+ } \
114
+ } \
115
+ metric(a, b, c, n, result); \
129
116
  }
130
117
 
131
- #define SIMSIMD_DECLARATION_WSUM(name, extension, type) \
132
- SIMSIMD_DYNAMIC void simsimd_##name##_##extension(simsimd_##type##_t const *a, simsimd_##type##_t const *b, \
133
- simsimd_size_t n, simsimd_distance_t alpha, \
134
- simsimd_distance_t beta, simsimd_##type##_t *result) { \
135
- static simsimd_kernel_wsum_punned_t metric = 0; \
118
+ #define SIMSIMD_DECLARATION_FMA(name, extension) \
119
+ SIMSIMD_DYNAMIC void simsimd_##name##_##extension( \
120
+ simsimd_##extension##_t const *a, simsimd_##extension##_t const *b, simsimd_##extension##_t const *c, \
121
+ simsimd_size_t n, simsimd_distance_t alpha, simsimd_distance_t beta, simsimd_##extension##_t *result) { \
122
+ static simsimd_kernel_fma_punned_t metric = 0; \
136
123
  if (metric == 0) { \
137
124
  simsimd_capability_t used_capability; \
138
125
  simsimd_find_kernel_punned(simsimd_metric_##name##_k, simsimd_datatype_##extension##_k, \
139
126
  simsimd_capabilities(), simsimd_cap_any_k, \
140
127
  (simsimd_kernel_punned_t *)(&metric), &used_capability); \
141
128
  } \
142
- metric(a, b, n, alpha, beta, result); \
129
+ metric(a, b, c, n, alpha, beta, result); \
130
+ }
131
+
132
+ #define SIMSIMD_DECLARATION_WSUM(name, extension) \
133
+ SIMSIMD_DYNAMIC void simsimd_##name##_##extension( \
134
+ simsimd_##extension##_t const *a, simsimd_##extension##_t const *b, simsimd_size_t n, \
135
+ simsimd_distance_t alpha, simsimd_distance_t beta, simsimd_##extension##_t *result) { \
136
+ static simsimd_kernel_wsum_punned_t metric = 0; \
137
+ if (metric == 0) { \
138
+ simsimd_capability_t used_capability; \
139
+ simsimd_find_kernel_punned(simsimd_metric_##name##_k, simsimd_datatype_##extension##_k, \
140
+ simsimd_capabilities(), simsimd_cap_any_k, \
141
+ (simsimd_kernel_punned_t *)(&metric), &used_capability); \
142
+ } \
143
+ metric(a, b, n, alpha, beta, result); \
143
144
  }
144
145
 
145
146
  // Dot products
146
- SIMSIMD_DECLARATION_DENSE(dot, i8, i8)
147
- SIMSIMD_DECLARATION_DENSE(dot, u8, u8)
148
- SIMSIMD_DECLARATION_DENSE(dot, f16, f16)
149
- SIMSIMD_DECLARATION_DENSE(dot, bf16, bf16)
150
- SIMSIMD_DECLARATION_DENSE(dot, f32, f32)
151
- SIMSIMD_DECLARATION_DENSE(dot, f64, f64)
152
- SIMSIMD_DECLARATION_DENSE(dot, f16c, f16)
153
- SIMSIMD_DECLARATION_DENSE(dot, bf16c, bf16)
154
- SIMSIMD_DECLARATION_DENSE(dot, f32c, f32)
155
- SIMSIMD_DECLARATION_DENSE(dot, f64c, f64)
156
- SIMSIMD_DECLARATION_DENSE(vdot, f16c, f16)
157
- SIMSIMD_DECLARATION_DENSE(vdot, bf16c, bf16)
158
- SIMSIMD_DECLARATION_DENSE(vdot, f32c, f32)
159
- SIMSIMD_DECLARATION_DENSE(vdot, f64c, f64)
147
+ SIMSIMD_DECLARATION_DENSE(dot, i8)
148
+ SIMSIMD_DECLARATION_DENSE(dot, u8)
149
+ SIMSIMD_DECLARATION_DENSE(dot, f16)
150
+ SIMSIMD_DECLARATION_DENSE(dot, bf16)
151
+ SIMSIMD_DECLARATION_DENSE(dot, f32)
152
+ SIMSIMD_DECLARATION_DENSE(dot, f64)
153
+ SIMSIMD_DECLARATION_DENSE(dot, f16c)
154
+ SIMSIMD_DECLARATION_DENSE(dot, bf16c)
155
+ SIMSIMD_DECLARATION_DENSE(dot, f32c)
156
+ SIMSIMD_DECLARATION_DENSE(dot, f64c)
157
+ SIMSIMD_DECLARATION_DENSE(vdot, f16c)
158
+ SIMSIMD_DECLARATION_DENSE(vdot, bf16c)
159
+ SIMSIMD_DECLARATION_DENSE(vdot, f32c)
160
+ SIMSIMD_DECLARATION_DENSE(vdot, f64c)
160
161
 
161
162
  // Spatial distances
162
- SIMSIMD_DECLARATION_DENSE(cos, i8, i8)
163
- SIMSIMD_DECLARATION_DENSE(cos, u8, u8)
164
- SIMSIMD_DECLARATION_DENSE(cos, f16, f16)
165
- SIMSIMD_DECLARATION_DENSE(cos, bf16, bf16)
166
- SIMSIMD_DECLARATION_DENSE(cos, f32, f32)
167
- SIMSIMD_DECLARATION_DENSE(cos, f64, f64)
168
- SIMSIMD_DECLARATION_DENSE(l2sq, i8, i8)
169
- SIMSIMD_DECLARATION_DENSE(l2sq, u8, u8)
170
- SIMSIMD_DECLARATION_DENSE(l2sq, f16, f16)
171
- SIMSIMD_DECLARATION_DENSE(l2sq, bf16, bf16)
172
- SIMSIMD_DECLARATION_DENSE(l2sq, f32, f32)
173
- SIMSIMD_DECLARATION_DENSE(l2sq, f64, f64)
174
- SIMSIMD_DECLARATION_DENSE(l2, i8, i8)
175
- SIMSIMD_DECLARATION_DENSE(l2, u8, u8)
176
- SIMSIMD_DECLARATION_DENSE(l2, f16, f16)
177
- SIMSIMD_DECLARATION_DENSE(l2, bf16, bf16)
178
- SIMSIMD_DECLARATION_DENSE(l2, f32, f32)
179
- SIMSIMD_DECLARATION_DENSE(l2, f64, f64)
163
+ SIMSIMD_DECLARATION_DENSE(cos, i8)
164
+ SIMSIMD_DECLARATION_DENSE(cos, u8)
165
+ SIMSIMD_DECLARATION_DENSE(cos, f16)
166
+ SIMSIMD_DECLARATION_DENSE(cos, bf16)
167
+ SIMSIMD_DECLARATION_DENSE(cos, f32)
168
+ SIMSIMD_DECLARATION_DENSE(cos, f64)
169
+ SIMSIMD_DECLARATION_DENSE(l2sq, i8)
170
+ SIMSIMD_DECLARATION_DENSE(l2sq, u8)
171
+ SIMSIMD_DECLARATION_DENSE(l2sq, f16)
172
+ SIMSIMD_DECLARATION_DENSE(l2sq, bf16)
173
+ SIMSIMD_DECLARATION_DENSE(l2sq, f32)
174
+ SIMSIMD_DECLARATION_DENSE(l2sq, f64)
175
+ SIMSIMD_DECLARATION_DENSE(l2, i8)
176
+ SIMSIMD_DECLARATION_DENSE(l2, u8)
177
+ SIMSIMD_DECLARATION_DENSE(l2, f16)
178
+ SIMSIMD_DECLARATION_DENSE(l2, bf16)
179
+ SIMSIMD_DECLARATION_DENSE(l2, f32)
180
+ SIMSIMD_DECLARATION_DENSE(l2, f64)
180
181
 
181
182
  // Binary distances
182
- SIMSIMD_DECLARATION_DENSE(hamming, b8, b8)
183
- SIMSIMD_DECLARATION_DENSE(jaccard, b8, b8)
183
+ SIMSIMD_DECLARATION_DENSE(hamming, b8)
184
+ SIMSIMD_DECLARATION_DENSE(jaccard, b8)
184
185
 
185
186
  // Probability distributions
186
- SIMSIMD_DECLARATION_DENSE(kl, f16, f16)
187
- SIMSIMD_DECLARATION_DENSE(kl, bf16, bf16)
188
- SIMSIMD_DECLARATION_DENSE(kl, f32, f32)
189
- SIMSIMD_DECLARATION_DENSE(kl, f64, f64)
190
- SIMSIMD_DECLARATION_DENSE(js, f16, f16)
191
- SIMSIMD_DECLARATION_DENSE(js, bf16, bf16)
192
- SIMSIMD_DECLARATION_DENSE(js, f32, f32)
193
- SIMSIMD_DECLARATION_DENSE(js, f64, f64)
187
+ SIMSIMD_DECLARATION_DENSE(kl, f16)
188
+ SIMSIMD_DECLARATION_DENSE(kl, bf16)
189
+ SIMSIMD_DECLARATION_DENSE(kl, f32)
190
+ SIMSIMD_DECLARATION_DENSE(kl, f64)
191
+ SIMSIMD_DECLARATION_DENSE(js, f16)
192
+ SIMSIMD_DECLARATION_DENSE(js, bf16)
193
+ SIMSIMD_DECLARATION_DENSE(js, f32)
194
+ SIMSIMD_DECLARATION_DENSE(js, f64)
194
195
 
195
196
  // Sparse sets
196
197
  SIMSIMD_DECLARATION_SPARSE(intersect, u16, u16)
197
198
  SIMSIMD_DECLARATION_SPARSE(intersect, u32, u32)
198
199
 
199
200
  // Curved spaces
200
- SIMSIMD_DECLARATION_CURVED(bilinear, f64, f64)
201
- SIMSIMD_DECLARATION_CURVED(mahalanobis, f64, f64)
202
- SIMSIMD_DECLARATION_CURVED(bilinear, f32, f32)
203
- SIMSIMD_DECLARATION_CURVED(mahalanobis, f32, f32)
204
- SIMSIMD_DECLARATION_CURVED(bilinear, f16, f16)
205
- SIMSIMD_DECLARATION_CURVED(mahalanobis, f16, f16)
206
- SIMSIMD_DECLARATION_CURVED(bilinear, bf16, bf16)
207
- SIMSIMD_DECLARATION_CURVED(mahalanobis, bf16, bf16)
201
+ SIMSIMD_DECLARATION_CURVED(bilinear, f64)
202
+ SIMSIMD_DECLARATION_CURVED(bilinear, f64c)
203
+ SIMSIMD_DECLARATION_CURVED(mahalanobis, f64)
204
+ SIMSIMD_DECLARATION_CURVED(bilinear, f32)
205
+ SIMSIMD_DECLARATION_CURVED(bilinear, f32c)
206
+ SIMSIMD_DECLARATION_CURVED(mahalanobis, f32)
207
+ SIMSIMD_DECLARATION_CURVED(bilinear, f16)
208
+ SIMSIMD_DECLARATION_CURVED(bilinear, f16c)
209
+ SIMSIMD_DECLARATION_CURVED(mahalanobis, f16)
210
+ SIMSIMD_DECLARATION_CURVED(bilinear, bf16)
211
+ SIMSIMD_DECLARATION_CURVED(bilinear, bf16c)
212
+ SIMSIMD_DECLARATION_CURVED(mahalanobis, bf16)
208
213
 
209
214
  // Element-wise operations
210
- SIMSIMD_DECLARATION_FMA(fma, f64, f64)
211
- SIMSIMD_DECLARATION_FMA(fma, f32, f32)
212
- SIMSIMD_DECLARATION_FMA(fma, f16, f16)
213
- SIMSIMD_DECLARATION_FMA(fma, bf16, bf16)
214
- SIMSIMD_DECLARATION_FMA(fma, i8, i8)
215
- SIMSIMD_DECLARATION_FMA(fma, u8, u8)
216
- SIMSIMD_DECLARATION_WSUM(wsum, f64, f64)
217
- SIMSIMD_DECLARATION_WSUM(wsum, f32, f32)
218
- SIMSIMD_DECLARATION_WSUM(wsum, f16, f16)
219
- SIMSIMD_DECLARATION_WSUM(wsum, bf16, bf16)
220
- SIMSIMD_DECLARATION_WSUM(wsum, i8, i8)
221
- SIMSIMD_DECLARATION_WSUM(wsum, u8, u8)
215
+ SIMSIMD_DECLARATION_FMA(fma, f64)
216
+ SIMSIMD_DECLARATION_FMA(fma, f32)
217
+ SIMSIMD_DECLARATION_FMA(fma, f16)
218
+ SIMSIMD_DECLARATION_FMA(fma, bf16)
219
+ SIMSIMD_DECLARATION_FMA(fma, i8)
220
+ SIMSIMD_DECLARATION_FMA(fma, u8)
221
+ SIMSIMD_DECLARATION_WSUM(wsum, f64)
222
+ SIMSIMD_DECLARATION_WSUM(wsum, f32)
223
+ SIMSIMD_DECLARATION_WSUM(wsum, f16)
224
+ SIMSIMD_DECLARATION_WSUM(wsum, bf16)
225
+ SIMSIMD_DECLARATION_WSUM(wsum, i8)
226
+ SIMSIMD_DECLARATION_WSUM(wsum, u8)
222
227
 
223
228
  SIMSIMD_DYNAMIC int simsimd_uses_neon(void) { return (simsimd_capabilities() & simsimd_cap_neon_k) != 0; }
224
229
  SIMSIMD_DYNAMIC int simsimd_uses_neon_f16(void) { return (simsimd_capabilities() & simsimd_cap_neon_f16_k) != 0; }
@@ -259,14 +264,14 @@ SIMSIMD_DYNAMIC simsimd_capability_t simsimd_capabilities(void) {
259
264
  simsimd_dot_f32((simsimd_f32_t *)x, (simsimd_f32_t *)x, 0, dummy_results);
260
265
  simsimd_dot_f64((simsimd_f64_t *)x, (simsimd_f64_t *)x, 0, dummy_results);
261
266
 
262
- simsimd_dot_f16c((simsimd_f16_t *)x, (simsimd_f16_t *)x, 0, dummy_results);
263
- simsimd_dot_bf16c((simsimd_bf16_t *)x, (simsimd_bf16_t *)x, 0, dummy_results);
264
- simsimd_dot_f32c((simsimd_f32_t *)x, (simsimd_f32_t *)x, 0, dummy_results);
265
- simsimd_dot_f64c((simsimd_f64_t *)x, (simsimd_f64_t *)x, 0, dummy_results);
266
- simsimd_vdot_f16c((simsimd_f16_t *)x, (simsimd_f16_t *)x, 0, dummy_results);
267
- simsimd_vdot_bf16c((simsimd_bf16_t *)x, (simsimd_bf16_t *)x, 0, dummy_results);
268
- simsimd_vdot_f32c((simsimd_f32_t *)x, (simsimd_f32_t *)x, 0, dummy_results);
269
- simsimd_vdot_f64c((simsimd_f64_t *)x, (simsimd_f64_t *)x, 0, dummy_results);
267
+ simsimd_dot_f16c((simsimd_f16c_t *)x, (simsimd_f16c_t *)x, 0, dummy_results);
268
+ simsimd_dot_bf16c((simsimd_bf16c_t *)x, (simsimd_bf16c_t *)x, 0, dummy_results);
269
+ simsimd_dot_f32c((simsimd_f32c_t *)x, (simsimd_f32c_t *)x, 0, dummy_results);
270
+ simsimd_dot_f64c((simsimd_f64c_t *)x, (simsimd_f64c_t *)x, 0, dummy_results);
271
+ simsimd_vdot_f16c((simsimd_f16c_t *)x, (simsimd_f16c_t *)x, 0, dummy_results);
272
+ simsimd_vdot_bf16c((simsimd_bf16c_t *)x, (simsimd_bf16c_t *)x, 0, dummy_results);
273
+ simsimd_vdot_f32c((simsimd_f32c_t *)x, (simsimd_f32c_t *)x, 0, dummy_results);
274
+ simsimd_vdot_f64c((simsimd_f64c_t *)x, (simsimd_f64c_t *)x, 0, dummy_results);
270
275
 
271
276
  simsimd_cos_i8((simsimd_i8_t *)x, (simsimd_i8_t *)x, 0, dummy_results);
272
277
  simsimd_cos_u8((simsimd_u8_t *)x, (simsimd_u8_t *)x, 0, dummy_results);
@@ -12,8 +12,37 @@
12
12
  * - Arm: NEON, SVE
13
13
  * - x86: Haswell, Ice Lake
14
14
  *
15
+ * The hardest part of optimizing binary similarity measures is the population count operation.
16
+ * It's natively supported by almost every insrtuction set, but the throughput and latency can
17
+ * be suboptimal. There are several ways to optimize this operation:
18
+ *
19
+ * - Lookup tables, mostly using nibbles (4-bit lookups)
20
+ * - Harley-Seal population counts: https://arxiv.org/pdf/1611.07612
21
+ *
22
+ * On binary vectors, when computing Jaccard similarity we can clearly see how the CPU struggles
23
+ * to compute that many population counts. There are several instructions we should keep in mind
24
+ * for future optimizations:
25
+ *
26
+ * - `_mm512_popcnt_epi64` maps to `VPOPCNTQ (ZMM, K, ZMM)`:
27
+ * - On Ice Lake: 3 cycles latency, ports: 1*p5
28
+ * - On Genoa: 2 cycles latency, ports: 1*FP01
29
+ * - `_mm512_shuffle_epi8` maps to `VPSHUFB (ZMM, ZMM, ZMM)`:
30
+ * - On Ice Lake: 1 cycles latency, ports: 1*p5
31
+ * - On Genoa: 2 cycles latency, ports: 1*FP12
32
+ * - `_mm512_sad_epu8` maps to `VPSADBW (ZMM, ZMM, ZMM)`:
33
+ * - On Ice Lake: 3 cycles latency, ports: 1*p5
34
+ * - On Zen4: 3 cycles latency, ports: 1*FP01
35
+ * - `_mm512_tertiarylogic_epi64` maps to `VPTERNLOGQ (ZMM, ZMM, ZMM, I8)`:
36
+ * - On Ice Lake: 1 cycles latency, ports: 1*p05
37
+ * - On Zen4: 1 cycles latency, ports: 1*FP0123
38
+ * - `_mm512_gf2p8mul_epi8` maps to `VPGF2P8AFFINEQB (ZMM, ZMM, ZMM)`:
39
+ * - On Ice Lake: 5 cycles latency, ports: 1*p0
40
+ * - On Zen4: 3 cycles latency, ports: 1*FP01
41
+ *
15
42
  * x86 intrinsics: https://www.intel.com/content/www/us/en/docs/intrinsics-guide/
16
43
  * Arm intrinsics: https://developer.arm.com/architectures/instruction-sets/intrinsics/
44
+ * SSE POPCOUNT experiments by Wojciech Muła: https://github.com/WojciechMula/sse-popcount
45
+ * R&D progress tracker: https://github.com/ashvardanian/SimSIMD/pull/138
17
46
  */
18
47
  #ifndef SIMSIMD_BINARY_H
19
48
  #define SIMSIMD_BINARY_H
@@ -89,10 +118,10 @@ SIMSIMD_INTERNAL simsimd_u32_t _simsimd_reduce_u8x16_neon(uint8x16_t vec) {
89
118
  // Sum the widened halves
90
119
  uint16x8_t sum16 = vaddq_u16(low_half, high_half);
91
120
 
92
- // Now reduce the `uint16x8_t` to a single `uint32_t`
93
- uint32x4_t sum32 = vpaddlq_u16(sum16); // pairwise add into 32-bit integers
94
- uint64x2_t sum64 = vpaddlq_u32(sum32); // pairwise add into 64-bit integers
95
- uint32_t final_sum = vaddvq_u64(sum64); // final horizontal add to 32-bit result
121
+ // Now reduce the `uint16x8_t` to a single `simsimd_u32_t`
122
+ uint32x4_t sum32 = vpaddlq_u16(sum16); // pairwise add into 32-bit integers
123
+ uint64x2_t sum64 = vpaddlq_u32(sum32); // pairwise add into 64-bit integers
124
+ simsimd_u32_t final_sum = vaddvq_u64(sum64); // final horizontal add to 32-bit result
96
125
  return final_sum;
97
126
  }
98
127
 
@@ -321,19 +350,6 @@ SIMSIMD_PUBLIC void simsimd_jaccard_b8_ice(simsimd_b8_t const *a, simsimd_b8_t c
321
350
  simsimd_distance_t *result) {
322
351
 
323
352
  simsimd_size_t intersection = 0, union_ = 0;
324
- //? On such vectors we can clearly see that the CPU struggles to perform this many parallel
325
- //? population counts, because the throughput of Jaccard and Hamming in this case starts to differ.
326
- //? One optimization, aside from Harley-Seal transforms can be using "shuffles" for nibble-popcount
327
- //? lookups, to utilize other ports on the CPU.
328
- //? https://github.com/ashvardanian/SimSIMD/pull/138
329
- //
330
- // - `_mm512_popcnt_epi64` maps to `VPOPCNTQ (ZMM, K, ZMM)`:
331
- // - On Ice Lake: 3 cycles latency, ports: 1*p5
332
- // - On Genoa: 2 cycles latency, ports: 1*FP01
333
- // - `_mm512_shuffle_epi8` maps to `VPSHUFB (ZMM, ZMM, ZMM)`:
334
- // - On Ice Lake: 1 cycles latency, ports: 1*p5
335
- // - On Genoa: 2 cycles latency, ports: 1*FP12
336
- //
337
353
  // It's harder to squeeze out performance from tiny representations, so we unroll the loops for binary metrics.
338
354
  if (n_words <= 64) { // Up to 512 bits.
339
355
  __mmask64 mask = (__mmask64)_bzhi_u64(0xFFFFFFFFFFFFFFFF, n_words);