usearch 2.21.1 → 2.21.3
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 +22 -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 +16 -9
- package/simsimd/Cargo.toml +7 -11
- package/simsimd/MANIFEST.in +1 -0
- package/simsimd/README.md +21 -20
- package/simsimd/build.rs +4 -3
- package/simsimd/c/lib.c +1 -1
- package/simsimd/include/simsimd/binary.h +11 -10
- package/simsimd/include/simsimd/dot.h +4 -4
- package/simsimd/include/simsimd/geospatial.h +1 -1
- package/simsimd/include/simsimd/mesh.h +1 -1
- package/simsimd/include/simsimd/probability.h +14 -14
- package/simsimd/include/simsimd/simsimd.h +86 -6
- package/simsimd/include/simsimd/sparse.h +60 -105
- package/simsimd/include/simsimd/types.h +4 -3
- package/simsimd/javascript/fallback.ts +10 -14
- package/simsimd/javascript/lib.c +1 -1
- package/simsimd/javascript/simsimd.ts +4 -4
- package/simsimd/package.json +1 -1
- package/simsimd/rust/lib.rs +288 -130
- package/simsimd/scripts/bench.cxx +38 -7
- package/simsimd/scripts/bench.rs +876 -0
- package/simsimd/scripts/test.py +1 -1
- package/simsimd/Cargo.lock +0 -713
- package/simsimd/scripts/bench_cosine.rs +0 -41
- package/simsimd/scripts/bench_native.rs +0 -272
- package/simsimd/scripts/bench_sqeuclidean.rs +0 -41
|
@@ -1,41 +0,0 @@
|
|
|
1
|
-
//! Cosine Similarity Benchmark
|
|
2
|
-
//!
|
|
3
|
-
//! Compares SimSIMD vs native Rust implementations using Criterion.
|
|
4
|
-
//! Run with:
|
|
5
|
-
//!
|
|
6
|
-
//! ```bash
|
|
7
|
-
//! cargo bench --bench bench_cosine
|
|
8
|
-
//! ```
|
|
9
|
-
use criterion::{criterion_group, criterion_main, BenchmarkId, Criterion};
|
|
10
|
-
use simsimd::SpatialSimilarity as SimSIMD;
|
|
11
|
-
|
|
12
|
-
mod native;
|
|
13
|
-
|
|
14
|
-
const DIMENSIONS: usize = 1536;
|
|
15
|
-
|
|
16
|
-
pub fn cos_benchmark(c: &mut Criterion) {
|
|
17
|
-
let inputs: (Vec<f32>, Vec<f32>) = (
|
|
18
|
-
native::generate_random_vector(DIMENSIONS),
|
|
19
|
-
native::generate_random_vector(DIMENSIONS),
|
|
20
|
-
);
|
|
21
|
-
|
|
22
|
-
let mut group = c.benchmark_group("SIMD Cosine");
|
|
23
|
-
|
|
24
|
-
for i in 0..=5 {
|
|
25
|
-
group.bench_with_input(BenchmarkId::new("SimSIMD", i), &i, |b, _| {
|
|
26
|
-
b.iter(|| SimSIMD::cosine(&inputs.0, &inputs.1))
|
|
27
|
-
});
|
|
28
|
-
group.bench_with_input(BenchmarkId::new("Rust Procedural", i), &i, |b, _| {
|
|
29
|
-
b.iter(|| native::baseline_cos_procedural(&inputs.0, &inputs.1))
|
|
30
|
-
});
|
|
31
|
-
group.bench_with_input(BenchmarkId::new("Rust Functional", i), &i, |b, _| {
|
|
32
|
-
b.iter(|| native::baseline_cos_functional(&inputs.0, &inputs.1))
|
|
33
|
-
});
|
|
34
|
-
group.bench_with_input(BenchmarkId::new("Rust Unrolled", i), &i, |b, _| {
|
|
35
|
-
b.iter(|| native::baseline_cos_unrolled(&inputs.0, &inputs.1))
|
|
36
|
-
});
|
|
37
|
-
}
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
criterion_group!(benches, cos_benchmark);
|
|
41
|
-
criterion_main!(benches);
|
|
@@ -1,272 +0,0 @@
|
|
|
1
|
-
//! Native Rust Baseline Implementations
|
|
2
|
-
//!
|
|
3
|
-
//! Provides baseline implementations of distance functions for benchmarking
|
|
4
|
-
//! against SimSIMD's SIMD-optimized versions.
|
|
5
|
-
#![allow(unused)]
|
|
6
|
-
use rand::Rng;
|
|
7
|
-
|
|
8
|
-
pub(crate) fn generate_random_vector(dim: usize) -> Vec<f32> {
|
|
9
|
-
(0..dim).map(|_| rand::thread_rng().gen()).collect()
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
pub(crate) fn baseline_cos_functional(a: &[f32], b: &[f32]) -> Option<f32> {
|
|
13
|
-
if a.len() != b.len() {
|
|
14
|
-
return None;
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
let (dot_product, norm_a, norm_b) = a
|
|
18
|
-
.iter()
|
|
19
|
-
.zip(b)
|
|
20
|
-
.map(|(a, b)| (a * b, a * a, b * b))
|
|
21
|
-
.fold((0.0, 0.0, 0.0), |acc, x| {
|
|
22
|
-
(acc.0 + x.0, acc.1 + x.1, acc.2 + x.2)
|
|
23
|
-
});
|
|
24
|
-
|
|
25
|
-
Some(1.0 - (dot_product / (norm_a.sqrt() * norm_b.sqrt())))
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
pub(crate) fn baseline_l2sq_functional(a: &[f32], b: &[f32]) -> Option<f32> {
|
|
29
|
-
if a.len() != b.len() {
|
|
30
|
-
return None;
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
Some(a.iter().zip(b).map(|(x, y)| (x - y).powi(2)).sum())
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
pub(crate) fn baseline_cos_procedural(a: &[f32], b: &[f32]) -> Option<f32> {
|
|
37
|
-
if a.len() != b.len() {
|
|
38
|
-
return None;
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
let mut dot_product = 0.0;
|
|
42
|
-
let mut norm_a = 0.0;
|
|
43
|
-
let mut norm_b = 0.0;
|
|
44
|
-
|
|
45
|
-
for i in 0..a.len() {
|
|
46
|
-
dot_product += a[i] * b[i];
|
|
47
|
-
norm_a += a[i] * a[i];
|
|
48
|
-
norm_b += b[i] * b[i];
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
Some(1.0 - (dot_product / (norm_a.sqrt() * norm_b.sqrt())))
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
pub(crate) fn baseline_l2sq_procedural(a: &[f32], b: &[f32]) -> Option<f32> {
|
|
55
|
-
if a.len() != b.len() {
|
|
56
|
-
return None;
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
let mut sum = 0.0;
|
|
60
|
-
for i in 0..a.len() {
|
|
61
|
-
let diff = a[i] - b[i];
|
|
62
|
-
sum += diff * diff;
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
Some(sum)
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
pub(crate) fn baseline_cos_unrolled(a: &[f32], b: &[f32]) -> Option<f32> {
|
|
69
|
-
if a.len() != b.len() {
|
|
70
|
-
return None;
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
let mut i = 0;
|
|
74
|
-
let mut remainder = a.len() % 8;
|
|
75
|
-
|
|
76
|
-
// We do this manual unrolling to allow the compiler to vectorize
|
|
77
|
-
// the loop and avoid some branching even if we're not doing it explicitly.
|
|
78
|
-
// This made a significant difference in benchmarking ~4x
|
|
79
|
-
let mut acc1 = 0.0;
|
|
80
|
-
let mut acc2 = 0.0;
|
|
81
|
-
let mut acc3 = 0.0;
|
|
82
|
-
let mut acc4 = 0.0;
|
|
83
|
-
let mut acc5 = 0.0;
|
|
84
|
-
let mut acc6 = 0.0;
|
|
85
|
-
let mut acc7 = 0.0;
|
|
86
|
-
let mut acc8 = 0.0;
|
|
87
|
-
|
|
88
|
-
let mut norm_a_acc1 = 0.0;
|
|
89
|
-
let mut norm_a_acc2 = 0.0;
|
|
90
|
-
let mut norm_a_acc3 = 0.0;
|
|
91
|
-
let mut norm_a_acc4 = 0.0;
|
|
92
|
-
let mut norm_a_acc5 = 0.0;
|
|
93
|
-
let mut norm_a_acc6 = 0.0;
|
|
94
|
-
let mut norm_a_acc7 = 0.0;
|
|
95
|
-
let mut norm_a_acc8 = 0.0;
|
|
96
|
-
|
|
97
|
-
let mut norm_b_acc1 = 0.0;
|
|
98
|
-
let mut norm_b_acc2 = 0.0;
|
|
99
|
-
let mut norm_b_acc3 = 0.0;
|
|
100
|
-
let mut norm_b_acc4 = 0.0;
|
|
101
|
-
let mut norm_b_acc5 = 0.0;
|
|
102
|
-
let mut norm_b_acc6 = 0.0;
|
|
103
|
-
let mut norm_b_acc7 = 0.0;
|
|
104
|
-
let mut norm_b_acc8 = 0.0;
|
|
105
|
-
|
|
106
|
-
while i < (a.len() - remainder) {
|
|
107
|
-
let a1 = unsafe { *a.get_unchecked(i) };
|
|
108
|
-
let a2 = unsafe { *a.get_unchecked(i + 1) };
|
|
109
|
-
let a3 = unsafe { *a.get_unchecked(i + 2) };
|
|
110
|
-
let a4 = unsafe { *a.get_unchecked(i + 3) };
|
|
111
|
-
let a5 = unsafe { *a.get_unchecked(i + 4) };
|
|
112
|
-
let a6 = unsafe { *a.get_unchecked(i + 5) };
|
|
113
|
-
let a7 = unsafe { *a.get_unchecked(i + 6) };
|
|
114
|
-
let a8 = unsafe { *a.get_unchecked(i + 7) };
|
|
115
|
-
|
|
116
|
-
let b1 = unsafe { *b.get_unchecked(i) };
|
|
117
|
-
let b2 = unsafe { *b.get_unchecked(i + 1) };
|
|
118
|
-
let b3 = unsafe { *b.get_unchecked(i + 2) };
|
|
119
|
-
let b4 = unsafe { *b.get_unchecked(i + 3) };
|
|
120
|
-
let b5 = unsafe { *b.get_unchecked(i + 4) };
|
|
121
|
-
let b6 = unsafe { *b.get_unchecked(i + 5) };
|
|
122
|
-
let b7 = unsafe { *b.get_unchecked(i + 6) };
|
|
123
|
-
let b8 = unsafe { *b.get_unchecked(i + 7) };
|
|
124
|
-
|
|
125
|
-
acc1 += (a1 * b1);
|
|
126
|
-
acc2 += (a2 * b2);
|
|
127
|
-
acc3 += (a3 * b3);
|
|
128
|
-
acc4 += (a4 * b4);
|
|
129
|
-
acc5 += (a5 * b5);
|
|
130
|
-
acc6 += (a6 * b6);
|
|
131
|
-
acc7 += (a7 * b7);
|
|
132
|
-
acc8 += (a8 * b8);
|
|
133
|
-
|
|
134
|
-
norm_a_acc1 += (a1 * a1);
|
|
135
|
-
norm_a_acc2 += (a2 * a2);
|
|
136
|
-
norm_a_acc3 += (a3 * a3);
|
|
137
|
-
norm_a_acc4 += (a4 * a4);
|
|
138
|
-
norm_a_acc5 += (a5 * a5);
|
|
139
|
-
norm_a_acc6 += (a6 * a6);
|
|
140
|
-
norm_a_acc7 += (a7 * a7);
|
|
141
|
-
norm_a_acc8 += (a8 * a8);
|
|
142
|
-
|
|
143
|
-
norm_b_acc1 += (b1 * b1);
|
|
144
|
-
norm_b_acc2 += (b2 * b2);
|
|
145
|
-
norm_b_acc3 += (b3 * b3);
|
|
146
|
-
norm_b_acc4 += (b4 * b4);
|
|
147
|
-
norm_b_acc5 += (b5 * b5);
|
|
148
|
-
norm_b_acc6 += (b6 * b6);
|
|
149
|
-
norm_b_acc7 += (b7 * b7);
|
|
150
|
-
norm_b_acc8 += (b8 * b8);
|
|
151
|
-
|
|
152
|
-
i += 8;
|
|
153
|
-
}
|
|
154
|
-
|
|
155
|
-
acc1 += acc2;
|
|
156
|
-
acc3 += acc4;
|
|
157
|
-
acc5 += acc6;
|
|
158
|
-
acc7 += acc8;
|
|
159
|
-
|
|
160
|
-
norm_a_acc1 += norm_a_acc2;
|
|
161
|
-
norm_a_acc3 += norm_a_acc4;
|
|
162
|
-
norm_a_acc5 += norm_a_acc6;
|
|
163
|
-
norm_a_acc7 += norm_a_acc8;
|
|
164
|
-
|
|
165
|
-
norm_b_acc1 += norm_b_acc2;
|
|
166
|
-
norm_b_acc3 += norm_b_acc4;
|
|
167
|
-
norm_b_acc5 += norm_b_acc6;
|
|
168
|
-
norm_b_acc7 += norm_b_acc8;
|
|
169
|
-
|
|
170
|
-
acc1 += acc3;
|
|
171
|
-
acc5 += acc7;
|
|
172
|
-
|
|
173
|
-
norm_a_acc1 += norm_a_acc3;
|
|
174
|
-
norm_a_acc5 += norm_a_acc7;
|
|
175
|
-
|
|
176
|
-
norm_b_acc1 += norm_b_acc3;
|
|
177
|
-
norm_b_acc5 += norm_b_acc7;
|
|
178
|
-
|
|
179
|
-
acc1 += acc5;
|
|
180
|
-
norm_a_acc1 += norm_a_acc5;
|
|
181
|
-
norm_b_acc1 += norm_b_acc5;
|
|
182
|
-
|
|
183
|
-
while i < a.len() {
|
|
184
|
-
let a = unsafe { *a.get_unchecked(i) };
|
|
185
|
-
let b = unsafe { *b.get_unchecked(i) };
|
|
186
|
-
|
|
187
|
-
acc1 += (a * b);
|
|
188
|
-
norm_a_acc1 += (a * a);
|
|
189
|
-
norm_b_acc1 += (b * b);
|
|
190
|
-
}
|
|
191
|
-
|
|
192
|
-
Some(1.0 - (acc1 / (norm_a_acc1 * norm_b_acc1).sqrt()))
|
|
193
|
-
}
|
|
194
|
-
|
|
195
|
-
pub(crate) fn baseline_l2sq_unrolled(a: &[f32], b: &[f32]) -> Option<f32> {
|
|
196
|
-
if a.len() != b.len() {
|
|
197
|
-
return None;
|
|
198
|
-
}
|
|
199
|
-
let mut i = 0;
|
|
200
|
-
let mut remainder = a.len() % 8;
|
|
201
|
-
|
|
202
|
-
// We do this manual unrolling to allow the compiler to vectorize
|
|
203
|
-
// the loop and avoid some branching even if we're not doing it explicitly.
|
|
204
|
-
// This made a significant difference in benchmarking ~4x
|
|
205
|
-
let mut acc1 = 0.0;
|
|
206
|
-
let mut acc2 = 0.0;
|
|
207
|
-
let mut acc3 = 0.0;
|
|
208
|
-
let mut acc4 = 0.0;
|
|
209
|
-
let mut acc5 = 0.0;
|
|
210
|
-
let mut acc6 = 0.0;
|
|
211
|
-
let mut acc7 = 0.0;
|
|
212
|
-
let mut acc8 = 0.0;
|
|
213
|
-
|
|
214
|
-
while i < (a.len() - remainder) {
|
|
215
|
-
let a1 = unsafe { *a.get_unchecked(i) };
|
|
216
|
-
let a2 = unsafe { *a.get_unchecked(i + 1) };
|
|
217
|
-
let a3 = unsafe { *a.get_unchecked(i + 2) };
|
|
218
|
-
let a4 = unsafe { *a.get_unchecked(i + 3) };
|
|
219
|
-
let a5 = unsafe { *a.get_unchecked(i + 4) };
|
|
220
|
-
let a6 = unsafe { *a.get_unchecked(i + 5) };
|
|
221
|
-
let a7 = unsafe { *a.get_unchecked(i + 6) };
|
|
222
|
-
let a8 = unsafe { *a.get_unchecked(i + 7) };
|
|
223
|
-
|
|
224
|
-
let b1 = unsafe { *b.get_unchecked(i) };
|
|
225
|
-
let b2 = unsafe { *b.get_unchecked(i + 1) };
|
|
226
|
-
let b3 = unsafe { *b.get_unchecked(i + 2) };
|
|
227
|
-
let b4 = unsafe { *b.get_unchecked(i + 3) };
|
|
228
|
-
let b5 = unsafe { *b.get_unchecked(i + 4) };
|
|
229
|
-
let b6 = unsafe { *b.get_unchecked(i + 5) };
|
|
230
|
-
let b7 = unsafe { *b.get_unchecked(i + 6) };
|
|
231
|
-
let b8 = unsafe { *b.get_unchecked(i + 7) };
|
|
232
|
-
|
|
233
|
-
let diff1 = a1 - b1;
|
|
234
|
-
let diff2 = a2 - b2;
|
|
235
|
-
let diff3 = a3 - b3;
|
|
236
|
-
let diff4 = a4 - b4;
|
|
237
|
-
let diff5 = a5 - b5;
|
|
238
|
-
let diff6 = a6 - b6;
|
|
239
|
-
let diff7 = a7 - b7;
|
|
240
|
-
let diff8 = a8 - b8;
|
|
241
|
-
|
|
242
|
-
acc1 += (diff1 * diff1);
|
|
243
|
-
acc2 += (diff2 * diff2);
|
|
244
|
-
acc3 += (diff3 * diff3);
|
|
245
|
-
acc4 += (diff4 * diff4);
|
|
246
|
-
acc5 += (diff5 * diff5);
|
|
247
|
-
acc6 += (diff6 * diff6);
|
|
248
|
-
acc7 += (diff7 * diff7);
|
|
249
|
-
acc8 += (diff8 * diff8);
|
|
250
|
-
|
|
251
|
-
i += 8;
|
|
252
|
-
}
|
|
253
|
-
|
|
254
|
-
acc1 += acc2;
|
|
255
|
-
acc3 += acc4;
|
|
256
|
-
acc5 += acc6;
|
|
257
|
-
acc7 += acc8;
|
|
258
|
-
|
|
259
|
-
acc1 += acc3;
|
|
260
|
-
acc5 += acc7;
|
|
261
|
-
|
|
262
|
-
acc1 += acc5;
|
|
263
|
-
|
|
264
|
-
while i < a.len() {
|
|
265
|
-
let a = unsafe { *a.get_unchecked(i) };
|
|
266
|
-
let b = unsafe { *b.get_unchecked(i) };
|
|
267
|
-
let diff = a - b;
|
|
268
|
-
acc1 += (diff * diff);
|
|
269
|
-
}
|
|
270
|
-
|
|
271
|
-
Some(acc1)
|
|
272
|
-
}
|
|
@@ -1,41 +0,0 @@
|
|
|
1
|
-
//! Squared Euclidean Distance Benchmark
|
|
2
|
-
//!
|
|
3
|
-
//! Compares SimSIMD vs native Rust implementations using Criterion.
|
|
4
|
-
//! Run with:
|
|
5
|
-
//!
|
|
6
|
-
//! ```bash
|
|
7
|
-
//! cargo bench --bench bench_sqeuclidean
|
|
8
|
-
//! ```
|
|
9
|
-
use criterion::{criterion_group, criterion_main, BenchmarkId, Criterion};
|
|
10
|
-
use simsimd::SpatialSimilarity as SimSIMD;
|
|
11
|
-
|
|
12
|
-
mod bench_native;
|
|
13
|
-
|
|
14
|
-
const DIMENSIONS: usize = 1536;
|
|
15
|
-
|
|
16
|
-
pub fn l2sq_benchmark(c: &mut Criterion) {
|
|
17
|
-
let inputs: (Vec<f32>, Vec<f32>) = (
|
|
18
|
-
bench_native::generate_random_vector(DIMENSIONS),
|
|
19
|
-
bench_native::generate_random_vector(DIMENSIONS),
|
|
20
|
-
);
|
|
21
|
-
|
|
22
|
-
let mut group = c.benchmark_group("SIMD SqEuclidean");
|
|
23
|
-
|
|
24
|
-
for i in 0..=5 {
|
|
25
|
-
group.bench_with_input(BenchmarkId::new("SimSIMD", i), &i, |b, _| {
|
|
26
|
-
b.iter(|| SimSIMD::sqeuclidean(&inputs.0, &inputs.1))
|
|
27
|
-
});
|
|
28
|
-
group.bench_with_input(BenchmarkId::new("Rust Procedural", i), &i, |b, _| {
|
|
29
|
-
b.iter(|| bench_native::baseline_l2sq_procedural(&inputs.0, &inputs.1))
|
|
30
|
-
});
|
|
31
|
-
group.bench_with_input(BenchmarkId::new("Rust Functional", i), &i, |b, _| {
|
|
32
|
-
b.iter(|| bench_native::baseline_l2sq_functional(&inputs.0, &inputs.1))
|
|
33
|
-
});
|
|
34
|
-
group.bench_with_input(BenchmarkId::new("Rust Unrolled", i), &i, |b, _| {
|
|
35
|
-
b.iter(|| bench_native::baseline_l2sq_unrolled(&inputs.0, &inputs.1))
|
|
36
|
-
});
|
|
37
|
-
}
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
criterion_group!(benches, l2sq_benchmark);
|
|
41
|
-
criterion_main!(benches);
|