usearch 2.16.8 → 2.16.10
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/README.md +1 -1
- package/include/usearch/index.hpp +1 -1
- package/package.json +1 -1
- package/prebuilds/darwin-arm64+x64/usearch.node +0 -0
- package/prebuilds/linux-arm64/usearch.node +0 -0
- package/prebuilds/linux-x64/usearch.node +0 -0
- package/simsimd/CONTRIBUTING.md +69 -12
- package/simsimd/Cargo.lock +1 -1
- package/simsimd/Cargo.toml +1 -1
- package/simsimd/README.md +253 -40
- package/simsimd/c/lib.c +114 -109
- package/simsimd/include/simsimd/binary.h +33 -17
- package/simsimd/include/simsimd/curved.h +867 -129
- package/simsimd/include/simsimd/dot.h +562 -569
- package/simsimd/include/simsimd/probability.h +14 -9
- package/simsimd/include/simsimd/simsimd.h +102 -46
- package/simsimd/include/simsimd/spatial.h +28 -18
- package/simsimd/include/simsimd/types.h +24 -0
- package/simsimd/javascript/fallback.ts +26 -15
- package/simsimd/javascript/simsimd.ts +1 -1
- package/simsimd/package-lock.json +10 -9
- package/simsimd/package.json +2 -2
- package/simsimd/rust/lib.rs +218 -5
- package/simsimd/scripts/bench.cxx +156 -97
- package/simsimd/scripts/test.c +14 -8
- package/simsimd/scripts/test.mjs +158 -145
- package/simsimd/scripts/test.py +71 -52
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
|
|
65
|
-
SIMSIMD_DYNAMIC void simsimd_##name##_##extension(simsimd_##
|
|
66
|
-
simsimd_size_t n,
|
|
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
|
|
100
|
-
SIMSIMD_DYNAMIC void simsimd_##name##_##extension(
|
|
101
|
-
|
|
102
|
-
|
|
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
|
|
132
|
-
SIMSIMD_DYNAMIC void simsimd_##name##_##extension(
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
static
|
|
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
|
|
147
|
-
SIMSIMD_DECLARATION_DENSE(dot, u8
|
|
148
|
-
SIMSIMD_DECLARATION_DENSE(dot, f16
|
|
149
|
-
SIMSIMD_DECLARATION_DENSE(dot, bf16
|
|
150
|
-
SIMSIMD_DECLARATION_DENSE(dot, f32
|
|
151
|
-
SIMSIMD_DECLARATION_DENSE(dot, f64
|
|
152
|
-
SIMSIMD_DECLARATION_DENSE(dot, f16c
|
|
153
|
-
SIMSIMD_DECLARATION_DENSE(dot, bf16c
|
|
154
|
-
SIMSIMD_DECLARATION_DENSE(dot, f32c
|
|
155
|
-
SIMSIMD_DECLARATION_DENSE(dot, f64c
|
|
156
|
-
SIMSIMD_DECLARATION_DENSE(vdot, f16c
|
|
157
|
-
SIMSIMD_DECLARATION_DENSE(vdot, bf16c
|
|
158
|
-
SIMSIMD_DECLARATION_DENSE(vdot, f32c
|
|
159
|
-
SIMSIMD_DECLARATION_DENSE(vdot, f64c
|
|
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
|
|
163
|
-
SIMSIMD_DECLARATION_DENSE(cos, u8
|
|
164
|
-
SIMSIMD_DECLARATION_DENSE(cos, f16
|
|
165
|
-
SIMSIMD_DECLARATION_DENSE(cos, bf16
|
|
166
|
-
SIMSIMD_DECLARATION_DENSE(cos, f32
|
|
167
|
-
SIMSIMD_DECLARATION_DENSE(cos, f64
|
|
168
|
-
SIMSIMD_DECLARATION_DENSE(l2sq, i8
|
|
169
|
-
SIMSIMD_DECLARATION_DENSE(l2sq, u8
|
|
170
|
-
SIMSIMD_DECLARATION_DENSE(l2sq, f16
|
|
171
|
-
SIMSIMD_DECLARATION_DENSE(l2sq, bf16
|
|
172
|
-
SIMSIMD_DECLARATION_DENSE(l2sq, f32
|
|
173
|
-
SIMSIMD_DECLARATION_DENSE(l2sq, f64
|
|
174
|
-
SIMSIMD_DECLARATION_DENSE(l2, i8
|
|
175
|
-
SIMSIMD_DECLARATION_DENSE(l2, u8
|
|
176
|
-
SIMSIMD_DECLARATION_DENSE(l2, f16
|
|
177
|
-
SIMSIMD_DECLARATION_DENSE(l2, bf16
|
|
178
|
-
SIMSIMD_DECLARATION_DENSE(l2, f32
|
|
179
|
-
SIMSIMD_DECLARATION_DENSE(l2, 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
|
|
183
|
-
SIMSIMD_DECLARATION_DENSE(jaccard, 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
|
|
187
|
-
SIMSIMD_DECLARATION_DENSE(kl, bf16
|
|
188
|
-
SIMSIMD_DECLARATION_DENSE(kl, f32
|
|
189
|
-
SIMSIMD_DECLARATION_DENSE(kl, f64
|
|
190
|
-
SIMSIMD_DECLARATION_DENSE(js, f16
|
|
191
|
-
SIMSIMD_DECLARATION_DENSE(js, bf16
|
|
192
|
-
SIMSIMD_DECLARATION_DENSE(js, f32
|
|
193
|
-
SIMSIMD_DECLARATION_DENSE(js, 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
|
|
201
|
-
SIMSIMD_DECLARATION_CURVED(
|
|
202
|
-
SIMSIMD_DECLARATION_CURVED(
|
|
203
|
-
SIMSIMD_DECLARATION_CURVED(
|
|
204
|
-
SIMSIMD_DECLARATION_CURVED(bilinear,
|
|
205
|
-
SIMSIMD_DECLARATION_CURVED(mahalanobis,
|
|
206
|
-
SIMSIMD_DECLARATION_CURVED(bilinear,
|
|
207
|
-
SIMSIMD_DECLARATION_CURVED(
|
|
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
|
|
211
|
-
SIMSIMD_DECLARATION_FMA(fma, f32
|
|
212
|
-
SIMSIMD_DECLARATION_FMA(fma, f16
|
|
213
|
-
SIMSIMD_DECLARATION_FMA(fma, bf16
|
|
214
|
-
SIMSIMD_DECLARATION_FMA(fma, i8
|
|
215
|
-
SIMSIMD_DECLARATION_FMA(fma, u8
|
|
216
|
-
SIMSIMD_DECLARATION_WSUM(wsum, f64
|
|
217
|
-
SIMSIMD_DECLARATION_WSUM(wsum, f32
|
|
218
|
-
SIMSIMD_DECLARATION_WSUM(wsum, f16
|
|
219
|
-
SIMSIMD_DECLARATION_WSUM(wsum, bf16
|
|
220
|
-
SIMSIMD_DECLARATION_WSUM(wsum, i8
|
|
221
|
-
SIMSIMD_DECLARATION_WSUM(wsum, 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((
|
|
263
|
-
simsimd_dot_bf16c((
|
|
264
|
-
simsimd_dot_f32c((
|
|
265
|
-
simsimd_dot_f64c((
|
|
266
|
-
simsimd_vdot_f16c((
|
|
267
|
-
simsimd_vdot_bf16c((
|
|
268
|
-
simsimd_vdot_f32c((
|
|
269
|
-
simsimd_vdot_f64c((
|
|
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 `
|
|
93
|
-
uint32x4_t sum32 = vpaddlq_u16(sum16);
|
|
94
|
-
uint64x2_t sum64 = vpaddlq_u32(sum32);
|
|
95
|
-
|
|
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);
|