Hardware-Accelerated Similarity Metrics and Distance Functions
f64
double-, f32
single-, and f16
half-precision, i8
integral, and binary vectors.scipy.spatial.distance
and numpy.inner
.Implemented distance functions include:
Technical Insights and related articles:
for
-loops.sqrt
calls with bithacks using Jan Kadlec's constant.PyArg_ParseTuple
for speed.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:
Kind | f32 improvement | f16 improvement | i8 improvement | Conventional method | SimSIMD |
---|---|---|---|---|---|
Cosine | 32 x | 79 x | 133 x | scipy.spatial.distance.cosine | cosine |
Euclidean ² | 5 x | 26 x | 17 x | scipy.spatial.distance.sqeuclidean | sqeuclidean |
Inner Product | 2 x | 9 x | 18 x | numpy.inner | inner |
Jensen Shannon | 31 x | 53 x | scipy.spatial.distance.jensenshannon | jensenshannon |
On the Intel Sapphire Rapids platform, SimSIMD was benchmarked against auto-vectorized code using GCC 12. GCC handles single-precision float
but might not be the best choice for int8
and _Float16
arrays, which has been part of the C language since 2011.
Kind | GCC 12 f32 | GCC 12 f16 | SimSIMD f16 | f16 improvement |
---|---|---|---|---|
Cosine | 3.28 M/s | 336.29 k/s | 6.88 M/s | 20 x |
Euclidean ² | 4.62 M/s | 147.25 k/s | 5.32 M/s | 36 x |
Inner Product | 3.81 M/s | 192.02 k/s | 5.99 M/s | 31 x |
Jensen Shannon | 1.18 M/s | 18.13 k/s | 2.14 M/s | 118 x |
Broader Benchmarking Results:
pip install simsimd
import simsimd
import numpy as np
vec1 = np.random.randn(1536).astype(np.float32)
vec2 = np.random.randn(1536).astype(np.float32)
dist = simsimd.cosine(vec1, vec2)
Supported functions include cosine
, inner
, sqeuclidean
, hamming
, and jaccard
.
batch1 = np.random.randn(100, 1536).astype(np.float32)
batch2 = np.random.randn(100, 1536).astype(np.float32)
dist = simsimd.cosine(batch1, batch2)
If either batch has more than one vector, the other batch must have one or the same number of vectors. If it contains just one, the value is broadcasted.
For calculating distances between all possible pairs of rows across two matrices (akin to scipy.spatial.distance.cdist
):
matrix1 = np.random.randn(1000, 1536).astype(np.float32)
matrix2 = np.random.randn(10, 1536).astype(np.float32)
distances = simsimd.cdist(matrix1, matrix2, metric="cosine")
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:
distances = simsimd.cdist(matrix1, matrix2, metric="cosine", threads=0)
To view a list of hardware backends that SimSIMD supports:
print(simsimd.get_capabilities())
Want to use it in Python with USearch? 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.
from usearch.index import Index, CompiledMetric, MetricKind, MetricSignature
from simsimd import pointer_to_sqeuclidean, pointer_to_cosine, pointer_to_inner
metric = CompiledMetric(
pointer=pointer_to_cosine("f16"),
kind=MetricKind.Cos,
signature=MetricSignature.ArrayArraySize,
)
index = Index(256, metric=metric)
To install, choose one of the following options depending on your environment:
npm install --save simsimd
yarn add simsimd
pnpm add simsimd
bun install simsimd
The package is distributed with prebuilt binaries for Node.js v10 and above for Linux (x86_64, arm64), macOS (x86_64, arm64), and Windows (i386,x86_64).
If your platform is not supported, you can build the package from source via npm run build
. This will automatically happen unless you install the package with --ignore-scripts
flag or use Bun.
After you install it, you will be able to call the SimSIMD functions on various TypedArray
variants:
const { sqeuclidean, cosine, inner, hamming, jaccard } = require('simsimd');
const vectorA = new Float32Array([1.0, 2.0, 3.0]);
const vectorB = new Float32Array([4.0, 5.0, 6.0]);
const distance = sqeuclidean(vectorA, vectorB);
console.log('Squared Euclidean Distance:', distance);
For integration within a CMake-based project, add the following segment to your CMakeLists.txt
:
FetchContent_Declare(
simsimd
GIT_REPOSITORY https://github.com/ashvardanian/simsimd.git
GIT_SHALLOW TRUE
)
FetchContent_MakeAvailable(simsimd)
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. A minimal usage example would be:
#include <simsimd/simsimd.h>
int main() {
simsimd_f32_t vector_a[1536];
simsimd_f32_t vector_b[1536];
simsimd_f32_t distance = simsimd_avx512_f32_cos(vector_a, vector_b, 1536);
return 0;
}
All of the functions names follow the same pattern: simsimd_{backend}_{type}_{metric}
.
avx512
, avx2
, neon
, or sve
.f64
, f32
, f16
, i8
, or b8
.cos
, ip
, l2sq
, hamming
, jaccard
, kl
, or js
.In case you want to avoid hard-coding the backend, you can use the simsimd_metric_punned_t
to pun the function pointer, and simsimd_capabilities
function to get the available backends at runtime.
To rerun experiments utilize the following command:
cmake -DCMAKE_BUILD_TYPE=Release -DSIMSIMD_BUILD_BENCHMARKS=1 -B ./build_release
cmake --build build_release --config Release
./build_release/simsimd_bench
./build_release/simsimd_bench --benchmark_filter=js
To test and benchmark with Python bindings:
pip install -e .
pytest python/test.py -s -x
pip install numpy scipy scikit-learn # for comparison baselines
python python/bench.py # to run default benchmarks
python python/bench.py --n 1000 --ndim 1000000 # batch size and dimensions
To test and benchmark JavaScript bindings:
npm install --dev
npm test
npm run bench
To test and benchmark GoLang bindings:
cd golang
go test # To test
go test -run=^$ -bench=. -benchmem # To benchmark
Author: Ashvardanian
Source Code: https://github.com/ashvardanian/simsimd
License: Apache-2.0 license