17个稳定版本
新 5.0.1 | 2024年8月18日 |
---|---|
4.4.0 | 2024年6月20日 |
4.2.2 | 2024年3月31日 |
3.9.0 | 2024年3月4日 |
0.1.2 |
|
#25 in 硬件支持
3,598次每月下载
在 2 crates中使用
83KB
987 行
SimSIMD 📏
在机器学习、科学计算、地理空间分析和信息检索中,计算低维和高维向量之间的点积、相似度测度和距离是普遍存在的。这些算法通常具有线性时间复杂度,空间复杂度恒定,并且是数据并行的。换句话说,它易于并行化和矢量化,通常在BLAS和LAPACK等包以及高级的numpy
和scipy
Python库中可用。讽刺的是,尽管编译器和数值计算已经发展了几十年,大多数库即使在最流行的硬件(如64位x86和Arm CPU)上,也通常比硬件潜力慢3-200倍。SimSIMD试图填补这一空白。 1️⃣ SimSIMD函数的运行速度几乎与memcpy
相同。2️⃣ SimSIMD比NumPy(105 vs 35)编译到更多平台,并且比大多数BLAS实现拥有更多的后端。
特性
SimSIMD提供了100多个针对各种距离和相似度度量的SIMD优化内核,加速了USearch和几个数据库管理系统产品的搜索。实现的距离函数包括
- 向量搜索的空间距离(欧几里得(L2)和余弦(角度)距离)。
- 实数和复数向量的点积,用于DSP和量子计算。
- 汉明(~曼哈顿)和贾卡德(~坦尼托)位级距离。
- 概率分布的Kullback-Leibler和Jensen-Shannon散度。
- 用于地理空间分析的哈弗辛公式和文森蒂公式。
- 对于Levenshtein、Needleman-Wunsch和其他文本度量,请查看StringZilla。
此外,SimSIMD...
- 处理
f64
、f32
和f16
实数和复数向量。 - 处理
i8
整数和b8
二进制向量。 - 是一个零依赖的只包含头文件的C 99库。
- 支持Python、Rust和JavaScript。
- 具有NEON和可伸缩向量扩展(SVE)的Arm后端。
- 具有Haswell、Skylake、Ice Lake和Sapphire Rapids的x86后端。
由于不同x86 CPU上SIMD支持的碎片化程度很高,SimSIMD使用选定英特尔CPU代号的名称作为其后端。然而,它们也适用于AMD CPU。英特尔Haswell与AMD Zen 1/2/3兼容,而AMD Genoa Zen 4涵盖了添加到英特尔Skylake和Ice Lake的AVX-512指令。您可以在以下博客文章中了解更多有关技术实现细节的信息:
- 使用Horner方法进行多项式逼近,比GCC 12快119倍.
- 使用Arm SVE和x86 AVX-512的掩码加载来消除尾部循环.
- 使用AVX-512 FP16进行半精度运算,而很少的编译器会进行向量化.
- 用Jan Kadlec的常量通过位操作替换LibC的
sqrt
调用. - 对于Python,为速度避免慢速的PyBind11、SWIG,甚至
PyArg_ParseTuple
. - 对于JavaScript,使用类型数组进行零拷贝调用.
基准测试
与NumPy和SciPy进行比较
给定来自OpenAI Ada API的1000个1536维嵌入,在支持NEON的Apple M2 Pro Arm CPU上运行,以下是SimSIMD与常规方法的性能表现:
类型 | f32 改进 |
f16 改进 |
i8 改进 |
常规方法 | SimSIMD |
---|---|---|---|---|---|
内积 | 2倍 | 9倍 | 18倍 | numpy.inner |
inner |
余弦距离 | 32倍 | 79倍 | 133倍 | scipy.spatial.distance.cosine |
cosine |
欧几里得距离² | 5倍 | 26倍 | 17倍 | scipy.spatial.distance.sqeuclidean |
sqeuclidean |
Jensen-Shannon 散度 | 31倍 | 53倍 | scipy.spatial.distance.jensenshannon |
jensenshannon |
与GCC自动向量化进行比较
在英特尔Sapphire Rapids平台上,SimSIMD与使用GCC 12自动向量化的代码进行了基准测试。GCC处理单精度float
,但对于自2011年以来一直是C语言一部分的int8
和_Float16
数组,可能不是最佳选择。
类型 | GCC 12 f32 |
GCC 12 f16 |
SimSIMD f16 |
f16 改进 |
---|---|---|---|---|
内积 | 3,810 K/s | 192 K/s | 5,990 K/s | 31倍 |
余弦距离 | 3,280 K/s | 336 K/s | 6,880 K/s | 20倍 |
欧几里得距离² | 4,620 K/s | 147 K/s | 5,320 K/s | 36倍 |
Jensen-Shannon 散度 | 1,180 K/s | 18 K/s | 2,140 K/s | 118倍 |
更广泛的基准测试结果:
在Python中使用SimSIMD
该包旨在取代对 numpy.inner
、numpy.dot
和 scipy.spatial.distance
的使用。除了显著提升性能外,SimSIMD 在混合精度配置中大幅提高了精度。NumPy 和 SciPy 处理 i8
或 f16
向量时,将使用相同的累加器类型,而 SimSIMD 可以结合 i8
枚举、i16
乘法和 i32
累加,以完全避免溢出。同样适用于以 f32
精度处理 f16
值。
安装
使用以下代码片段安装 SimSIMD 并列出您的机器上可用的硬件加速选项
pip install simsimd
python -c "import simsimd; print(simsimd.get_capabilities())"
一对一距离
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)
支持的功能包括 cosine
、inner
、sqeuclidean
、hamming
和 jaccard
。支持实数和复数的点积
vec1 = np.random.randn(768).astype(np.float64) + 1j * np.random.randn(768).astype(np.float64)
vec2 = np.random.randn(768).astype(np.float64) + 1j * np.random.randn(768).astype(np.float64)
dist = simsimd.dot(vec1.astype(np.complex128), vec2.astype(np.complex128))
dist = simsimd.dot(vec1.astype(np.complex64), vec2.astype(np.complex64))
dist = simsimd.vdot(vec1.astype(np.complex64), vec2.astype(np.complex64)) # conjugate, same as `np.vdot`
与 SciPy 不同,SimSIMD 允许显式声明输入向量的精度,这对于混合精度配置特别有用。
dist = simsimd.cosine(vec1, vec2, "i8")
dist = simsimd.cosine(vec1, vec2, "f16")
dist = simsimd.cosine(vec1, vec2, "f32")
dist = simsimd.cosine(vec1, vec2, "f64")
它还允许使用 SimSIMD 处理 NumPy 不支持的半精度复数。为此,将数据视为连续的偶数长度的 np.float16
向量,并用 complex32
字符串覆盖类型解析。
vec1 = np.random.randn(1536).astype(np.float16)
vec2 = np.random.randn(1536).astype(np.float16)
simd.dot(vec1, vec2, "complex32")
simd.vdot(vec1, vec2, "complex32")
一对多距离
每个距离函数不仅可以用于一对一,还可以用于一对多和多多距离计算。对于一对多
vec1 = np.random.randn(1536).astype(np.float32) # rank 1 tensor
batch1 = np.random.randn(1, 1536).astype(np.float32) # rank 2 tensor
batch2 = np.random.randn(100, 1536).astype(np.float32)
dist_rank1 = simsimd.cosine(vec1, batch2)
dist_rank2 = simsimd.cosine(batch1, batch2)
多多距离
SimSIMD 中的所有距离函数都可以用于计算多多距离。对于两个包含 100 个向量的批次计算 100 个距离,可以这样调用
batch1 = np.random.randn(100, 1536).astype(np.float32)
batch2 = np.random.randn(100, 1536).astype(np.float32)
dist = simsimd.cosine(batch1, batch2)
输入矩阵必须具有相同的形状。这种功能在 NumPy 或 SciPy 中不是原生存在的,通常需要创建中间数组,这既低效又消耗内存。
多多所有对距离
可以使用 SimSIMD 计算两个矩阵中所有可能行对之间的距离(类似于 scipy.spatial.distance.cdist
)。结果对象将具有 DistancesTensor
类型,与 NumPy 和其他库零拷贝兼容。对于两个包含 10 和 1,000 个条目的数组,结果张量将有 10,000 个单元格
import numpy as np
from simsimd import cdist, DistancesTensor
matrix1 = np.random.randn(1000, 1536).astype(np.float32)
matrix2 = np.random.randn(10, 1536).astype(np.float32)
distances: DistancesTensor = simsimd.cdist(matrix1, matrix2, metric="cosine") # zero-copy
distances_array: np.ndarray = np.array(distances, copy=True) # now managed by NumPy
多线程
默认情况下,计算使用单个 CPU 核心。为了在 Linux 系统上优化并利用所有 CPU 核心,请添加 threads=0
参数。或者,指定自定义的线程数
distances = simsimd.cdist(matrix1, matrix2, metric="cosine", threads=0)
使用 USearch 与 Python API
想用 USearch 在 Python 中使用它吗?您可以包装 SimSIMD 后端的原始 C 函数指针,将其包装到 CompiledMetric
中,并将其传递给 USearch,类似于它处理 Numba 的 JIT 编译代码。
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)
在 Rust 中使用 SimSIMD
要安装,请将以下内容添加到您的 Cargo.toml
[dependencies]
simsimd = "..."
在使用 SimSIMD 库之前,请确保您已将必要的特性和类型导入到 Rust 源文件中。该库为不同的距离/相似度类型提供了几个特性 - SpatialSimilarity
、BinarySimilarity
和 ProbabilitySimilarity
。
空间相似度:余弦和欧几里得距离
use simsimd::SpatialSimilarity;
fn main() {
let vector_a: Vec<f32> = vec![1.0, 2.0, 3.0];
let vector_b: Vec<f32> = vec![4.0, 5.0, 6.0];
// Compute the cosine similarity between vector_a and vector_b
let cosine_similarity = f32::cosine(&vector_a, &vector_b)
.expect("Vectors must be of the same length");
println!("Cosine Similarity: {}", cosine_similarity);
// Compute the squared Euclidean distance between vector_a and vector_b
let sq_euclidean_distance = f32::sqeuclidean(&vector_a, &vector_b)
.expect("Vectors must be of the same length");
println!("Squared Euclidean Distance: {}", sq_euclidean_distance);
}
空间相似度函数适用于 f64
、f32
、f16
和 i8
类型。
点积:内积和复数内积
use simsimd::SpatialSimilarity;
use simsimd::ComplexProducts;
fn main() {
let vector_a: Vec<f32> = vec![1.0, 2.0, 3.0, 4.0];
let vector_b: Vec<f32> = vec![5.0, 6.0, 7.0, 8.0];
// Compute the inner product between vector_a and vector_b
let inner_product = SpatialSimilarity::dot(&vector_a, &vector_b)
.expect("Vectors must be of the same length");
println!("Inner Product: {}", inner_product);
// Compute the complex inner product between complex_vector_a and complex_vector_b
let complex_inner_product = ComplexProducts::dot(&vector_a, &vector_b)
.expect("Vectors must be of the same length");
let complex_conjugate_inner_product = ComplexProducts::vdot(&vector_a, &vector_b)
.expect("Vectors must be of the same length");
println!("Complex Inner Product: {:?}", complex_inner_product); // -18, 69
println!("Complex C. Inner Product: {:?}", complex_conjugate_inner_product); // 70, -8
}
复数内积适用于 f64
、f32
和 f16
类型。
概率分布:Jensen-Shannon 和 Kullback-Leibler 散度
use simsimd::SpatialSimilarity;
fn main() {
let vector_a: Vec<f32> = vec![1.0, 2.0, 3.0];
let vector_b: Vec<f32> = vec![4.0, 5.0, 6.0];
let cosine_similarity = f32::jensenshannon(&vector_a, &vector_b)
.expect("Vectors must be of the same length");
println!("Cosine Similarity: {}", cosine_similarity);
let sq_euclidean_distance = f32::kullbackleibler(&vector_a, &vector_b)
.expect("Vectors must be of the same length");
println!("Squared Euclidean Distance: {}", sq_euclidean_distance);
}
概率相似度函数适用于 f64
、f32
和 f16
类型。
二进制相似度:汉明和贾卡德距离
类似于空间距离,可以计算无符号整数切片之间的位级距离函数
use simsimd::BinarySimilarity;
fn main() {
let vector_a = &[0b11110000, 0b00001111, 0b10101010];
let vector_b = &[0b11110000, 0b00001111, 0b01010101];
// Compute the Hamming distance between vector_a and vector_b
let hamming_distance = u8::hamming(&vector_a, &vector_b)
.expect("Vectors must be of the same length");
println!("Hamming Distance: {}", hamming_distance);
// Compute the Jaccard distance between vector_a and vector_b
let jaccard_distance = u8::jaccard(&vector_a, &vector_b)
.expect("Vectors must be of the same length");
println!("Jaccard Distance: {}", jaccard_distance);
}
二进制相似度函数仅适用于 u8
类型。
半精度浮点数
Rust 没有原生支持半精度浮点数,但 SimSIMD 提供了 f16
类型。它没有功能 - 它是 transparent
的包装器,围绕 u16
,并可以与 half
或任何其他半精度库一起使用。
use simsimd::SpatialSimilarity;
use simsimd::f16 as SimF16;
use half::f16 as HalfF16;
fn main() {
let vector_a: Vec<HalfF16> = ...
let vector_b: Vec<HalfF16> = ...
let buffer_a: &[SimF16] = unsafe { std::slice::from_raw_parts(a_half.as_ptr() as *const SimF16, a_half.len()) };
let buffer_b: &[SimF16] = unsafe { std::slice::from_raw_parts(b_half.as_ptr() as *const SimF16, b_half.len()) };
// Compute the cosine similarity between vector_a and vector_b
let cosine_similarity = SimF16::cosine(&vector_a, &vector_b)
.expect("Vectors must be of the same length");
println!("Cosine Similarity: {}", cosine_similarity);
}
半精度 Brain-Float 数
"brain-float-16" 是一种流行的机器学习格式。它在硬件中得到广泛支持,非常适合机器使用,但软件支持仍然落后。与 NumPy 不同,您已经在 SimSIMD 中可以使用 bf16
数据类型。幸运的是,要将 f32
转换为 bf16
,您只需要删除最后 16 位
import numpy as np
import simsimd as simd
a = np.random.randn(ndim).astype(np.float32)
b = np.random.randn(ndim).astype(np.float32)
# NumPy doesn't natively support brain-float, so we need a trick!
# Luckily, it's very easy to reduce the representation accuracy
# by simply masking the low 16-bits of our 32-bit single-precision
# numbers. We can also add `0x8000` to round the numbers.
a_f32rounded = ((a.view(np.uint32) + 0x8000) & 0xFFFF0000).view(np.float32)
b_f32rounded = ((b.view(np.uint32) + 0x8000) & 0xFFFF0000).view(np.float32)
# To represent them as brain-floats, we need to drop the second half
a_bf16 = np.right_shift(a_f32rounded.view(np.uint32), 16).astype(np.uint16)
b_bf16 = np.right_shift(b_f32rounded.view(np.uint32), 16).astype(np.uint16)
# Now we can compare the results
expected = np.inner(a_f32rounded, b_f32rounded)
result = simd.inner(a_bf16, b_bf16, "bf16")
动态分派
SimSIMD 提供了一种动态分派机制,以选择当前 CPU 的最先进的微内核。您可以查询支持的后端,并使用 SimSIMD::capabilities
函数来选择最佳选项。
println!("uses neon: {}", capabilities::uses_neon());
println!("uses sve: {}", capabilities::uses_sve());
println!("uses haswell: {}", capabilities::uses_haswell());
println!("uses skylake: {}", capabilities::uses_skylake());
println!("uses ice: {}", capabilities::uses_ice());
println!("uses genoa: {}", capabilities::uses_genoa());
println!("uses sapphire: {}", capabilities::uses_sapphire());
在 JavaScript 中使用 SimSIMD
根据您的环境选择以下选项之一进行安装
npm install--保存 simsimd
yarn add simsimd
pnpm add simsimd
bun install simsimd
该软件包附带预构建的二进制文件,但如果您的平台不受支持,您可以通过 npm run build
从源代码构建软件包。除非您使用带有 --ignore-scripts
标志安装软件包或使用 Bun,否则此操作将自动执行。安装后,您将能够调用各种 TypedArray
变体的 SimSIMD 函数
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);
还支持其他数字类型和精度级别。对于双精度浮点数,请使用 Float64Array
const vectorA = new Float64Array([1.0, 2.0, 3.0]);
const vectorB = new Float64Array([4.0, 5.0, 6.0]);
const distance = cosine(vectorA, vectorB);
在进行机器学习和高维向量搜索时,您可能希望将它们量化为 8 位整数。您可能需要将值从 $[-1, 1]$ 范围投影到 $[-127, 127]$ 范围,然后将它们转换为 Int8Array
const quantizedVectorA = new Int8Array(vectorA.map(v => (v * 127)));
const quantizedVectorB = new Int8Array(vectorB.map(v => (v * 127)));
const distance = cosine(quantizedVectorA, quantizedVectorB);
更极端的量化案例是使用二进制向量。您可以映射所有正值为 1
,所有负值和零为 0
,将八个值打包到一个字节中。之后,可以计算汉明和贾卡德距离。
const { toBinary, hamming } = require('simsimd');
const binaryVectorA = toBinary(vectorA);
const binaryVectorB = toBinary(vectorB);
const distance = hamming(binaryVectorA, binaryVectorB);
在 C 中使用 SimSIMD
为了在基于 CMake 的项目中集成,请将以下段添加到您的 CMakeLists.txt
FetchContent_Declare(
simsimd
GIT_REPOSITORY https://github.com/ashvardanian/simsimd.git
GIT_SHALLOW TRUE
)
FetchContent_MakeAvailable(simsimd)
之后,您可以在 C 代码中以多种方式使用 SimSIMD 库。最简单的方法是包含头文件,编译器将自动选择 SimSIMD 将使用的最新 CPU 扩展。
#include <simsimd/simsimd.h>
int main() {
simsimd_f32_t vector_a[1536];
simsimd_f32_t vector_b[1536];
simsimd_metric_punned_t distance_function = simsimd_metric_punned(
simsimd_metric_cos_k, // Metric kind, like the angular cosine distance
simsimd_datatype_f32_k, // Data type, like: f16, f32, f64, i8, b8, and complex variants
simsimd_cap_any_k); // Which CPU capabilities are we allowed to use
simsimd_distance_t distance;
distance_function(vector_a, vector_b, 1536, &distance);
return 0;
}
动态分派
为了避免硬编码后端,您可以依靠 c/lib.c
将所有可能的后端打包到一个二进制文件中,并在运行时选择最新的 CPU 功能。C 库的这个功能称为动态分派,并在 Python、JavaScript 和 Rust 绑定中广泛使用。要测试在运行时机器上可用的 CPU 功能,请使用以下 API
int uses_neon = simsimd_uses_neon();
int uses_sve = simsimd_uses_sve();
int uses_haswell = simsimd_uses_haswell();
int uses_skylake = simsimd_uses_skylake();
int uses_ice = simsimd_uses_ice();
int uses_genoa = simsimd_uses_genoa();
int uses_sapphire = simsimd_uses_sapphire();
simsimd_capability_t capabilities = simsimd_capabilities();
为了区分运行时和编译时分发,定义以下宏:
#define SIMSIMD_DYNAMIC_DISPATCH 1 // or 0
空间距离:余弦距离和欧几里得距离
#include <simsimd/simsimd.h>
int main() {
simsimd_f64_t f64s[1536];
simsimd_f32_t f32s[1536];
simsimd_f16_t f16s[1536];
simsimd_i8_t i8[1536];
simsimd_distance_t distance;
// Cosine distance between two vectors
simsimd_cos_i8(i8s, i8s, 1536, &distance);
simsimd_cos_f16(f16s, f16s, 1536, &distance);
simsimd_cos_f32(f32s, f32s, 1536, &distance);
simsimd_cos_f64(f64s, f64s, 1536, &distance);
// Euclidean distance between two vectors
simsimd_l2sq_i8(i8s, i8s, 1536, &distance);
simsimd_l2sq_f16(f16s, f16s, 1536, &distance);
simsimd_l2sq_f32(f32s, f32s, 1536, &distance);
simsimd_l2sq_f64(f64s, f64s, 1536, &distance);
return 0;
}
点积:内积和复数内积
#include <simsimd/simsimd.h>
int main() {
simsimd_f64_t f64s[1536];
simsimd_f32_t f32s[1536];
simsimd_f16_t f16s[1536];
simsimd_distance_t distance;
// Inner product between two vectors
simsimd_dot_f16(f16s, f16s, 1536, &distance);
simsimd_dot_f32(f32s, f32s, 1536, &distance);
simsimd_dot_f64(f64s, f64s, 1536, &distance);
// Complex inner product between two vectors
simsimd_dot_f16c(f16s, f16s, 1536, &distance);
simsimd_dot_f32c(f32s, f32s, 1536, &distance);
simsimd_dot_f64c(f64s, f64s, 1536, &distance);
// Complex conjugate inner product between two vectors
simsimd_vdot_f16c(f16s, f16s, 1536, &distance);
simsimd_vdot_f32c(f32s, f32s, 1536, &distance);
simsimd_vdot_f64c(f64s, f64s, 1536, &distance);
return 0;
}
二进制距离:汉明距离和贾卡德距离
#include <simsimd/simsimd.h>
int main() {
simsimd_b8_t b8s[1536 / 8]; // 8 bits per word
simsimd_distance_t distance;
// Hamming distance between two vectors
simsimd_hamming_b8(b8s, b8s, 1536 / 8, &distance);
// Jaccard distance between two vectors
simsimd_jaccard_b8(b8s, b8s, 1536 / 8, &distance);
return 0;
}
概率分布:Jensen-Shannon 和 Kullback-Leibler 散度
#include <simsimd/simsimd.h>
int main() {
simsimd_f64_t f64s[1536];
simsimd_f32_t f32s[1536];
simsimd_f16_t f16s[1536];
simsimd_distance_t distance;
// Jensen-Shannon divergence between two vectors
simsimd_js_f16(f16s, f16s, 1536, &distance);
simsimd_js_f32(f32s, f32s, 1536, &distance);
simsimd_js_f64(f64s, f64s, 1536, &distance);
// Kullback-Leibler divergence between two vectors
simsimd_kl_f16(f16s, f16s, 1536, &distance);
simsimd_kl_f32(f32s, f32s, 1536, &distance);
simsimd_kl_f64(f64s, f64s, 1536, &distance);
return 0;
}
半精度浮点数
如果您想使用SimSIMD的_Float16
功能,请确保您的开发环境兼容C 11。对于其他SimSIMD功能,C 99兼容性就足够了。要在导入之前显式禁用半精度支持,请定义以下宏:
#define SIMSIMD_NATIVE_F16 0 // or 1
#define SIMSIMD_NATIVE_BF16 0 // or 1
#include <simsimd/simsimd.h>
特定目标的后端
SimSIMD对所有后端都公开了所有内核,您可以选择最适合当前CPU的最先进内核,而不依赖内置的分发机制。所有函数名遵循相同的模式:simsimd_{function}_{type}_{backend}
。
- 后端可以是
serial
、haswell
、skylake
、ice
、sapphire
、neon
或sve
。 - 类型可以是
f64
、f32
、f16
、f64c
、f32c
、f16c
、i8
或b8
。 - 函数可以是
dot
、vdot
、cos
、l2sq
、hamming
、jaccard
、kl
或js
。
为了避免硬编码后端,您可以使用simsimd_metric_punned_t
来punning函数指针,以及使用simsimd_capabilities
函数在运行时获取可用的后端。
simsimd_dot_f64_sve
simsimd_cos_f64_sve
simsimd_l2sq_f64_sve
simsimd_dot_f64_skylake
simsimd_cos_f64_skylake
simsimd_l2sq_f64_skylake
simsimd_dot_f64_serial
simsimd_cos_f64_serial
simsimd_l2sq_f64_serial
simsimd_js_f64_serial
simsimd_kl_f64_serial
simsimd_dot_f32_sve
simsimd_cos_f32_sve
simsimd_l2sq_f32_sve
simsimd_dot_f32_neon
simsimd_cos_f32_neon
simsimd_l2sq_f32_neon
simsimd_js_f32_neon
simsimd_kl_f32_neon
simsimd_dot_f32_skylake
simsimd_cos_f32_skylake
simsimd_l2sq_f32_skylake
simsimd_js_f32_skylake
simsimd_kl_f32_skylake
simsimd_dot_f32_serial
simsimd_cos_f32_serial
simsimd_l2sq_f32_serial
simsimd_js_f32_serial
simsimd_kl_f32_serial
simsimd_dot_f16_sve
simsimd_cos_f16_sve
simsimd_l2sq_f16_sve
simsimd_dot_f16_neon
simsimd_cos_f16_neon
simsimd_l2sq_f16_neon
simsimd_js_f16_neon
simsimd_kl_f16_neon
simsimd_dot_f16_sapphire
simsimd_cos_f16_sapphire
simsimd_l2sq_f16_sapphire
simsimd_js_f16_sapphire
simsimd_kl_f16_sapphire
simsimd_dot_f16_haswell
simsimd_cos_f16_haswell
simsimd_l2sq_f16_haswell
simsimd_js_f16_haswell
simsimd_kl_f16_haswell
simsimd_dot_f16_serial
simsimd_cos_f16_serial
simsimd_l2sq_f16_serial
simsimd_js_f16_serial
simsimd_kl_f16_serial
simsimd_cos_i8_neon
simsimd_cos_i8_neon
simsimd_l2sq_i8_neon
simsimd_cos_i8_ice
simsimd_cos_i8_ice
simsimd_l2sq_i8_ice
simsimd_cos_i8_haswell
simsimd_cos_i8_haswell
simsimd_l2sq_i8_haswell
simsimd_cos_i8_serial
simsimd_cos_i8_serial
simsimd_l2sq_i8_serial
simsimd_hamming_b8_sve
simsimd_jaccard_b8_sve
simsimd_hamming_b8_neon
simsimd_jaccard_b8_neon
simsimd_hamming_b8_ice
simsimd_jaccard_b8_ice
simsimd_hamming_b8_haswell
simsimd_jaccard_b8_haswell
simsimd_hamming_b8_serial
simsimd_jaccard_b8_serial
simsimd_dot_f32c_sve
simsimd_vdot_f32c_sve
simsimd_dot_f32c_neon
simsimd_vdot_f32c_neon
simsimd_dot_f32c_haswell
simsimd_vdot_f32c_haswell
simsimd_dot_f32c_skylake
simsimd_vdot_f32c_skylake
simsimd_dot_f32c_serial
simsimd_vdot_f32c_serial
simsimd_dot_f64c_sve
simsimd_vdot_f64c_sve
simsimd_dot_f64c_skylake
simsimd_vdot_f64c_skylake
simsimd_dot_f64c_serial
simsimd_vdot_f64c_serial
simsimd_dot_f16c_sve
simsimd_vdot_f16c_sve
simsimd_dot_f16c_neon
simsimd_vdot_f16c_neon
simsimd_dot_f16c_haswell
simsimd_vdot_f16c_haswell
simsimd_dot_f16c_sapphire
simsimd_vdot_f16c_sapphire
simsimd_dot_f16c_serial
simsimd_vdot_f16c_serial
无运行时依赖
~185KB