usearch 2.7.3 → 2.7.7
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/.github/workflows/Dockerfile.libs +4 -6
- package/.github/workflows/prerelease.yml +4 -1
- package/.github/workflows/release.yml +8 -28
- package/.github/workflows/update_version.sh +1 -0
- package/CITATION.cff +2 -2
- package/CMakeLists.txt +1 -1
- package/Cargo.toml +1 -1
- package/README.md +4 -4
- package/binding.gyp +1 -1
- package/build.gradle +20 -5
- package/conanfile.py +1 -1
- package/cpp/bench.cpp +1 -1
- package/csharp/nuget/nuget-package.props +1 -1
- package/docs/compilation.md +0 -2
- package/golang/README.md +0 -1
- package/include/usearch/index.hpp +1 -1
- package/include/usearch/index_plugins.hpp +128 -247
- package/package.json +1 -1
- package/python/lib.cpp +3 -4
- package/simsimd/.github/workflows/prerelease.yml +33 -58
- package/simsimd/.github/workflows/release.yml +22 -9
- package/simsimd/.github/workflows/update_version.sh +3 -1
- package/simsimd/.releaserc +9 -1
- package/simsimd/.vscode/launch.json +3 -0
- package/simsimd/.vscode/settings.json +15 -2
- package/simsimd/.vscode/tasks.json +2 -2
- package/simsimd/CMakeLists.txt +36 -55
- package/simsimd/README.md +163 -109
- package/simsimd/binding.gyp +18 -0
- package/simsimd/cpp/bench.cxx +187 -0
- package/simsimd/go.mod +3 -0
- package/simsimd/golang/bench_test.go +40 -0
- package/simsimd/golang/simsimd.go +47 -0
- package/simsimd/golang/unit_test.go +40 -0
- package/simsimd/include/simsimd/binary.h +186 -0
- package/simsimd/include/simsimd/probability.h +645 -0
- package/simsimd/include/simsimd/simsimd.h +335 -575
- package/simsimd/include/simsimd/spatial.h +924 -0
- package/simsimd/include/simsimd/types.h +196 -0
- package/simsimd/javascript/bench.js +117 -0
- package/simsimd/javascript/lib.c +102 -0
- package/simsimd/javascript/simsimd.js +45 -0
- package/simsimd/javascript/test.js +29 -0
- package/simsimd/package.json +31 -2
- package/simsimd/pyproject.toml +5 -6
- package/simsimd/python/bench.ipynb +187 -0
- package/simsimd/python/bench.py +268 -0
- package/simsimd/python/lib.c +453 -72
- package/simsimd/python/test.py +167 -35
- package/simsimd/setup.py +51 -8
- package/wasmer.toml +1 -1
- package/simsimd/bench.cxx +0 -137
- package/simsimd/include/simsimd/simsimd_bio.h +0 -54
- package/simsimd/include/simsimd/simsimd_chem.h +0 -167
- package/simsimd/include/simsimd/simsimd_text.h +0 -92
- package/simsimd/python/run.sh +0 -1
- package/simsimd/python/test.c +0 -109
package/simsimd/.releaserc
CHANGED
|
@@ -11,6 +11,10 @@
|
|
|
11
11
|
{
|
|
12
12
|
"preset": "eslint",
|
|
13
13
|
"releaseRules": [
|
|
14
|
+
{
|
|
15
|
+
"tag": "Break",
|
|
16
|
+
"release": "major"
|
|
17
|
+
},
|
|
14
18
|
{
|
|
15
19
|
"tag": "Add",
|
|
16
20
|
"release": "minor"
|
|
@@ -35,6 +39,10 @@
|
|
|
35
39
|
{
|
|
36
40
|
"preset": "eslint",
|
|
37
41
|
"releaseRules": [
|
|
42
|
+
{
|
|
43
|
+
"tag": "Break",
|
|
44
|
+
"release": "major"
|
|
45
|
+
},
|
|
38
46
|
{
|
|
39
47
|
"tag": "Add",
|
|
40
48
|
"release": "minor"
|
|
@@ -68,7 +76,7 @@
|
|
|
68
76
|
"VERSION",
|
|
69
77
|
"conanfile.py",
|
|
70
78
|
"package.json",
|
|
71
|
-
"Cargo.toml"
|
|
79
|
+
"Cargo.toml"
|
|
72
80
|
],
|
|
73
81
|
"message": "Build: Released ${nextRelease.version} [skip ci]\n\n${nextRelease.notes}"
|
|
74
82
|
}
|
|
@@ -86,16 +86,29 @@
|
|
|
86
86
|
"__nullptr": "cpp",
|
|
87
87
|
"unordered_set": "cpp",
|
|
88
88
|
"span": "cpp",
|
|
89
|
-
"arm_neon.h": "c"
|
|
89
|
+
"arm_neon.h": "c",
|
|
90
|
+
"__tree": "cpp",
|
|
91
|
+
"filesystem": "cpp"
|
|
90
92
|
},
|
|
91
93
|
"cSpell.words": [
|
|
94
|
+
"castsi",
|
|
92
95
|
"dtype",
|
|
93
96
|
"dtypes",
|
|
97
|
+
"jaccard",
|
|
94
98
|
"kann",
|
|
99
|
+
"Kullback",
|
|
100
|
+
"Leibler",
|
|
101
|
+
"loadu",
|
|
95
102
|
"maccs",
|
|
103
|
+
"maskz",
|
|
104
|
+
"napi",
|
|
96
105
|
"simd",
|
|
97
106
|
"simsimd",
|
|
107
|
+
"sqeuclidean",
|
|
98
108
|
"tanimoto",
|
|
99
|
-
"
|
|
109
|
+
"typedarray",
|
|
110
|
+
"unsw",
|
|
111
|
+
"Vardanian",
|
|
112
|
+
"vpopcntdq"
|
|
100
113
|
]
|
|
101
114
|
}
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
"tasks": [
|
|
4
4
|
{
|
|
5
5
|
"label": "Build Benchmark: Debug",
|
|
6
|
-
"command": "cmake -DCMAKE_BUILD_TYPE=Debug -B ./build_debug && make -j 2 -C ./build_debug",
|
|
6
|
+
"command": "cmake -DCMAKE_BUILD_TYPE=Debug -DSIMSIMD_BUILD_BENCHMARKS=1 -B ./build_debug && make -j 2 -C ./build_debug",
|
|
7
7
|
"args": [],
|
|
8
8
|
"type": "shell"
|
|
9
9
|
},
|
|
@@ -15,7 +15,7 @@
|
|
|
15
15
|
},
|
|
16
16
|
{
|
|
17
17
|
"label": "Build Benchmark: Release",
|
|
18
|
-
"command": "cmake -DCMAKE_BUILD_TYPE=RelWithDebInfo -B ./build_release && make -j 2 -C ./build_release",
|
|
18
|
+
"command": "cmake -DCMAKE_BUILD_TYPE=RelWithDebInfo -DSIMSIMD_BUILD_BENCHMARKS=1 -B ./build_release && make -j 2 -C ./build_release",
|
|
19
19
|
"args": [],
|
|
20
20
|
"type": "shell"
|
|
21
21
|
}
|
package/simsimd/CMakeLists.txt
CHANGED
|
@@ -1,18 +1,21 @@
|
|
|
1
|
+
# SimSIMD library CMakeLists.txt
|
|
1
2
|
cmake_minimum_required(VERSION 3.14 FATAL_ERROR)
|
|
2
|
-
project(simsimd LANGUAGES CXX
|
|
3
|
+
project(simsimd LANGUAGES C CXX)
|
|
4
|
+
|
|
5
|
+
set(CMAKE_C_STANDARD 11)
|
|
6
|
+
set(CMAKE_C_STANDARD_REQUIRED YES)
|
|
7
|
+
set(CMAKE_C_EXTENSIONS NO)
|
|
8
|
+
|
|
3
9
|
set(CMAKE_CXX_STANDARD 17)
|
|
4
10
|
set(CMAKE_CXX_STANDARD_REQUIRED YES)
|
|
5
11
|
set(CMAKE_CXX_EXTENSIONS NO)
|
|
6
12
|
|
|
7
|
-
option(SIMSIMD_USE_OPENMP "Use OpenMP for Parallelism" OFF)
|
|
8
13
|
option(SIMSIMD_BUILD_BENCHMARKS "Compile a micro-benchmark for current ISA" OFF)
|
|
9
14
|
|
|
10
|
-
#
|
|
15
|
+
# Fetch external dependencies
|
|
11
16
|
include(FetchContent)
|
|
12
|
-
include_directories(include/)
|
|
13
17
|
|
|
14
|
-
#
|
|
15
|
-
# https://github.com/google/benchmark/blob/main/docs/user_guide.md#using-register-benchmark
|
|
18
|
+
# Suppress building tests of Google Benchmark
|
|
16
19
|
set(BENCHMARK_ENABLE_TESTING OFF)
|
|
17
20
|
set(BENCHMARK_ENABLE_INSTALL OFF)
|
|
18
21
|
set(BENCHMARK_ENABLE_DOXYGEN OFF)
|
|
@@ -20,67 +23,45 @@ set(BENCHMARK_INSTALL_DOCS OFF)
|
|
|
20
23
|
set(BENCHMARK_DOWNLOAD_DEPENDENCIES ON)
|
|
21
24
|
set(BENCHMARK_ENABLE_GTEST_TESTS OFF)
|
|
22
25
|
set(BENCHMARK_USE_BUNDLED_GTEST ON)
|
|
26
|
+
|
|
23
27
|
FetchContent_Declare(
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
)
|
|
28
|
+
benchmark
|
|
29
|
+
GIT_REPOSITORY https://github.com/google/benchmark.git
|
|
30
|
+
GIT_TAG v1.7.0)
|
|
28
31
|
FetchContent_MakeAvailable(benchmark)
|
|
29
32
|
|
|
30
|
-
#
|
|
33
|
+
# Default to Release build type if not set
|
|
31
34
|
if(NOT CMAKE_BUILD_TYPE)
|
|
32
|
-
|
|
35
|
+
set(CMAKE_BUILD_TYPE Release)
|
|
33
36
|
endif()
|
|
34
37
|
|
|
35
|
-
|
|
36
|
-
set(
|
|
37
|
-
set(
|
|
38
|
-
set(
|
|
38
|
+
# Global compiler flags for debug and release
|
|
39
|
+
set(CMAKE_CXX_FLAGS_DEBUG "-g -fsanitize=address")
|
|
40
|
+
set(CMAKE_CXX_FLAGS_RELEASE "-O3")
|
|
41
|
+
set(CMAKE_C_FLAGS_DEBUG "-g -fsanitize=address")
|
|
42
|
+
set(CMAKE_C_FLAGS_RELEASE "-O3")
|
|
39
43
|
|
|
40
|
-
#
|
|
41
|
-
|
|
42
|
-
if(
|
|
44
|
+
# Compiler-specific flags
|
|
45
|
+
if(CMAKE_CXX_COMPILER_ID MATCHES "^(Apple)?Clang$")
|
|
46
|
+
if(NOT APPLE)
|
|
47
|
+
add_compile_options(-march=native)
|
|
48
|
+
endif()
|
|
49
|
+
add_compile_options(-pedantic -ferror-limit=1 -ffast-math)
|
|
43
50
|
elseif(CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
|
|
44
|
-
|
|
45
|
-
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -pedantic -fmax-errors=1")
|
|
46
|
-
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -march=native")
|
|
47
|
-
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -pedantic -fmax-errors=1")
|
|
48
|
-
elseif(CMAKE_CXX_COMPILER_ID STREQUAL "NVIDIA" OR CMAKE_CXX_COMPILER_ID STREQUAL "NVHPC")
|
|
49
|
-
set(CMAKE_CUDA_FLAGS "${CMAKE_CUDA_FLAGS} --expt-relaxed-constexpr --extended-lambda")
|
|
50
|
-
set(CMAKE_CUDA_FLAGS "${CMAKE_CUDA_FLAGS} -gencode arch=compute_86,code=sm_86")
|
|
51
|
-
|
|
51
|
+
add_compile_options(-march=native -pedantic -fmax-errors=1 -ffast-math)
|
|
52
52
|
elseif(CMAKE_CXX_COMPILER_ID STREQUAL "IntelLLVM")
|
|
53
|
-
|
|
54
|
-
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -ferror-limit=1")
|
|
53
|
+
add_compile_options(-w -ferror-limit=1)
|
|
55
54
|
endif()
|
|
56
55
|
|
|
57
|
-
# Define
|
|
56
|
+
# Define the header-only library
|
|
57
|
+
file(GLOB SIMSIMD_SOURCES include/simsimd/*.h)
|
|
58
58
|
add_library(simsimd INTERFACE)
|
|
59
|
-
target_sources(simsimd INTERFACE
|
|
59
|
+
target_sources(simsimd INTERFACE ${SIMSIMD_SOURCES})
|
|
60
60
|
target_include_directories(simsimd INTERFACE "${PROJECT_SOURCE_DIR}/include")
|
|
61
61
|
|
|
62
|
-
# Build
|
|
63
|
-
if(
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
if(${SIMSIMD_USE_OPENMP})
|
|
69
|
-
find_package(OpenMP REQUIRED)
|
|
70
|
-
target_link_libraries(simsimd_bench OpenMP::OpenMP_CXX)
|
|
71
|
-
endif()
|
|
72
|
-
|
|
73
|
-
if(CMAKE_CXX_COMPILER_ID STREQUAL "Clang")
|
|
74
|
-
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS}")
|
|
75
|
-
|
|
76
|
-
elseif(CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
|
|
77
|
-
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS}")
|
|
78
|
-
|
|
79
|
-
elseif(CMAKE_CXX_COMPILER_ID STREQUAL "NVIDIA" OR CMAKE_CXX_COMPILER_ID STREQUAL "NVHPC")
|
|
80
|
-
set_property(SOURCE simsimd_bench.cpp PROPERTY LANGUAGE CUDA)
|
|
81
|
-
set_target_properties(simsimd_bench PROPERTIES POSITION_INDEPENDENT_CODE ON)
|
|
82
|
-
set_target_properties(simsimd_bench PROPERTIES CUDA_ARCHITECTURES "86")
|
|
83
|
-
|
|
84
|
-
elseif(CMAKE_CXX_COMPILER_ID STREQUAL "Intel" OR CMAKE_CXX_COMPILER_ID STREQUAL "IntelLLVM")
|
|
85
|
-
endif()
|
|
62
|
+
# Build benchmarks if required
|
|
63
|
+
if(SIMSIMD_BUILD_BENCHMARKS)
|
|
64
|
+
find_package(Threads REQUIRED)
|
|
65
|
+
add_executable(simsimd_bench cpp/bench.cxx)
|
|
66
|
+
target_link_libraries(simsimd_bench simsimd Threads::Threads benchmark)
|
|
86
67
|
endif()
|
package/simsimd/README.md
CHANGED
|
@@ -1,41 +1,129 @@
|
|
|
1
1
|
# SimSIMD 📏
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
They are tuned for Machine Learning applications and mid-size vectors with 100-1024 dimensions.
|
|
5
|
-
One can expect the following performance for Cosine (Angular) distance, the most common metric in AI.
|
|
3
|
+
## Efficient Alternative to [`scipy.spatial.distance`][scipy] and [`numpy.inner`][numpy]
|
|
6
4
|
|
|
7
|
-
|
|
8
|
-
| :----- | :------ | :--------- | ------------: | -------------: |
|
|
9
|
-
| Serial | `f32` | ✅ | 5 GB/s | 5 GB/s |
|
|
10
|
-
| SVE | `f32` | ✅ | 34 GB/s | 40 GB/s |
|
|
11
|
-
| SVE | `f16` | ✅ | 28 GB/s | 35 GB/s |
|
|
12
|
-
| NEON | `f16` | ❌ | 16 GB/s | 18 GB/s |
|
|
5
|
+
SimSIMD leverages SIMD intrinsics, capabilities that only select compilers effectively utilize. This framework supports conventional AVX2 instructions on x86, NEON on Arm, as well as __rare__ AVX-512 FP16 instructions on x86 and Scalable Vector Extensions (SVE) on Arm. Designed specifically for Machine Learning contexts, it's optimized for handling high-dimensional vector embeddings.
|
|
13
6
|
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
7
|
+
- ✅ __3-200x faster__ than NumPy and SciPy distance functions.
|
|
8
|
+
- ✅ Euclidean (L2), Inner Product, and Cosine (Angular) spatial distances.
|
|
9
|
+
- ✅ Hamming (~ Manhattan) and Jaccard (~ Tanimoto) binary distances.
|
|
10
|
+
- ✅ Single-precision `f32`, half-precision `f16`, `i8`, and binary vectors.
|
|
11
|
+
- ✅ Compatible with GCC and Clang on MacOS and Linux, and MinGW on Windows.
|
|
12
|
+
- ✅ Compatible with NumPy, PyTorch, TensorFlow, and other tensors.
|
|
13
|
+
- ✅ Has __no dependencies__, not even LibC.
|
|
14
|
+
- ✅ [JavaScript API](#using-simsimd-in-javascript).
|
|
15
|
+
- ✅ [C API](#using-simsimd-in-c).
|
|
18
16
|
|
|
19
|
-
|
|
17
|
+
[scipy]: https://docs.scipy.org/doc/scipy/reference/spatial.distance.html#module-scipy.spatial.distance
|
|
18
|
+
[numpy]: https://numpy.org/doc/stable/reference/generated/numpy.inner.html
|
|
20
19
|
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
20
|
+
## Benchmarks
|
|
21
|
+
|
|
22
|
+
### Apple M2 Pro
|
|
23
|
+
|
|
24
|
+
Given 1000 embeddings from OpenAI Ada API with 1536 dimensions, running on the Apple M2 Pro Arm CPU with NEON support, here's how SimSIMD performs against conventional methods:
|
|
25
|
+
|
|
26
|
+
| Conventional | SimSIMD | `f32` improvement | `f16` improvement | `i8` improvement |
|
|
27
|
+
| :------------------------------------- | :---------------- | ----------------: | ----------------: | ---------------: |
|
|
28
|
+
| `numpy.inner` | `inner` | __2 x__ | __9 x__ | __18 x__ |
|
|
29
|
+
| `scipy.spatial.distance.cosine` | `cosine` | __32 x__ | __79 x__ | __133 x__ |
|
|
30
|
+
| `scipy.spatial.distance.sqeuclidean` | `sqeuclidean` | __5 x__ | __26 x__ | __17 x__ |
|
|
31
|
+
| `scipy.spatial.distance.jensenshannon` | `jensenshannon` | __41 x__ | __76 x__ | |
|
|
32
|
+
| `scipy.special.kl_div` | `kullbackleibler` | __21 x__ | __18 x__ | |
|
|
33
|
+
|
|
34
|
+
### Intel Sapphire Rapids
|
|
35
|
+
|
|
36
|
+
On the Intel Sapphire Rapids platform, SimSIMD was benchmarked against autovectorized-code using GCC 12. GCC handles single-precision `float` and `int8_t` well. However, it fails on `_Float16` arrays, which has been part of the C language since 2011.
|
|
37
|
+
|
|
38
|
+
| | GCC 12 `f32` | GCC 12 `f16` | SimSIMD `f16` | `f16` improvement |
|
|
39
|
+
| :------------ | -----------: | -----------: | ------------: | ----------------: |
|
|
40
|
+
| `cosine` | 3.28 M/s | 336.29 k/s | 6.88 M/s | __20 x__ |
|
|
41
|
+
| `sqeuclidean` | 4.62 M/s | 147.25 k/s | 5.32 M/s | __36 x__ |
|
|
42
|
+
| `inner` | 3.81 M/s | 192.02 k/s | 5.99 M/s | __31 x__ |
|
|
43
|
+
|
|
44
|
+
__Technical Insights__:
|
|
45
|
+
|
|
46
|
+
- [Uses Arm SVE and x86 AVX-512's masked loads to eliminate tail `for`-loops](https://ashvardanian.com/posts/simsimd-faster-scipy/#tails-of-the-past-the-significance-of-masked-loads).
|
|
47
|
+
- [Uses AVX-512 FP16 for half-precision operations, that few compilers vectorize](https://ashvardanian.com/posts/simsimd-faster-scipy/#the-challenge-of-f16).
|
|
48
|
+
- [Substitutes LibC's `sqrt` calls with bithacks using Jan Kadlec's constant](https://ashvardanian.com/posts/simsimd-faster-scipy/#bonus-section-bypassing-sqrt-and-libc-dependencies).
|
|
49
|
+
- Avoids slow PyBind11 and SWIG, directly using the CPython C API.
|
|
50
|
+
- Avoids slow `PyArg_ParseTuple` and manually unpacks argument tuples.
|
|
51
|
+
|
|
52
|
+
__Broader Benchmarking Results__:
|
|
53
|
+
|
|
54
|
+
- [Apple M2 Pro](https://ashvardanian.com/posts/simsimd-faster-scipy/#appendix-1-performance-on-apple-m2-pro).
|
|
55
|
+
- [4th Gen Intel Xeon Platinum](https://ashvardanian.com/posts/simsimd-faster-scipy/#appendix-2-performance-on-4th-gen-intel-xeon-platinum-8480).
|
|
56
|
+
- [AWS Graviton 3](https://ashvardanian.com/posts/simsimd-faster-scipy/#appendix-3-performance-on-aws-graviton-3).
|
|
57
|
+
|
|
58
|
+
## Using in Python
|
|
59
|
+
|
|
60
|
+
### Installation
|
|
61
|
+
|
|
62
|
+
```sh
|
|
63
|
+
pip install simsimd
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
### Distance Between 2 Vectors
|
|
67
|
+
|
|
68
|
+
```py
|
|
69
|
+
import simsimd
|
|
70
|
+
import numpy as np
|
|
71
|
+
|
|
72
|
+
vec1 = np.random.randn(1536).astype(np.float32)
|
|
73
|
+
vec2 = np.random.randn(1536).astype(np.float32)
|
|
74
|
+
dist = simsimd.cosine(vec1, vec2)
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
Supported functions include `cosine`, `inner`, `sqeuclidean`, `hamming`, and `jaccard`.
|
|
78
|
+
|
|
79
|
+
### Distance Between 2 Batches
|
|
80
|
+
|
|
81
|
+
```py
|
|
82
|
+
batch1 = np.random.randn(100, 1536).astype(np.float32)
|
|
83
|
+
batch2 = np.random.randn(100, 1536).astype(np.float32)
|
|
84
|
+
dist = simsimd.cosine(batch1, batch2)
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
If either batch has more than one vector, the other batch must have one or same number of vectors.
|
|
88
|
+
If it contains just one, the value is broadcasted.
|
|
89
|
+
|
|
90
|
+
### All Pairwise Distances
|
|
91
|
+
|
|
92
|
+
For calculating distances between all possible pairs of rows across two matrices (akin to [`scipy.spatial.distance.cdist`](https://docs.scipy.org/doc/scipy/reference/generated/scipy.spatial.distance.cdist.html)):
|
|
93
|
+
|
|
94
|
+
```py
|
|
95
|
+
matrix1 = np.random.randn(1000, 1536).astype(np.float32)
|
|
96
|
+
matrix2 = np.random.randn(10, 1536).astype(np.float32)
|
|
97
|
+
distances = simsimd.cdist(matrix1, matrix2, metric="cosine")
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
### Multithreading
|
|
101
|
+
|
|
102
|
+
By default, computations use a single CPU core. To optimize and utilize all CPU cores on Linux systems, add the `threads=0` argument. Alternatively, specify a custom number of threads:
|
|
103
|
+
|
|
104
|
+
```py
|
|
105
|
+
distances = simsimd.cdist(matrix1, matrix2, metric="cosine", threads=0)
|
|
29
106
|
```
|
|
30
107
|
|
|
108
|
+
### Hardware Backend Capabilities
|
|
109
|
+
|
|
110
|
+
To view a list of hardware backends that SimSIMD supports:
|
|
111
|
+
|
|
112
|
+
```py
|
|
113
|
+
print(simsimd.get_capabilities())
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
### Using Python API with USearch
|
|
117
|
+
|
|
31
118
|
Want to use it in Python with [USearch](https://github.com/unum-cloud/usearch)?
|
|
119
|
+
You can wrap the raw C function pointers SimSIMD backends into a `CompiledMetric`, and pass it to USearch, similar to how it handles Numba's JIT-compiled code.
|
|
32
120
|
|
|
33
121
|
```py
|
|
34
|
-
from usearch import Index, CompiledMetric, MetricKind, MetricSignature
|
|
35
|
-
from simsimd import
|
|
122
|
+
from usearch.index import Index, CompiledMetric, MetricKind, MetricSignature
|
|
123
|
+
from simsimd import pointer_to_sqeuclidean, pointer_to_cosine, pointer_to_inner
|
|
36
124
|
|
|
37
125
|
metric = CompiledMetric(
|
|
38
|
-
pointer=
|
|
126
|
+
pointer=pointer_to_cosine("f16"),
|
|
39
127
|
kind=MetricKind.Cos,
|
|
40
128
|
signature=MetricSignature.ArrayArraySize,
|
|
41
129
|
)
|
|
@@ -43,100 +131,66 @@ metric = CompiledMetric(
|
|
|
43
131
|
index = Index(256, metric=metric)
|
|
44
132
|
```
|
|
45
133
|
|
|
46
|
-
##
|
|
47
|
-
|
|
48
|
-
In the C99 interface, all functions are prepended with the `simsimd_` namespace prefix.
|
|
49
|
-
The signature defines the number of arguments:
|
|
50
|
-
|
|
51
|
-
- two pointers, and length,
|
|
52
|
-
- two pointers.
|
|
53
|
-
|
|
54
|
-
The latter is intended for cases where the number of dimensions is hard-coded.
|
|
55
|
-
Constraints define the limitations on the number of dimensions an argument vector can have.
|
|
56
|
-
|
|
57
|
-
| Name | Signature | ISA Extension | Constraints |
|
|
58
|
-
| :---------------------- | :-------: | :-----------: | :------------: |
|
|
59
|
-
| `dot_f32_sve` | ✳️✳️📏 | Arm SVE | |
|
|
60
|
-
| `dot_f32x4_neon` | ✳️✳️📏 | Arm NEON | `d % 4 == 0` |
|
|
61
|
-
| `cos_f32_sve` | ✳️✳️📏 | Arm SVE | |
|
|
62
|
-
| `cos_f16_sve` | ✳️✳️📏 | Arm SVE | |
|
|
63
|
-
| `cos_f16x4_neon` | ✳️✳️📏 | Arm NEON | `d % 4 == 0` |
|
|
64
|
-
| `cos_i8x16_neon` | ✳️✳️📏 | Arm NEON | `d % 16 == 0` |
|
|
65
|
-
| `cos_f32x4_neon` | ✳️✳️📏 | Arm NEON | `d % 4 == 0` |
|
|
66
|
-
| `cos_f16x16_avx512` | ✳️✳️📏 | x86 AVX-512 | `d % 16 == 0` |
|
|
67
|
-
| `cos_f32x4_avx2` | ✳️✳️📏 | x86 AVX2 | `d % 4 == 0` |
|
|
68
|
-
| `l2sq_f32_sve` | ✳️✳️📏 | Arm SVE | |
|
|
69
|
-
| `l2sq_f16_sve` | ✳️✳️📏 | Arm SVE | |
|
|
70
|
-
| `hamming_b1x8_sve` | ✳️✳️📏 | Arm SVE | `d % 8 == 0` |
|
|
71
|
-
| `hamming_b1x128_sve` | ✳️✳️📏 | Arm SVE | `d % 128 == 0` |
|
|
72
|
-
| `hamming_b1x128_avx512` | ✳️✳️📏 | x86 AVX-512 | `d % 128 == 0` |
|
|
73
|
-
| `tanimoto_b1x8_naive` | ✳️✳️📏 | | `d % 8 == 0` |
|
|
74
|
-
| `tanimoto_maccs_naive` | ✳️✳️ | | `d == 166` |
|
|
75
|
-
| `tanimoto_maccs_neon` | ✳️✳️ | Arm NEON | `d == 166` |
|
|
76
|
-
| `tanimoto_maccs_sve` | ✳️✳️ | Arm SVE | `d == 166` |
|
|
77
|
-
| `tanimoto_maccs_avx512` | ✳️✳️ | x86 AVX-512 | `d == 166` |
|
|
134
|
+
## Using SimSIMD in JavaScript
|
|
78
135
|
|
|
79
|
-
|
|
136
|
+
After you add `simsimd` as a dependency and `npm install`, you will be able to call SimSIMD function on various `TypedArray` variants:
|
|
137
|
+
|
|
138
|
+
```js
|
|
139
|
+
const { sqeuclidean, cosine, inner, hamming, jaccard } = require('simsimd');
|
|
140
|
+
|
|
141
|
+
const vectorA = new Float32Array([1.0, 2.0, 3.0]);
|
|
142
|
+
const vectorB = new Float32Array([4.0, 5.0, 6.0]);
|
|
143
|
+
|
|
144
|
+
const distance = sqeuclidean(vectorA, vectorB);
|
|
145
|
+
console.log('Squared Euclidean Distance:', distance);
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
## Using SimSIMD in C
|
|
149
|
+
|
|
150
|
+
If you're aiming to utilize the `_Float16` functionality with SimSIMD, ensure your development environment is compatible with C 11. For other functionalities of SimSIMD, C 99 compatibility will suffice.
|
|
151
|
+
|
|
152
|
+
For integration within a CMake-based project, add the following segment to your `CMakeLists.txt`:
|
|
153
|
+
|
|
154
|
+
```cmake
|
|
155
|
+
FetchContent_Declare(
|
|
156
|
+
simsimd
|
|
157
|
+
GIT_REPOSITORY https://github.com/ashvardanian/simsimd.git
|
|
158
|
+
GIT_SHALLOW TRUE
|
|
159
|
+
)
|
|
160
|
+
FetchContent_MakeAvailable(simsimd)
|
|
161
|
+
include_directories(${simsimd_SOURCE_DIR}/include)
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
Stay updated with the latest advancements by always using the most recent compiler available for your platform. This ensures that you benefit from the newest intrinsics.
|
|
165
|
+
|
|
166
|
+
Should you wish to integrate SimSIMD within USearch, simply compile USearch with the flag `USEARCH_USE_SIMSIMD=1`. Notably, this is the default setting on the majority of platforms.
|
|
167
|
+
|
|
168
|
+
__To rerun experiments__ utilize the following command:
|
|
169
|
+
|
|
170
|
+
```sh
|
|
171
|
+
cmake -DCMAKE_BUILD_TYPE=Release -DSIMSIMD_BUILD_BENCHMARKS=1 -B ./build_release && make -C ./build_release && ./build_release/simsimd_bench
|
|
172
|
+
```
|
|
80
173
|
|
|
81
|
-
|
|
82
|
-
Light-weight distance functions would be memory bound, implying that multi-core performance may be lower if the bus bandwidth cannot saturate all the cores.
|
|
83
|
-
Similarly, heavy-weight distance functions running on all cores may result in CPU frequency downclocking.
|
|
84
|
-
This is well illustrated by the single-core performance of the Intel `i9-13950HX`, equipped with DDR5 memory.
|
|
85
|
-
|
|
86
|
-
| Method | Threads | Vector Size | Speed |
|
|
87
|
-
| :--------------------- | ------: | ----------: | --------: |
|
|
88
|
-
| `dot_f32x4_avx2` | 1 | 1024 b | 96.2 GB/s |
|
|
89
|
-
| `dot_f32x4_avx2` | 32 | 1024 b | 23.6 GB/s |
|
|
90
|
-
| `cos_f32_naive` | 1 | 1024 b | 15.3 GB/s |
|
|
91
|
-
| `cos_f32_naive` | 32 | 1024 b | 4.5 GB/s |
|
|
92
|
-
| `cos_f32x4_avx2` | 1 | 1024 b | 56.3 GB/s |
|
|
93
|
-
| `cos_f32x4_avx2` | 32 | 1024 b | 13.9 GB/s |
|
|
94
|
-
| `tanimoto_maccs_naive` | 1 | 21 b | 2.8 GB/s |
|
|
95
|
-
| `tanimoto_maccs_naive` | 32 | 21 b | 1.2 GB/s |
|
|
96
|
-
|
|
97
|
-
Switching to the Intel Sapphire Rapids server platform, we can also evaluate some of the AVX-512 extensions, including `VPOPCNTDQ` and `F16`.
|
|
98
|
-
|
|
99
|
-
| Method | Threads | Vector Size | Speed |
|
|
100
|
-
| :---------------------- | ------: | ----------: | ---------: |
|
|
101
|
-
| `dot_f32x4_avx2` | 1 | 1024 b | 57.8 GB/s |
|
|
102
|
-
| `dot_f32x4_avx2` | 224 | 1024 b | 16.1 GB/s |
|
|
103
|
-
| `cos_f32_naive` | 1 | 1024 b | 10.7 GB/s |
|
|
104
|
-
| `cos_f32_naive` | 224 | 1024 b | 3.0 GB/s |
|
|
105
|
-
| `cos_f32x4_avx2` | 1 | 1024 b | 39.5 GB/s |
|
|
106
|
-
| `cos_f32x4_avx2` | 224 | 1024 b | 15.1 GB/s |
|
|
107
|
-
| `cos_f16x16_avx512` | 1 | 1024 b | 50.6 GB/s |
|
|
108
|
-
| `cos_f16x16_avx512` | 224 | 1024 b | 15.9 GB/s |
|
|
109
|
-
| `hamming_b1x128_avx512` | 1 | 1024 b | 790.3 GB/s |
|
|
110
|
-
| `hamming_b1x128_avx512` | 224 | 1024 b | 259.3 GB/s |
|
|
111
|
-
| `tanimoto_maccs_naive` | 1 | 21 b | 3.0 GB/s |
|
|
112
|
-
| `tanimoto_maccs_naive` | 224 | 21 b | 1.3 GB/s |
|
|
113
|
-
| `tanimoto_maccs_avx512` | 1 | 21 b | 13.1 GB/s |
|
|
114
|
-
| `tanimoto_maccs_avx512` | 224 | 21 b | 3.7 GB/s |
|
|
115
|
-
|
|
116
|
-
To replicate this on your hardware, please run the following on Linux:
|
|
174
|
+
__To test and benchmark with Python bindings__:
|
|
117
175
|
|
|
118
176
|
```sh
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
-B ./build && make -C ./build && ./build/simsimd_bench
|
|
177
|
+
pip install -e .
|
|
178
|
+
pytest python/test.py -s -x
|
|
179
|
+
python python/bench.py --n 1000 --ndim 1000000 # batch size and dimensions
|
|
123
180
|
```
|
|
124
181
|
|
|
125
|
-
|
|
182
|
+
__To test and benchmark JavaScript bindings__:
|
|
126
183
|
|
|
127
184
|
```sh
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
-DCMAKE_C_COMPILER="/opt/homebrew/opt/llvm/bin/clang" \
|
|
132
|
-
-DCMAKE_CXX_COMPILER="/opt/homebrew/opt/llvm/bin/clang++" \
|
|
133
|
-
-DSIMSIMD_BUILD_BENCHMARKS=1 \
|
|
134
|
-
&& \
|
|
135
|
-
make -C ./build -j && ./build/simsimd_bench
|
|
185
|
+
npm install --dev
|
|
186
|
+
npm test
|
|
187
|
+
npm bench
|
|
136
188
|
```
|
|
137
189
|
|
|
138
|
-
|
|
190
|
+
__To test and benchmark GoLang bindings__:
|
|
139
191
|
|
|
140
192
|
```sh
|
|
141
|
-
|
|
193
|
+
cd golang
|
|
194
|
+
go test # To test
|
|
195
|
+
go test -run=^$ -bench=. -benchmem # To benchmark
|
|
142
196
|
```
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
{
|
|
2
|
+
"targets": [
|
|
3
|
+
{
|
|
4
|
+
"target_name": "simsimd",
|
|
5
|
+
"sources": ["javascript/lib.c"],
|
|
6
|
+
"include_dirs": ["include"],
|
|
7
|
+
"cflags": [
|
|
8
|
+
"-std=c11",
|
|
9
|
+
"-ffast-math",
|
|
10
|
+
"-Wno-unknown-pragmas",
|
|
11
|
+
"-Wno-maybe-uninitialized",
|
|
12
|
+
"-Wno-cast-function-type",
|
|
13
|
+
"-Wno-switch",
|
|
14
|
+
"-DSIMSIMD_NATIVE_F16=0",
|
|
15
|
+
],
|
|
16
|
+
}
|
|
17
|
+
]
|
|
18
|
+
}
|