usearch 2.17.1 → 2.19.4
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 +6 -2
- package/c/lib.cpp +5 -0
- package/c/module.modulemap +4 -0
- package/c/usearch.h +12 -4
- package/cpp/README.md +1 -1
- package/cpp/bench.cpp +1 -1
- package/cpp/test.cpp +110 -16
- package/include/usearch/index.hpp +48 -35
- package/include/usearch/index_dense.hpp +34 -19
- package/include/usearch/index_plugins.hpp +29 -7
- package/javascript/README.md +8 -6
- package/javascript/dist/cjs/usearch.d.ts +9 -4
- package/javascript/dist/cjs/usearch.js +23 -8
- package/javascript/dist/esm/usearch.d.ts +9 -4
- package/javascript/dist/esm/usearch.js +21 -7
- package/javascript/lib.cpp +137 -59
- package/javascript/usearch.test.js +359 -192
- package/javascript/usearch.ts +35 -11
- 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 +23 -8
- package/simsimd/Cargo.lock +1 -1
- package/simsimd/Cargo.toml +1 -1
- package/simsimd/README.md +49 -9
- package/simsimd/build.rs +7 -3
- package/simsimd/c/lib.c +6 -1
- package/simsimd/include/simsimd/binary.h +3 -3
- package/simsimd/include/simsimd/dot.h +2 -2
- package/simsimd/include/simsimd/elementwise.h +26 -17
- package/simsimd/include/simsimd/simsimd.h +125 -49
- package/simsimd/include/simsimd/sparse.h +30 -29
- package/simsimd/include/simsimd/spatial.h +4 -3
- package/simsimd/include/simsimd/types.h +19 -8
- package/simsimd/javascript/lib.c +0 -1
- package/simsimd/package-lock.json +411 -362
- package/simsimd/package.json +9 -9
- package/simsimd/rust/lib.rs +63 -29
- package/simsimd/scripts/bench.cxx +13 -0
- package/simsimd/scripts/bench_cosine.rs +8 -0
- package/simsimd/scripts/bench_native.rs +4 -0
- package/simsimd/scripts/bench_sqeuclidean.rs +8 -0
- package/simsimd/scripts/test.c +22 -0
- package/simsimd/scripts/test.py +80 -4
- package/simsimd/swift/SimSIMD.swift +17 -0
- package/simsimd/swift/Test.swift +36 -0
package/javascript/usearch.ts
CHANGED
|
@@ -16,8 +16,8 @@ type CompiledSearchResult = [
|
|
|
16
16
|
];
|
|
17
17
|
|
|
18
18
|
interface CompiledIndex {
|
|
19
|
-
add(keys: BigUint64Array, vectors: Vector): void;
|
|
20
|
-
search(vectors: VectorOrMatrix, k: number): CompiledSearchResult;
|
|
19
|
+
add(keys: BigUint64Array, vectors: Vector, threads: number): void;
|
|
20
|
+
search(vectors: VectorOrMatrix, k: number, threads: number): CompiledSearchResult;
|
|
21
21
|
contains(keys: BigUint64Array): boolean[];
|
|
22
22
|
count(keys: BigUint64Array): number | number[];
|
|
23
23
|
remove(keys: BigUint64Array): number[];
|
|
@@ -37,7 +37,8 @@ interface Compiled {
|
|
|
37
37
|
queries: VectorOrMatrix,
|
|
38
38
|
dimensions: number,
|
|
39
39
|
count: number,
|
|
40
|
-
metric: MetricKind
|
|
40
|
+
metric: MetricKind,
|
|
41
|
+
threads: number
|
|
41
42
|
): CompiledSearchResult;
|
|
42
43
|
}
|
|
43
44
|
|
|
@@ -326,10 +327,12 @@ export class Index {
|
|
|
326
327
|
* If a single key is provided, it is associated with all provided vectors.
|
|
327
328
|
* @param {Float32Array|Float64Array|Int8Array} vectors - Input matrix representing vectors,
|
|
328
329
|
* matrix of size n * d, where n is the number of vectors, and d is their dimensionality.
|
|
330
|
+
* @param {number} [threads=0] - Optional, default is 0. Number of threads to use for indexing.
|
|
331
|
+
* If set to 0, the number of threads is determined automatically.
|
|
329
332
|
* @throws Will throw an error if the length of keys doesn't match the number of vectors
|
|
330
333
|
* or if it's not a single key.
|
|
331
334
|
*/
|
|
332
|
-
add(keys: bigint | bigint[] | BigUint64Array, vectors: Vector) {
|
|
335
|
+
add(keys: bigint | bigint[] | BigUint64Array, vectors: Vector, threads: number = 0): void {
|
|
333
336
|
let normalizedKeys = normalizeKeys(keys);
|
|
334
337
|
let normalizedVectors = normalizeVectors(
|
|
335
338
|
vectors,
|
|
@@ -351,8 +354,14 @@ export class Index {
|
|
|
351
354
|
);
|
|
352
355
|
}
|
|
353
356
|
|
|
357
|
+
if ((!Number.isNaN(threads) && typeof threads !== "number") || threads < 0) {
|
|
358
|
+
throw new Error(
|
|
359
|
+
"`threads` must be a non-negative integer representing the number of threads to use for searching."
|
|
360
|
+
);
|
|
361
|
+
}
|
|
362
|
+
|
|
354
363
|
// Call the compiled method
|
|
355
|
-
this.#compiledIndex.add(normalizedKeys, normalizedVectors);
|
|
364
|
+
this.#compiledIndex.add(normalizedKeys, normalizedVectors, threads);
|
|
356
365
|
}
|
|
357
366
|
|
|
358
367
|
/**
|
|
@@ -372,16 +381,22 @@ export class Index {
|
|
|
372
381
|
*
|
|
373
382
|
* @param {Float32Array|Float64Array|Int8Array|Array<Array<number>>} vectors - Input matrix representing query vectors, can be a TypedArray or an array of TypedArray.
|
|
374
383
|
* @param {number} k - The number of nearest neighbors to search for each query vector.
|
|
384
|
+
* @param {number} [threads=0] - Optional, default is 0. Number of threads to use for searching. If set to 0, the number of threads is determined automatically.
|
|
375
385
|
* @return {Matches|BatchMatches} - Search results for one or more queries, containing keys, distances, and counts of the matches found.
|
|
376
386
|
* @throws Will throw an error if `k` is not a positive integer or if the size of the vectors is not a multiple of dimensions.
|
|
377
387
|
* @throws Will throw an error if `vectors` is not a valid input type (TypedArray or an array of TypedArray) or if its flattened size is not a multiple of dimensions.
|
|
378
388
|
*/
|
|
379
|
-
search(vectors: VectorOrMatrix, k: number): Matches | BatchMatches {
|
|
389
|
+
search(vectors: VectorOrMatrix, k: number, threads: number = 0): Matches | BatchMatches {
|
|
380
390
|
if ((!Number.isNaN(k) && typeof k !== "number") || k <= 0) {
|
|
381
391
|
throw new Error(
|
|
382
392
|
"`k` must be a positive integer representing the number of nearest neighbors to search for."
|
|
383
393
|
);
|
|
384
394
|
}
|
|
395
|
+
if ((!Number.isNaN(threads) && typeof threads !== "number") || threads < 0) {
|
|
396
|
+
throw new Error(
|
|
397
|
+
"`threads` must be a non-negative integer representing the number of threads to use for searching."
|
|
398
|
+
);
|
|
399
|
+
}
|
|
385
400
|
|
|
386
401
|
const normalizedVectors = normalizeVectors(
|
|
387
402
|
vectors,
|
|
@@ -389,7 +404,7 @@ export class Index {
|
|
|
389
404
|
);
|
|
390
405
|
|
|
391
406
|
// Call the compiled method and create Matches or BatchMatches object with the result
|
|
392
|
-
const result = this.#compiledIndex.search(normalizedVectors, k);
|
|
407
|
+
const result = this.#compiledIndex.search(normalizedVectors, k, threads);
|
|
393
408
|
const countInQueries =
|
|
394
409
|
normalizedVectors.length / Number(this.#compiledIndex.dimensions());
|
|
395
410
|
const batchMatches = new BatchMatches(...result, k);
|
|
@@ -533,6 +548,7 @@ type NumberArrayConstructor =
|
|
|
533
548
|
* @param {number} dimensions - The dimensionality of the vectors in both the dataset and the queries. It defines the number of elements in each vector.
|
|
534
549
|
* @param {number} count - The number of nearest neighbors to return for each query. If the dataset contains fewer vectors than the specified count, the result will contain only the available vectors.
|
|
535
550
|
* @param {MetricKind} metric - The distance metric to be used for the search.
|
|
551
|
+
* @param {number} [threads=0] - Optional, default is 0. The number of threads to use for the search. If set to 0, the number of threads is determined automatically.
|
|
536
552
|
* @return {Matches|BatchMatches} - Returns a `Matches` or `BatchMatches` object containing the results of the search.
|
|
537
553
|
* @throws Will throw an error if `dimensions` and `count` are not positive integers.
|
|
538
554
|
* @throws Will throw an error if `metric` is not a valid MetricKind.
|
|
@@ -546,20 +562,22 @@ type NumberArrayConstructor =
|
|
|
546
562
|
* const dimensions = 2; // The number of elements in each vector.
|
|
547
563
|
* const count = 1; // The number of nearest neighbors to return for each query.
|
|
548
564
|
* const metric = MetricKind.IP; // Using the Inner Product distance metric.
|
|
565
|
+
* const threads = 0; // How many threads to use to perform the search.
|
|
549
566
|
*
|
|
550
|
-
* const result = exactSearch(dataset, queries, dimensions, count, metric);
|
|
567
|
+
* const result = exactSearch(dataset, queries, dimensions, count, metric, threads);
|
|
551
568
|
* // result might be:
|
|
552
569
|
* // {
|
|
553
570
|
* // keys: BigUint64Array [ 1n ],
|
|
554
571
|
* // distances: Float32Array [ some_value ],
|
|
555
572
|
* // }
|
|
556
573
|
*/
|
|
557
|
-
function exactSearch(
|
|
574
|
+
export function exactSearch(
|
|
558
575
|
dataset: VectorOrMatrix,
|
|
559
576
|
queries: VectorOrMatrix,
|
|
560
577
|
dimensions: number,
|
|
561
578
|
count: number,
|
|
562
|
-
metric: MetricKind
|
|
579
|
+
metric: MetricKind,
|
|
580
|
+
threads: number = 0
|
|
563
581
|
): Matches | BatchMatches {
|
|
564
582
|
// Validate and normalize the dimensions and count
|
|
565
583
|
dimensions = Number(dimensions);
|
|
@@ -567,6 +585,11 @@ function exactSearch(
|
|
|
567
585
|
if (count <= 0 || dimensions <= 0) {
|
|
568
586
|
throw new Error("Dimensions and count must be positive integers.");
|
|
569
587
|
}
|
|
588
|
+
if ((!Number.isNaN(threads) && typeof threads !== "number") || threads < 0) {
|
|
589
|
+
throw new Error(
|
|
590
|
+
"`threads` must be a non-negative integer representing the number of threads to use for searching."
|
|
591
|
+
);
|
|
592
|
+
}
|
|
570
593
|
|
|
571
594
|
// Validate metric
|
|
572
595
|
if (!Object.values(MetricKind).includes(metric)) {
|
|
@@ -599,7 +622,8 @@ function exactSearch(
|
|
|
599
622
|
queries,
|
|
600
623
|
dimensions,
|
|
601
624
|
count,
|
|
602
|
-
metric
|
|
625
|
+
metric,
|
|
626
|
+
threads
|
|
603
627
|
);
|
|
604
628
|
|
|
605
629
|
// Create and return a Matches or BatchMatches object with the result
|
package/package.json
CHANGED
|
Binary file
|
|
Binary file
|
|
Binary file
|
package/simsimd/CONTRIBUTING.md
CHANGED
|
@@ -68,6 +68,7 @@ Replacing the default compiler across the entire system is not recommended on Ma
|
|
|
68
68
|
|
|
69
69
|
```sh
|
|
70
70
|
brew install llvm openblas
|
|
71
|
+
unset DEVELOPER_DIR
|
|
71
72
|
cmake -D CMAKE_BUILD_TYPE=Release \
|
|
72
73
|
-D SIMSIMD_BUILD_TESTS=1 \
|
|
73
74
|
-D SIMSIMD_BUILD_BENCHMARKS=1 \
|
|
@@ -86,23 +87,37 @@ cmake --build build_release --config Release
|
|
|
86
87
|
When benchmarking, make sure to disable multi-threading in the BLAS library, as it may interfere with the results:
|
|
87
88
|
|
|
88
89
|
```sh
|
|
89
|
-
export OPENBLAS_NUM_THREADS=1
|
|
90
|
-
export MKL_NUM_THREADS=1
|
|
91
|
-
export VECLIB_MAXIMUM_THREADS=1
|
|
92
|
-
export BLIS_NUM_THREADS=1
|
|
90
|
+
export OPENBLAS_NUM_THREADS=1 # for OpenBLAS
|
|
91
|
+
export MKL_NUM_THREADS=1 # for Intel MKL
|
|
92
|
+
export VECLIB_MAXIMUM_THREADS=1 # for Apple Accelerate
|
|
93
|
+
export BLIS_NUM_THREADS=1 # for BLIS
|
|
93
94
|
```
|
|
94
95
|
|
|
95
96
|
## Python
|
|
96
97
|
|
|
97
|
-
|
|
98
|
+
Python bindings are implemented using pure CPython, so you wouldn't need to install SWIG, PyBind11, or any other third-party library.
|
|
99
|
+
Still, you need a virtual environment.
|
|
100
|
+
If you already have one:
|
|
98
101
|
|
|
99
102
|
```sh
|
|
100
|
-
pip install -e . #
|
|
103
|
+
pip install -e . # build locally from source
|
|
101
104
|
pip install pytest pytest-repeat tabulate # testing dependencies
|
|
102
105
|
pytest scripts/test.py -s -x -Wd # to run tests
|
|
103
106
|
|
|
104
107
|
# to check supported SIMD instructions:
|
|
105
|
-
python -c "import simsimd; print(simsimd.get_capabilities())"
|
|
108
|
+
python -c "import simsimd; print(simsimd.get_capabilities())"
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
Alternatively, use `uv` to create the virtual environment.
|
|
112
|
+
|
|
113
|
+
```sh
|
|
114
|
+
uv venv --python 3.13t # or your preferred version
|
|
115
|
+
source .venv/bin/activate # activate the environment
|
|
116
|
+
uv pip install -e . # build locally from source
|
|
117
|
+
|
|
118
|
+
# to run GIL-related tests in a free-threaded environment:
|
|
119
|
+
uv pip install pytest pytest-repeat tabulate numpy scipy
|
|
120
|
+
PYTHON_GIL=0 python -m pytest scripts/test.py -s -x -Wd -k gil
|
|
106
121
|
```
|
|
107
122
|
|
|
108
123
|
Here, `-s` will output the logs.
|
|
@@ -234,7 +249,7 @@ irm https://deno.land/install.ps1 | iex # Windows
|
|
|
234
249
|
Testing:
|
|
235
250
|
|
|
236
251
|
```sh
|
|
237
|
-
deno test
|
|
252
|
+
deno test -A
|
|
238
253
|
```
|
|
239
254
|
|
|
240
255
|
### Bun
|
package/simsimd/Cargo.lock
CHANGED
package/simsimd/Cargo.toml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
[package]
|
|
2
2
|
name = "simsimd"
|
|
3
3
|
description = "Portable mixed-precision BLAS-like vector math library for x86 and ARM"
|
|
4
|
-
version = "6.
|
|
4
|
+
version = "6.4.10"
|
|
5
5
|
edition = "2021"
|
|
6
6
|
license = "Apache-2.0"
|
|
7
7
|
authors = ["Ash Vardanian <1983160+ashvardanian@users.noreply.github.com>"]
|
package/simsimd/README.md
CHANGED
|
@@ -190,7 +190,7 @@ You can learn more about the technical implementation details in the following b
|
|
|
190
190
|
|
|
191
191
|
<!-- Euclidean distance with different precision level -->
|
|
192
192
|
<tr>
|
|
193
|
-
<td colspan="4" align="center">
|
|
193
|
+
<td colspan="4" align="center">euclidean distance between 1536d vectors in <code>int8</code></td>
|
|
194
194
|
</tr>
|
|
195
195
|
<tr>
|
|
196
196
|
<td align="center"> <!-- scipy.spatial.distance.sqeuclidean -->
|
|
@@ -207,7 +207,7 @@ You can learn more about the technical implementation details in the following b
|
|
|
207
207
|
</td>
|
|
208
208
|
</tr>
|
|
209
209
|
<tr>
|
|
210
|
-
<td colspan="4" align="center">
|
|
210
|
+
<td colspan="4" align="center">euclidean distance between 1536d vectors in <code>bfloat16</code></td>
|
|
211
211
|
</tr>
|
|
212
212
|
<tr>
|
|
213
213
|
<td align="center"> <!-- scipy.spatial.distance.sqeuclidean -->
|
|
@@ -223,7 +223,7 @@ You can learn more about the technical implementation details in the following b
|
|
|
223
223
|
</td>
|
|
224
224
|
</tr>
|
|
225
225
|
<tr>
|
|
226
|
-
<td colspan="4" align="center">
|
|
226
|
+
<td colspan="4" align="center">euclidean distance between 1536d vectors in <code>float16</code></td>
|
|
227
227
|
</tr>
|
|
228
228
|
<tr>
|
|
229
229
|
<td align="center"> <!-- scipy.spatial.distance.sqeuclidean -->
|
|
@@ -240,7 +240,7 @@ You can learn more about the technical implementation details in the following b
|
|
|
240
240
|
</td>
|
|
241
241
|
</tr>
|
|
242
242
|
<tr>
|
|
243
|
-
<td colspan="4" align="center">
|
|
243
|
+
<td colspan="4" align="center">euclidean distance between 1536d vectors in <code>float32</code></td>
|
|
244
244
|
</tr>
|
|
245
245
|
<tr>
|
|
246
246
|
<td align="center"> <!-- scipy.spatial.distance.sqeuclidean -->
|
|
@@ -257,7 +257,7 @@ You can learn more about the technical implementation details in the following b
|
|
|
257
257
|
</td>
|
|
258
258
|
</tr>
|
|
259
259
|
<tr>
|
|
260
|
-
<td colspan="4" align="center">
|
|
260
|
+
<td colspan="4" align="center">euclidean distance between 1536d vectors in <code>float64</code></td>
|
|
261
261
|
</tr>
|
|
262
262
|
<tr>
|
|
263
263
|
<td align="center"> <!-- scipy.spatial.distance.sqeuclidean -->
|
|
@@ -491,7 +491,7 @@ slow_output_color = (alpha * light_intensity * diffuse_component + beta * specul
|
|
|
491
491
|
|
|
492
492
|
By default, computations use a single CPU core.
|
|
493
493
|
To override this behavior, use the `threads` argument.
|
|
494
|
-
Set it to `0` to use all available CPU cores.
|
|
494
|
+
Set it to `0` to use all available CPU cores and let the underlying C library manage the thread pool.
|
|
495
495
|
Here is an example of dealing with large sets of binary vectors:
|
|
496
496
|
|
|
497
497
|
```py
|
|
@@ -507,9 +507,42 @@ distances = simsimd.cdist(matrix1, matrix2,
|
|
|
507
507
|
)
|
|
508
508
|
```
|
|
509
509
|
|
|
510
|
+
Alternatively, when using free-threading Python 3.13t builds, one can combine single-threaded SimSIMD operations with Python's `concurrent.futures.ThreadPoolExecutor` to parallelize the computations.
|
|
510
511
|
By default, the output distances will be stored in double-precision `float64` floating-point numbers.
|
|
511
512
|
That behavior may not be space-efficient, especially if you are computing the hamming distance between short binary vectors, that will generally fit into 8x smaller `uint8` or `uint16` types.
|
|
512
|
-
To override this behavior, use the `
|
|
513
|
+
To override this behavior, use the `out_dtype` argument, or consider pre-allocating the output array and passing it to the `out` argument.
|
|
514
|
+
A more complete example may look like this:
|
|
515
|
+
|
|
516
|
+
```py
|
|
517
|
+
from multiprocessing import cpu_count
|
|
518
|
+
from concurrent.futures import ThreadPoolExecutor
|
|
519
|
+
from simsimd import cosine
|
|
520
|
+
import numpy as np
|
|
521
|
+
|
|
522
|
+
# Generate large dataset
|
|
523
|
+
vectors_a = np.random.rand(100_000, 1536).astype(np.float32)
|
|
524
|
+
vectors_b = np.random.rand(100_000, 1536).astype(np.float32)
|
|
525
|
+
distances = np.zeros((100_000,), dtype=np.float32)
|
|
526
|
+
|
|
527
|
+
def compute_batch(start_idx, end_idx):
|
|
528
|
+
batch_a = vectors_a[start_idx:end_idx]
|
|
529
|
+
batch_b = vectors_b[start_idx:end_idx]
|
|
530
|
+
cosine(batch_a, batch_b, out=distances[start_idx:end_idx])
|
|
531
|
+
|
|
532
|
+
# Use all CPU cores with true parallelism (no GIL!)
|
|
533
|
+
num_threads = cpu_count()
|
|
534
|
+
chunk_size = len(vectors_a) // num_threads
|
|
535
|
+
|
|
536
|
+
with ThreadPoolExecutor(max_workers=num_threads) as executor:
|
|
537
|
+
futures = []
|
|
538
|
+
for i in range(num_threads):
|
|
539
|
+
start_idx = i * chunk_size
|
|
540
|
+
end_idx = (i + 1) * chunk_size if i < num_threads - 1 else len(vectors_a)
|
|
541
|
+
futures.append(executor.submit(compute_batch, start_idx, end_idx))
|
|
542
|
+
|
|
543
|
+
# Collect results from all threads
|
|
544
|
+
results = [future.result() for future in futures]
|
|
545
|
+
```
|
|
513
546
|
|
|
514
547
|
### Helper Functions
|
|
515
548
|
|
|
@@ -825,14 +858,17 @@ Simplest of all, you can include the headers, and the compiler will automaticall
|
|
|
825
858
|
#include <simsimd/simsimd.h>
|
|
826
859
|
|
|
827
860
|
int main() {
|
|
861
|
+
simsimd_flush_denormals(); // Optional, to avoid performance penalties on denormal numbers
|
|
862
|
+
|
|
828
863
|
simsimd_f32_t vector_a[1536];
|
|
829
864
|
simsimd_f32_t vector_b[1536];
|
|
830
|
-
simsimd_kernel_punned_t
|
|
865
|
+
simsimd_kernel_punned_t metric_punned = simsimd_metric_punned(
|
|
831
866
|
simsimd_metric_cos_k, // Metric kind, like the angular cosine distance
|
|
832
867
|
simsimd_datatype_f32_k, // Data type, like: f16, f32, f64, i8, b8, and complex variants
|
|
833
868
|
simsimd_cap_any_k); // Which CPU capabilities are we allowed to use
|
|
834
869
|
simsimd_distance_t distance;
|
|
835
|
-
|
|
870
|
+
simsimd_metric_dense_punned_t metric = (simsimd_metric_dense_punned_t)metric_punned;
|
|
871
|
+
metric(vector_a, vector_b, 1536, &distance);
|
|
836
872
|
return 0;
|
|
837
873
|
}
|
|
838
874
|
```
|
|
@@ -1384,3 +1420,7 @@ $ grep -oP 'SIMSIMD_PUBLIC void simsimd_\w+_\w+_\w+\(' include/simsimd/*.h | sor
|
|
|
1384
1420
|
> include/simsimd/binary.h:SIMSIMD_PUBLIC void simsimd_jaccard_b8_serial(
|
|
1385
1421
|
> include/simsimd/binary.h:SIMSIMD_PUBLIC void simsimd_jaccard_b8_sve(
|
|
1386
1422
|
```
|
|
1423
|
+
|
|
1424
|
+
## License
|
|
1425
|
+
|
|
1426
|
+
Feel free to use the project under Apache 2.0 or the Three-clause BSD license at your preference.
|
package/simsimd/build.rs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
fn main() {
|
|
1
|
+
fn main() -> Result<(), cc::Error> {
|
|
2
2
|
let mut build = cc::Build::new();
|
|
3
3
|
|
|
4
4
|
build
|
|
@@ -12,7 +12,7 @@ fn main() {
|
|
|
12
12
|
.flag("-pedantic") // Ensure strict compliance with the C standard
|
|
13
13
|
.warnings(false);
|
|
14
14
|
|
|
15
|
-
if build.try_compile("simsimd")
|
|
15
|
+
if let Err(e) = build.try_compile("simsimd") {
|
|
16
16
|
print!("cargo:warning=Failed to compile with all SIMD backends...");
|
|
17
17
|
|
|
18
18
|
let target_arch = std::env::var("CARGO_CFG_TARGET_ARCH").unwrap_or_default();
|
|
@@ -39,9 +39,11 @@ fn main() {
|
|
|
39
39
|
],
|
|
40
40
|
};
|
|
41
41
|
|
|
42
|
+
let mut result = Err(e);
|
|
42
43
|
for flag in flags_to_try.iter() {
|
|
43
44
|
build.define(flag, "0");
|
|
44
|
-
|
|
45
|
+
result = build.try_compile("simsimd");
|
|
46
|
+
if result.is_ok() {
|
|
45
47
|
break;
|
|
46
48
|
}
|
|
47
49
|
|
|
@@ -51,6 +53,7 @@ fn main() {
|
|
|
51
53
|
flag
|
|
52
54
|
);
|
|
53
55
|
}
|
|
56
|
+
result?;
|
|
54
57
|
}
|
|
55
58
|
|
|
56
59
|
println!("cargo:rerun-if-changed=c/lib.c");
|
|
@@ -62,4 +65,5 @@ fn main() {
|
|
|
62
65
|
println!("cargo:rerun-if-changed=include/simsimd/probability.h");
|
|
63
66
|
println!("cargo:rerun-if-changed=include/simsimd/binary.h");
|
|
64
67
|
println!("cargo:rerun-if-changed=include/simsimd/types.h");
|
|
68
|
+
Ok(())
|
|
65
69
|
}
|
package/simsimd/c/lib.c
CHANGED
|
@@ -241,6 +241,7 @@ SIMSIMD_DYNAMIC int simsimd_uses_sapphire(void) { return (simsimd_capabilities()
|
|
|
241
241
|
SIMSIMD_DYNAMIC int simsimd_uses_turin(void) { return (simsimd_capabilities() & simsimd_cap_turin_k) != 0; }
|
|
242
242
|
SIMSIMD_DYNAMIC int simsimd_uses_sierra(void) { return (simsimd_capabilities() & simsimd_cap_sierra_k) != 0; }
|
|
243
243
|
SIMSIMD_DYNAMIC int simsimd_uses_dynamic_dispatch(void) { return 1; }
|
|
244
|
+
SIMSIMD_DYNAMIC int simsimd_flush_denormals(void) { return _simsimd_flush_denormals(); }
|
|
244
245
|
|
|
245
246
|
SIMSIMD_DYNAMIC simsimd_capability_t simsimd_capabilities(void) {
|
|
246
247
|
//! The latency of the CPUID instruction can be over 100 cycles, so we cache the result.
|
|
@@ -254,7 +255,11 @@ SIMSIMD_DYNAMIC simsimd_capability_t simsimd_capabilities(void) {
|
|
|
254
255
|
// with dummy inputs:
|
|
255
256
|
simsimd_distance_t dummy_results_buffer[2];
|
|
256
257
|
simsimd_distance_t *dummy_results = &dummy_results_buffer[0];
|
|
257
|
-
|
|
258
|
+
|
|
259
|
+
// Passing `NULL` as `x` will trigger all kinds of `nonull` warnings on GCC.
|
|
260
|
+
typedef double largest_scalar_t;
|
|
261
|
+
largest_scalar_t dummy_input[1];
|
|
262
|
+
void *x = &dummy_input[0];
|
|
258
263
|
|
|
259
264
|
// Dense:
|
|
260
265
|
simsimd_dot_i8((simsimd_i8_t *)x, (simsimd_i8_t *)x, 0, dummy_results);
|
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
* - x86: Haswell, Ice Lake
|
|
14
14
|
*
|
|
15
15
|
* The hardest part of optimizing binary similarity measures is the population count operation.
|
|
16
|
-
* It's natively supported by almost every
|
|
16
|
+
* It's natively supported by almost every instruction set, but the throughput and latency can
|
|
17
17
|
* be suboptimal. There are several ways to optimize this operation:
|
|
18
18
|
*
|
|
19
19
|
* - Lookup tables, mostly using nibbles (4-bit lookups)
|
|
@@ -107,8 +107,8 @@ SIMSIMD_PUBLIC void simsimd_jaccard_b8_serial(simsimd_b8_t const *a, simsimd_b8_
|
|
|
107
107
|
#if _SIMSIMD_TARGET_ARM
|
|
108
108
|
#if SIMSIMD_TARGET_NEON
|
|
109
109
|
#pragma GCC push_options
|
|
110
|
-
#pragma GCC target("arch=armv8
|
|
111
|
-
#pragma clang attribute push(__attribute__((target("arch=armv8
|
|
110
|
+
#pragma GCC target("arch=armv8-a+simd")
|
|
111
|
+
#pragma clang attribute push(__attribute__((target("arch=armv8-a+simd"))), apply_to = function)
|
|
112
112
|
|
|
113
113
|
SIMSIMD_INTERNAL simsimd_u32_t _simsimd_reduce_u8x16_neon(uint8x16_t vec) {
|
|
114
114
|
// Split the vector into two halves and widen to `uint16x8_t`
|
|
@@ -244,8 +244,8 @@ SIMSIMD_MAKE_COMPLEX_VDOT(accurate, bf16c, f64, SIMSIMD_BF16_TO_F32) // simsimd_
|
|
|
244
244
|
#if _SIMSIMD_TARGET_ARM
|
|
245
245
|
#if SIMSIMD_TARGET_NEON
|
|
246
246
|
#pragma GCC push_options
|
|
247
|
-
#pragma GCC target("arch=armv8
|
|
248
|
-
#pragma clang attribute push(__attribute__((target("arch=armv8
|
|
247
|
+
#pragma GCC target("arch=armv8-a+simd")
|
|
248
|
+
#pragma clang attribute push(__attribute__((target("arch=armv8-a+simd"))), apply_to = function)
|
|
249
249
|
|
|
250
250
|
SIMSIMD_INTERNAL float32x4_t _simsimd_partial_load_f32x4_neon(simsimd_f32_t const *x, simsimd_size_t n) {
|
|
251
251
|
union {
|
|
@@ -1469,10 +1469,19 @@ simsimd_fma_bf16_skylake_cycle:
|
|
|
1469
1469
|
|
|
1470
1470
|
#if SIMSIMD_TARGET_SAPPHIRE
|
|
1471
1471
|
#pragma GCC push_options
|
|
1472
|
-
#pragma GCC target("avx2", "avx512f", "avx512vl", "bmi2", "avx512bw", "avx512fp16")
|
|
1473
|
-
#pragma clang attribute push(__attribute__((target("avx2,avx512f,avx512vl,bmi2,avx512bw,avx512fp16"))), \
|
|
1472
|
+
#pragma GCC target("avx2", "avx512f", "avx512vl", "bmi2", "avx512bw", "avx512fp16", "f16c")
|
|
1473
|
+
#pragma clang attribute push(__attribute__((target("avx2,avx512f,avx512vl,bmi2,avx512bw,avx512fp16,f16c"))), \
|
|
1474
1474
|
apply_to = function)
|
|
1475
1475
|
|
|
1476
|
+
/**
|
|
1477
|
+
* Using `_mm512_set1_ph((_Float16)1.f)` results in compilation warnings if we are pedantic.
|
|
1478
|
+
* https://www.intel.com/content/www/us/en/docs/cpp-compiler/developer-guide-reference/2021-8/details-about-intrinsics-for-half-floats.html
|
|
1479
|
+
*/
|
|
1480
|
+
SIMSIMD_INTERNAL __m512h _mm512_set1_ph_from_ps(float a) {
|
|
1481
|
+
unsigned short h = _cvtss_sh(a, _MM_FROUND_TO_NEAREST_INT | _MM_FROUND_NO_EXC);
|
|
1482
|
+
return (__m512h)_mm512_set1_epi16(h);
|
|
1483
|
+
}
|
|
1484
|
+
|
|
1476
1485
|
SIMSIMD_PUBLIC void simsimd_sum_f16_sapphire(simsimd_f16_t const *a, simsimd_f16_t const *b, simsimd_size_t n,
|
|
1477
1486
|
simsimd_f16_t *result) {
|
|
1478
1487
|
__mmask32 mask = 0xFFFFFFFF;
|
|
@@ -1500,7 +1509,7 @@ SIMSIMD_PUBLIC void simsimd_scale_f16_sapphire(simsimd_f16_t const *a, simsimd_s
|
|
|
1500
1509
|
simsimd_f16_t *result) {
|
|
1501
1510
|
|
|
1502
1511
|
__mmask32 mask = 0xFFFFFFFF;
|
|
1503
|
-
__m512h alpha_vec =
|
|
1512
|
+
__m512h alpha_vec = _mm512_set1_ph_from_ps(alpha);
|
|
1504
1513
|
__m512h a_f16_vec, b_f16_vec;
|
|
1505
1514
|
__m512h sum_f16_vec;
|
|
1506
1515
|
simsimd_scale_f16_sapphire_cycle:
|
|
@@ -1540,8 +1549,8 @@ SIMSIMD_PUBLIC void simsimd_wsum_f16_sapphire( //
|
|
|
1540
1549
|
|
|
1541
1550
|
// The general case.
|
|
1542
1551
|
__mmask32 mask = 0xFFFFFFFF;
|
|
1543
|
-
__m512h alpha_vec =
|
|
1544
|
-
__m512h beta_vec =
|
|
1552
|
+
__m512h alpha_vec = _mm512_set1_ph_from_ps(alpha);
|
|
1553
|
+
__m512h beta_vec = _mm512_set1_ph_from_ps(beta);
|
|
1545
1554
|
__m512h a_f16_vec, b_f16_vec;
|
|
1546
1555
|
__m512h a_scaled_f16_vec, sum_f16_vec;
|
|
1547
1556
|
simsimd_wsum_f16_sapphire_cycle:
|
|
@@ -1568,8 +1577,8 @@ SIMSIMD_PUBLIC void simsimd_fma_f16_sapphire(
|
|
|
1568
1577
|
simsimd_distance_t alpha, simsimd_distance_t beta, simsimd_f16_t *result) {
|
|
1569
1578
|
|
|
1570
1579
|
__mmask32 mask = 0xFFFFFFFF;
|
|
1571
|
-
__m512h alpha_vec =
|
|
1572
|
-
__m512h beta_vec =
|
|
1580
|
+
__m512h alpha_vec = _mm512_set1_ph_from_ps(alpha);
|
|
1581
|
+
__m512h beta_vec = _mm512_set1_ph_from_ps(beta);
|
|
1573
1582
|
__m512h a_f16_vec, b_f16_vec, c_f16_vec;
|
|
1574
1583
|
__m512h ab_f16_vec, ab_scaled_f16_vec, sum_f16_vec;
|
|
1575
1584
|
simsimd_fma_f16_sapphire_cycle:
|
|
@@ -1619,7 +1628,7 @@ simsimd_sum_u8_sapphire_cycle:
|
|
|
1619
1628
|
SIMSIMD_PUBLIC void simsimd_scale_u8_sapphire(simsimd_u8_t const *a, simsimd_size_t n, simsimd_distance_t alpha,
|
|
1620
1629
|
simsimd_u8_t *result) {
|
|
1621
1630
|
__mmask64 mask = 0xFFFFFFFFFFFFFFFFull;
|
|
1622
|
-
__m512h alpha_vec =
|
|
1631
|
+
__m512h alpha_vec = _mm512_set1_ph_from_ps(alpha);
|
|
1623
1632
|
__m512i a_u8_vec, b_u8_vec, sum_u8_vec;
|
|
1624
1633
|
__m512h a_f16_low_vec, a_f16_high_vec;
|
|
1625
1634
|
__m512h a_scaled_f16_low_vec, a_scaled_f16_high_vec, sum_f16_low_vec, sum_f16_high_vec;
|
|
@@ -1670,8 +1679,8 @@ SIMSIMD_PUBLIC void simsimd_wsum_u8_sapphire( //
|
|
|
1670
1679
|
|
|
1671
1680
|
// The general case.
|
|
1672
1681
|
__mmask64 mask = 0xFFFFFFFFFFFFFFFFull;
|
|
1673
|
-
__m512h alpha_vec =
|
|
1674
|
-
__m512h beta_vec =
|
|
1682
|
+
__m512h alpha_vec = _mm512_set1_ph_from_ps(alpha);
|
|
1683
|
+
__m512h beta_vec = _mm512_set1_ph_from_ps(beta);
|
|
1675
1684
|
__m512i a_u8_vec, b_u8_vec, sum_u8_vec;
|
|
1676
1685
|
__m512h a_f16_low_vec, a_f16_high_vec, b_f16_low_vec, b_f16_high_vec;
|
|
1677
1686
|
__m512h a_scaled_f16_low_vec, a_scaled_f16_high_vec, sum_f16_low_vec, sum_f16_high_vec;
|
|
@@ -1739,7 +1748,7 @@ SIMSIMD_PUBLIC void simsimd_scale_i8_sapphire(simsimd_i8_t const *a, simsimd_siz
|
|
|
1739
1748
|
simsimd_i8_t *result) {
|
|
1740
1749
|
|
|
1741
1750
|
__mmask64 mask = 0xFFFFFFFFFFFFFFFFull;
|
|
1742
|
-
__m512h alpha_vec =
|
|
1751
|
+
__m512h alpha_vec = _mm512_set1_ph_from_ps(alpha);
|
|
1743
1752
|
__m512i a_i8_vec, sum_i8_vec;
|
|
1744
1753
|
__m512h a_f16_low_vec, a_f16_high_vec;
|
|
1745
1754
|
__m512h sum_f16_low_vec, sum_f16_high_vec;
|
|
@@ -1791,8 +1800,8 @@ SIMSIMD_PUBLIC void simsimd_wsum_i8_sapphire( //
|
|
|
1791
1800
|
|
|
1792
1801
|
// The general case.
|
|
1793
1802
|
__mmask64 mask = 0xFFFFFFFFFFFFFFFFull;
|
|
1794
|
-
__m512h alpha_vec =
|
|
1795
|
-
__m512h beta_vec =
|
|
1803
|
+
__m512h alpha_vec = _mm512_set1_ph_from_ps(alpha);
|
|
1804
|
+
__m512h beta_vec = _mm512_set1_ph_from_ps(beta);
|
|
1796
1805
|
__m512i a_i8_vec, b_i8_vec, sum_i8_vec;
|
|
1797
1806
|
__m512h a_f16_low_vec, a_f16_high_vec, b_f16_low_vec, b_f16_high_vec;
|
|
1798
1807
|
__m512h a_scaled_f16_low_vec, a_scaled_f16_high_vec, sum_f16_low_vec, sum_f16_high_vec;
|
|
@@ -1836,8 +1845,8 @@ SIMSIMD_PUBLIC void simsimd_fma_i8_sapphire(
|
|
|
1836
1845
|
simsimd_distance_t alpha, simsimd_distance_t beta, simsimd_i8_t *result) {
|
|
1837
1846
|
|
|
1838
1847
|
__mmask64 mask = 0xFFFFFFFFFFFFFFFF;
|
|
1839
|
-
__m512h alpha_vec =
|
|
1840
|
-
__m512h beta_vec =
|
|
1848
|
+
__m512h alpha_vec = _mm512_set1_ph_from_ps(alpha);
|
|
1849
|
+
__m512h beta_vec = _mm512_set1_ph_from_ps(beta);
|
|
1841
1850
|
__m512i a_i8_vec, b_i8_vec, c_i8_vec, sum_i8_vec;
|
|
1842
1851
|
__m512h a_f16_low_vec, a_f16_high_vec, b_f16_low_vec, b_f16_high_vec;
|
|
1843
1852
|
__m512h c_f16_low_vec, c_f16_high_vec, ab_f16_low_vec, ab_f16_high_vec;
|
|
@@ -1889,8 +1898,8 @@ SIMSIMD_PUBLIC void simsimd_fma_u8_sapphire(
|
|
|
1889
1898
|
simsimd_distance_t alpha, simsimd_distance_t beta, simsimd_u8_t *result) {
|
|
1890
1899
|
|
|
1891
1900
|
__mmask64 mask = 0xFFFFFFFFFFFFFFFF;
|
|
1892
|
-
__m512h alpha_vec =
|
|
1893
|
-
__m512h beta_vec =
|
|
1901
|
+
__m512h alpha_vec = _mm512_set1_ph_from_ps(alpha);
|
|
1902
|
+
__m512h beta_vec = _mm512_set1_ph_from_ps(beta);
|
|
1894
1903
|
__m512i a_u8_vec, b_u8_vec, c_u8_vec, sum_u8_vec;
|
|
1895
1904
|
__m512h a_f16_low_vec, a_f16_high_vec, b_f16_low_vec, b_f16_high_vec;
|
|
1896
1905
|
__m512h c_f16_low_vec, c_f16_high_vec, ab_f16_low_vec, ab_f16_high_vec;
|