usearch 2.8.1 → 2.8.2
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/CITATION.cff +1 -1
- package/Cargo.toml +1 -1
- package/README.md +1 -1
- package/conanfile.py +1 -1
- package/csharp/nuget/nuget-package.props +1 -1
- package/include/usearch/index.hpp +1 -1
- package/package.json +1 -1
- package/simsimd/.github/workflows/prerelease.yml +20 -11
- package/simsimd/.github/workflows/release.yml +24 -8
- package/simsimd/CMakeLists.txt +2 -2
- package/simsimd/README.md +1 -1
- package/simsimd/cpp/bench.cxx +11 -6
- package/simsimd/include/simsimd/binary.h +36 -22
- package/simsimd/include/simsimd/probability.h +94 -70
- package/simsimd/include/simsimd/simsimd.h +30 -21
- package/simsimd/include/simsimd/spatial.h +199 -117
- package/simsimd/package-ci.json +10 -0
- package/simsimd/package.json +1 -5
- package/simsimd/python/lib.c +1 -0
- package/simsimd/python/test.py +47 -22
- package/wasmer.toml +1 -1
package/CITATION.cff
CHANGED
package/Cargo.toml
CHANGED
package/README.md
CHANGED
package/conanfile.py
CHANGED
|
@@ -7,7 +7,7 @@ required_conan_version = ">=1.53.0"
|
|
|
7
7
|
class USearchConan(ConanFile):
|
|
8
8
|
|
|
9
9
|
name = "usearch"
|
|
10
|
-
version = '2.8.
|
|
10
|
+
version = '2.8.2'
|
|
11
11
|
license = "Apache-2.0"
|
|
12
12
|
description = "Smaller & Faster Single-File Vector Search Engine from Unum"
|
|
13
13
|
homepage = "https://github.com/unum-cloud/usearch"
|
package/package.json
CHANGED
|
@@ -61,20 +61,29 @@ jobs:
|
|
|
61
61
|
|
|
62
62
|
test_javascript:
|
|
63
63
|
name: Test JavaScript
|
|
64
|
-
runs-on: ubuntu-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
64
|
+
runs-on: ubuntu-22.04
|
|
65
|
+
env:
|
|
66
|
+
CC: gcc-12
|
|
67
|
+
CXX: g++-12
|
|
68
|
+
|
|
69
69
|
steps:
|
|
70
70
|
- uses: actions/checkout@v4
|
|
71
|
+
with:
|
|
72
|
+
ref: "main"
|
|
73
|
+
- run: git submodule update --init --recursive
|
|
74
|
+
|
|
75
|
+
- name: Update compilers
|
|
76
|
+
run: |
|
|
77
|
+
sudo apt update
|
|
78
|
+
sudo apt install -y cmake build-essential libjemalloc-dev libomp-dev gcc-12 g++-12
|
|
79
|
+
|
|
71
80
|
- name: Set up Node.js
|
|
72
81
|
uses: actions/setup-node@v3
|
|
73
82
|
with:
|
|
74
|
-
node-version:
|
|
83
|
+
node-version: 18
|
|
75
84
|
|
|
76
|
-
- name: Build
|
|
77
|
-
run:
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
85
|
+
- name: Build and Test
|
|
86
|
+
run: |
|
|
87
|
+
npm install
|
|
88
|
+
npm ci
|
|
89
|
+
npm test
|
|
@@ -18,18 +18,18 @@ permissions:
|
|
|
18
18
|
jobs:
|
|
19
19
|
versioning:
|
|
20
20
|
name: Semantic Release
|
|
21
|
-
runs-on: ubuntu-
|
|
21
|
+
runs-on: ubuntu-22.04
|
|
22
22
|
steps:
|
|
23
23
|
- uses: actions/checkout@v3
|
|
24
24
|
with:
|
|
25
25
|
persist-credentials: false
|
|
26
26
|
- uses: actions/setup-node@v3
|
|
27
|
-
- run: npm install --save-dev @semantic-release/exec @semantic-release/git conventional-changelog-eslint semantic-release && npx semantic-release
|
|
27
|
+
- run: npm install --save-dev --prefix ./package-ci @semantic-release/exec @semantic-release/git conventional-changelog-eslint semantic-release && npx --prefix ./package-ci semantic-release
|
|
28
28
|
|
|
29
29
|
rebase:
|
|
30
30
|
name: Rebase Dev. Branch
|
|
31
31
|
needs: versioning
|
|
32
|
-
runs-on: ubuntu-
|
|
32
|
+
runs-on: ubuntu-22.04
|
|
33
33
|
steps:
|
|
34
34
|
- name: Checkout the latest code
|
|
35
35
|
uses: actions/checkout@v3
|
|
@@ -109,17 +109,33 @@ jobs:
|
|
|
109
109
|
name: Publish JavaScript
|
|
110
110
|
needs: versioning
|
|
111
111
|
runs-on: ubuntu-22.04
|
|
112
|
+
env:
|
|
113
|
+
CC: gcc-12
|
|
114
|
+
CXX: g++-12
|
|
115
|
+
|
|
112
116
|
steps:
|
|
113
117
|
- uses: actions/checkout@v3
|
|
114
118
|
with:
|
|
115
119
|
ref: "main"
|
|
116
120
|
- run: git submodule update --init --recursive
|
|
117
|
-
|
|
121
|
+
|
|
122
|
+
- name: Update compilers
|
|
123
|
+
run: |
|
|
124
|
+
sudo apt update
|
|
125
|
+
sudo apt install -y cmake build-essential libjemalloc-dev libomp-dev gcc-12 g++-12
|
|
126
|
+
|
|
127
|
+
- name: Set up Node.js
|
|
128
|
+
uses: actions/setup-node@v3
|
|
118
129
|
with:
|
|
119
130
|
node-version: 18
|
|
120
|
-
|
|
121
|
-
-
|
|
122
|
-
|
|
123
|
-
|
|
131
|
+
|
|
132
|
+
- name: Build and Test
|
|
133
|
+
run: |
|
|
134
|
+
npm install
|
|
135
|
+
npm ci
|
|
136
|
+
npm test
|
|
137
|
+
|
|
138
|
+
- name: Publish
|
|
139
|
+
uses: JS-DevTools/npm-publish@v2
|
|
124
140
|
with:
|
|
125
141
|
token: ${{ secrets.NPM_TOKEN }}
|
package/simsimd/CMakeLists.txt
CHANGED
|
@@ -46,9 +46,9 @@ if(CMAKE_CXX_COMPILER_ID MATCHES "^(Apple)?Clang$")
|
|
|
46
46
|
if(NOT APPLE)
|
|
47
47
|
add_compile_options(-march=native)
|
|
48
48
|
endif()
|
|
49
|
-
add_compile_options(-pedantic -ferror-limit=1
|
|
49
|
+
add_compile_options(-pedantic -ferror-limit=1)
|
|
50
50
|
elseif(CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
|
|
51
|
-
add_compile_options(-march=native -pedantic -fmax-errors=1
|
|
51
|
+
add_compile_options(-march=native -pedantic -fmax-errors=1)
|
|
52
52
|
elseif(CMAKE_CXX_COMPILER_ID STREQUAL "IntelLLVM")
|
|
53
53
|
add_compile_options(-w -ferror-limit=1)
|
|
54
54
|
endif()
|
package/simsimd/README.md
CHANGED
|
@@ -34,7 +34,7 @@ Given 1000 embeddings from OpenAI Ada API with 1536 dimensions, running on the A
|
|
|
34
34
|
|
|
35
35
|
### Intel Sapphire Rapids
|
|
36
36
|
|
|
37
|
-
On the Intel Sapphire Rapids platform, SimSIMD was benchmarked against auto-vectorized code using GCC 12. GCC handles single-precision `float
|
|
37
|
+
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.
|
|
38
38
|
|
|
39
39
|
| | GCC 12 `f32` | GCC 12 `f16` | SimSIMD `f16` | `f16` improvement |
|
|
40
40
|
| :-------------- | -----------: | -----------: | ------------: | ----------------: |
|
package/simsimd/cpp/bench.cxx
CHANGED
|
@@ -3,8 +3,8 @@
|
|
|
3
3
|
|
|
4
4
|
#include <benchmark/benchmark.h>
|
|
5
5
|
|
|
6
|
-
#define SIMSIMD_RSQRT sqrtf
|
|
7
|
-
#define SIMSIMD_LOG logf
|
|
6
|
+
#define SIMSIMD_RSQRT(x) (1 / sqrtf(x))
|
|
7
|
+
#define SIMSIMD_LOG(x) (logf(x))
|
|
8
8
|
#include <simsimd/simsimd.h>
|
|
9
9
|
|
|
10
10
|
namespace bm = benchmark;
|
|
@@ -78,7 +78,7 @@ void register_(std::string name, metric_at* distance_func, metric_at* baseline_f
|
|
|
78
78
|
using pair_bytes_t = vectors_pair_gt<scalar_at, 1536 / sizeof(scalar_at)>;
|
|
79
79
|
|
|
80
80
|
std::size_t seconds = 10;
|
|
81
|
-
std::size_t threads = std::thread::hardware_concurrency(); // 1;
|
|
81
|
+
std::size_t threads = 1; // std::thread::hardware_concurrency(); // 1;
|
|
82
82
|
std::string name_dims = name + "_" + std::to_string(pair_dims_t{}.dimensions()) + "d";
|
|
83
83
|
std::string name_bytes = name + "_" + std::to_string(pair_bytes_t{}.size_bytes()) + "b";
|
|
84
84
|
|
|
@@ -95,7 +95,8 @@ int main(int argc, char** argv) {
|
|
|
95
95
|
bool compiled_with_sve = false;
|
|
96
96
|
bool compiled_with_neon = false;
|
|
97
97
|
bool compiled_with_avx2 = false;
|
|
98
|
-
bool
|
|
98
|
+
bool compiled_with_avx512vpopcntdq = false;
|
|
99
|
+
bool compiled_with_avx512vnni = false;
|
|
99
100
|
|
|
100
101
|
#if defined(__ARM_FEATURE_SVE)
|
|
101
102
|
compiled_with_sve = true;
|
|
@@ -107,7 +108,10 @@ int main(int argc, char** argv) {
|
|
|
107
108
|
compiled_with_avx2 = true;
|
|
108
109
|
#endif
|
|
109
110
|
#if defined(__AVX512VPOPCNTDQ__)
|
|
110
|
-
|
|
111
|
+
compiled_with_avx512vpopcntdq = true;
|
|
112
|
+
#endif
|
|
113
|
+
#if defined(__AVX512VNNI__)
|
|
114
|
+
compiled_with_avx512vnni = true;
|
|
111
115
|
#endif
|
|
112
116
|
|
|
113
117
|
// Log supported functionality
|
|
@@ -117,7 +121,8 @@ int main(int argc, char** argv) {
|
|
|
117
121
|
std::printf("- Arm NEON support enabled: %s\n", flags[compiled_with_neon]);
|
|
118
122
|
std::printf("- Arm SVE support enabled: %s\n", flags[compiled_with_sve]);
|
|
119
123
|
std::printf("- x86 AVX2 support enabled: %s\n", flags[compiled_with_avx2]);
|
|
120
|
-
std::printf("- x86 AVX512VPOPCNTDQ support enabled: %s\n", flags[
|
|
124
|
+
std::printf("- x86 AVX512VPOPCNTDQ support enabled: %s\n", flags[compiled_with_avx512vpopcntdq]);
|
|
125
|
+
std::printf("- x86 AVX512VNNI support enabled: %s\n", flags[compiled_with_avx512vnni]);
|
|
121
126
|
std::printf("\n");
|
|
122
127
|
|
|
123
128
|
// Run the benchmarks
|
|
@@ -133,41 +133,55 @@ simsimd_sve_b8_jaccard(simsimd_b8_t const* a, simsimd_b8_t const* b, simsimd_siz
|
|
|
133
133
|
|
|
134
134
|
#if SIMSIMD_TARGET_X86_AVX512
|
|
135
135
|
|
|
136
|
-
__attribute__((target("avx512vpopcntdq,avx512vl,avx512bw,avx512f"))) //
|
|
136
|
+
__attribute__((target("avx512vpopcntdq,avx512vl,avx512bw,avx512f,bmi2"))) //
|
|
137
137
|
inline static simsimd_f32_t
|
|
138
138
|
simsimd_avx512_b8_hamming(simsimd_b8_t const* a, simsimd_b8_t const* b, simsimd_size_t n_words) {
|
|
139
139
|
__m512i differences_vec = _mm512_setzero_si512();
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
140
|
+
__m512i a_vec, b_vec;
|
|
141
|
+
|
|
142
|
+
simsimd_avx512_b8_hamming_cycle:
|
|
143
|
+
if (n_words < 64) {
|
|
144
|
+
__mmask64 mask = _bzhi_u64(0xFFFFFFFFFFFFFFFF, n_words);
|
|
145
|
+
a_vec = _mm512_maskz_loadu_epi8(mask, a);
|
|
146
|
+
b_vec = _mm512_maskz_loadu_epi8(mask, b);
|
|
147
|
+
n_words = 0;
|
|
148
|
+
} else {
|
|
149
|
+
a_vec = _mm512_loadu_epi8(a);
|
|
150
|
+
b_vec = _mm512_loadu_epi8(b);
|
|
151
|
+
a += 64, b += 64, n_words -= 64;
|
|
149
152
|
}
|
|
153
|
+
__m512i xor_vec = _mm512_xor_si512(a_vec, b_vec);
|
|
154
|
+
differences_vec = _mm512_add_epi64(differences_vec, _mm512_popcnt_epi64(xor_vec));
|
|
155
|
+
if (n_words)
|
|
156
|
+
goto simsimd_avx512_b8_hamming_cycle;
|
|
150
157
|
|
|
151
158
|
simsimd_size_t differences = _mm512_reduce_add_epi64(differences_vec);
|
|
152
159
|
return (simsimd_f32_t)differences;
|
|
153
160
|
}
|
|
154
161
|
|
|
155
|
-
__attribute__((target("avx512vpopcntdq,avx512vl,avx512bw,avx512f"))) //
|
|
162
|
+
__attribute__((target("avx512vpopcntdq,avx512vl,avx512bw,avx512f,bmi2"))) //
|
|
156
163
|
inline static simsimd_f32_t
|
|
157
164
|
simsimd_avx512_b8_jaccard(simsimd_b8_t const* a, simsimd_b8_t const* b, simsimd_size_t n_words) {
|
|
158
165
|
__m512i intersection_vec = _mm512_setzero_si512(), union_vec = _mm512_setzero_si512();
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
166
|
+
__m512i a_vec, b_vec;
|
|
167
|
+
|
|
168
|
+
simsimd_avx512_b8_jaccard_cycle:
|
|
169
|
+
if (n_words < 64) {
|
|
170
|
+
__mmask64 mask = _bzhi_u64(0xFFFFFFFFFFFFFFFF, n_words);
|
|
171
|
+
a_vec = _mm512_maskz_loadu_epi8(mask, a);
|
|
172
|
+
b_vec = _mm512_maskz_loadu_epi8(mask, b);
|
|
173
|
+
n_words = 0;
|
|
174
|
+
} else {
|
|
175
|
+
a_vec = _mm512_loadu_epi8(a);
|
|
176
|
+
b_vec = _mm512_loadu_epi8(b);
|
|
177
|
+
a += 64, b += 64, n_words -= 64;
|
|
170
178
|
}
|
|
179
|
+
__m512i and_vec = _mm512_and_si512(a_vec, b_vec);
|
|
180
|
+
__m512i or_vec = _mm512_or_si512(a_vec, b_vec);
|
|
181
|
+
intersection_vec = _mm512_add_epi64(intersection_vec, _mm512_popcnt_epi64(and_vec));
|
|
182
|
+
union_vec = _mm512_add_epi64(union_vec, _mm512_popcnt_epi64(or_vec));
|
|
183
|
+
if (n_words)
|
|
184
|
+
goto simsimd_avx512_b8_jaccard_cycle;
|
|
171
185
|
|
|
172
186
|
simsimd_size_t intersection = _mm512_reduce_add_epi64(intersection_vec),
|
|
173
187
|
union_ = _mm512_reduce_add_epi64(union_vec);
|
|
@@ -370,58 +370,70 @@ simsimd_avx512_f32_log2(__m512 x) {
|
|
|
370
370
|
return _mm512_add_ps(_mm512_mul_ps(p, _mm512_sub_ps(m, one)), e);
|
|
371
371
|
}
|
|
372
372
|
|
|
373
|
-
__attribute__((target("avx512f,avx512vl"))) //
|
|
373
|
+
__attribute__((target("avx512f,avx512vl,bmi2"))) //
|
|
374
374
|
inline static simsimd_f32_t
|
|
375
375
|
simsimd_avx512_f32_kl(simsimd_f32_t const* a, simsimd_f32_t const* b, simsimd_size_t n) {
|
|
376
376
|
__m512 sum_vec = _mm512_set1_ps(0);
|
|
377
377
|
simsimd_f32_t epsilon = 1e-6;
|
|
378
378
|
__m512 epsilon_vec = _mm512_set1_ps(epsilon);
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
379
|
+
__m512 a_vec, b_vec;
|
|
380
|
+
|
|
381
|
+
simsimd_avx512_f32_kl_cycle:
|
|
382
|
+
if (n < 16) {
|
|
383
|
+
__mmask16 mask = _bzhi_u32(0xFFFFFFFF, n);
|
|
384
|
+
a_vec = _mm512_add_ps(_mm512_maskz_loadu_ps(mask, a), epsilon_vec);
|
|
385
|
+
b_vec = _mm512_add_ps(_mm512_maskz_loadu_ps(mask, b), epsilon_vec);
|
|
386
|
+
n = 0;
|
|
387
|
+
} else {
|
|
388
|
+
a_vec = _mm512_add_ps(_mm512_loadu_ps(a), epsilon_vec);
|
|
389
|
+
b_vec = _mm512_add_ps(_mm512_loadu_ps(b), epsilon_vec);
|
|
390
|
+
a += 16, b += 16, n -= 16;
|
|
387
391
|
}
|
|
392
|
+
__m512 ratio_vec = _mm512_div_ps(a_vec, b_vec);
|
|
393
|
+
__m512 log_ratio_vec = simsimd_avx512_f32_log2(ratio_vec);
|
|
394
|
+
__m512 prod_vec = _mm512_mul_ps(a_vec, log_ratio_vec);
|
|
395
|
+
sum_vec = _mm512_add_ps(sum_vec, prod_vec);
|
|
396
|
+
if (n)
|
|
397
|
+
goto simsimd_avx512_f32_kl_cycle;
|
|
398
|
+
|
|
388
399
|
simsimd_f32_t log2_normalizer = 0.693147181f;
|
|
389
400
|
return _mm512_reduce_add_ps(sum_vec) * log2_normalizer;
|
|
390
401
|
}
|
|
391
402
|
|
|
392
|
-
__attribute__((target("avx512f,avx512vl"))) //
|
|
403
|
+
__attribute__((target("avx512f,avx512vl,bmi2"))) //
|
|
393
404
|
inline static simsimd_f32_t
|
|
394
405
|
simsimd_avx512_f32_js(simsimd_f32_t const* a, simsimd_f32_t const* b, simsimd_size_t n) {
|
|
395
406
|
__m512 sum_a_vec = _mm512_set1_ps(0);
|
|
396
407
|
__m512 sum_b_vec = _mm512_set1_ps(0);
|
|
397
408
|
simsimd_f32_t epsilon = 1e-6;
|
|
398
409
|
__m512 epsilon_vec = _mm512_set1_ps(epsilon);
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
// Division is an expensive operation. Instead of doing it twice,
|
|
412
|
-
// we can approximate the reciprocal of `m` and multiply instead.
|
|
413
|
-
__m512 m_recip_approx = _mm512_rcp14_ps(m_vec); // or `_mm512_rcp28_ps` for slightly better precision
|
|
414
|
-
__m512 ratio_a_vec = _mm512_mul_ps(a_vec, m_recip_approx);
|
|
415
|
-
__m512 ratio_b_vec = _mm512_mul_ps(b_vec, m_recip_approx);
|
|
416
|
-
|
|
417
|
-
// The natural logarithm is equivalent to `log2`, multiplied by the `loge(2)`
|
|
418
|
-
__m512 log_ratio_a_vec = simsimd_avx512_f32_log2(ratio_a_vec);
|
|
419
|
-
__m512 log_ratio_b_vec = simsimd_avx512_f32_log2(ratio_b_vec);
|
|
420
|
-
|
|
421
|
-
// Instead of separate multiplication and addition, invoke the FMA
|
|
422
|
-
sum_a_vec = _mm512_maskz_fmadd_ps(nonzero_mask, a_vec, log_ratio_a_vec, sum_a_vec);
|
|
423
|
-
sum_b_vec = _mm512_maskz_fmadd_ps(nonzero_mask, b_vec, log_ratio_b_vec, sum_b_vec);
|
|
410
|
+
__m512 a_vec, b_vec;
|
|
411
|
+
|
|
412
|
+
simsimd_avx512_f32_js_cycle:
|
|
413
|
+
if (n < 16) {
|
|
414
|
+
__mmask16 mask = _bzhi_u32(0xFFFFFFFF, n);
|
|
415
|
+
a_vec = _mm512_maskz_loadu_ps(mask, a);
|
|
416
|
+
b_vec = _mm512_maskz_loadu_ps(mask, b);
|
|
417
|
+
n = 0;
|
|
418
|
+
} else {
|
|
419
|
+
a_vec = _mm512_loadu_ps(a);
|
|
420
|
+
b_vec = _mm512_loadu_ps(b);
|
|
421
|
+
a += 16, b += 16, n -= 16;
|
|
424
422
|
}
|
|
423
|
+
__m512 m_vec = _mm512_mul_ps(_mm512_add_ps(a_vec, b_vec), _mm512_set1_ps(0.5f));
|
|
424
|
+
__mmask16 nonzero_mask_a = _mm512_cmp_ps_mask(a_vec, epsilon_vec, _CMP_GE_OQ);
|
|
425
|
+
__mmask16 nonzero_mask_b = _mm512_cmp_ps_mask(b_vec, epsilon_vec, _CMP_GE_OQ);
|
|
426
|
+
__mmask16 nonzero_mask = nonzero_mask_a & nonzero_mask_b;
|
|
427
|
+
__m512 m_recip_approx = _mm512_rcp14_ps(m_vec);
|
|
428
|
+
__m512 ratio_a_vec = _mm512_mul_ps(a_vec, m_recip_approx);
|
|
429
|
+
__m512 ratio_b_vec = _mm512_mul_ps(b_vec, m_recip_approx);
|
|
430
|
+
__m512 log_ratio_a_vec = simsimd_avx512_f32_log2(ratio_a_vec);
|
|
431
|
+
__m512 log_ratio_b_vec = simsimd_avx512_f32_log2(ratio_b_vec);
|
|
432
|
+
sum_a_vec = _mm512_maskz_fmadd_ps(nonzero_mask, a_vec, log_ratio_a_vec, sum_a_vec);
|
|
433
|
+
sum_b_vec = _mm512_maskz_fmadd_ps(nonzero_mask, b_vec, log_ratio_b_vec, sum_b_vec);
|
|
434
|
+
if (n)
|
|
435
|
+
goto simsimd_avx512_f32_js_cycle;
|
|
436
|
+
|
|
425
437
|
simsimd_f32_t log2_normalizer = 0.693147181f;
|
|
426
438
|
return _mm512_reduce_add_ps(_mm512_add_ps(sum_a_vec, sum_b_vec)) * 0.5f * log2_normalizer;
|
|
427
439
|
}
|
|
@@ -456,56 +468,68 @@ simsimd_avx512_f16_log2(__m512h x) {
|
|
|
456
468
|
return _mm512_add_ph(_mm512_mul_ph(p, _mm512_sub_ph(m, one)), e);
|
|
457
469
|
}
|
|
458
470
|
|
|
459
|
-
__attribute__((target("avx512f,avx512vl,avx512fp16"))) //
|
|
471
|
+
__attribute__((target("avx512f,avx512vl,avx512fp16,bmi2"))) //
|
|
460
472
|
inline static simsimd_f32_t
|
|
461
473
|
simsimd_avx512_f16_kl(simsimd_f16_t const* a, simsimd_f16_t const* b, simsimd_size_t n) {
|
|
462
474
|
__m512h sum_vec = _mm512_set1_ph((_Float16)0);
|
|
463
475
|
__m512h epsilon_vec = _mm512_set1_ph((_Float16)1e-6f);
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
476
|
+
__m512h a_vec, b_vec;
|
|
477
|
+
|
|
478
|
+
simsimd_avx512_f16_kl_cycle:
|
|
479
|
+
if (n < 32) {
|
|
480
|
+
__mmask32 mask = _bzhi_u32(0xFFFFFFFF, n);
|
|
481
|
+
a_vec = _mm512_maskz_add_ph(mask, _mm512_castsi512_ph(_mm512_maskz_loadu_epi16(mask, a)), epsilon_vec);
|
|
482
|
+
b_vec = _mm512_maskz_add_ph(mask, _mm512_castsi512_ph(_mm512_maskz_loadu_epi16(mask, b)), epsilon_vec);
|
|
483
|
+
n = 0;
|
|
484
|
+
} else {
|
|
485
|
+
a_vec = _mm512_add_ph(_mm512_castsi512_ph(_mm512_loadu_epi16(a)), epsilon_vec);
|
|
486
|
+
b_vec = _mm512_add_ph(_mm512_castsi512_ph(_mm512_loadu_epi16(b)), epsilon_vec);
|
|
487
|
+
a += 32, b += 32, n -= 32;
|
|
472
488
|
}
|
|
489
|
+
__m512h ratio_vec = _mm512_div_ph(a_vec, b_vec);
|
|
490
|
+
__m512h log_ratio_vec = simsimd_avx512_f16_log2(ratio_vec);
|
|
491
|
+
__m512h prod_vec = _mm512_mul_ph(a_vec, log_ratio_vec);
|
|
492
|
+
sum_vec = _mm512_add_ph(sum_vec, prod_vec);
|
|
493
|
+
if (n)
|
|
494
|
+
goto simsimd_avx512_f16_kl_cycle;
|
|
495
|
+
|
|
473
496
|
simsimd_f32_t log2_normalizer = 0.693147181f;
|
|
474
497
|
return _mm512_reduce_add_ph(sum_vec) * log2_normalizer;
|
|
475
498
|
}
|
|
476
499
|
|
|
477
|
-
__attribute__((target("avx512f,avx512vl,avx512fp16"))) //
|
|
500
|
+
__attribute__((target("avx512f,avx512vl,avx512fp16,bmi2"))) //
|
|
478
501
|
inline static simsimd_f32_t
|
|
479
502
|
simsimd_avx512_f16_js(simsimd_f16_t const* a, simsimd_f16_t const* b, simsimd_size_t n) {
|
|
480
503
|
__m512h sum_a_vec = _mm512_set1_ph((_Float16)0);
|
|
481
504
|
__m512h sum_b_vec = _mm512_set1_ph((_Float16)0);
|
|
482
505
|
__m512h epsilon_vec = _mm512_set1_ph((_Float16)1e-6f);
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
// Division is an expensive operation. Instead of doing it twice,
|
|
496
|
-
// we can approximate the reciprocal of `m` and multiply instead.
|
|
497
|
-
__m512h m_recip_approx = _mm512_rcp_ph(m_vec);
|
|
498
|
-
__m512h ratio_a_vec = _mm512_mul_ph(a_vec, m_recip_approx);
|
|
499
|
-
__m512h ratio_b_vec = _mm512_mul_ph(b_vec, m_recip_approx);
|
|
500
|
-
|
|
501
|
-
// The natural logarithm is equivalent to `log2`, multiplied by the `loge(2)`
|
|
502
|
-
__m512h log_ratio_a_vec = simsimd_avx512_f16_log2(ratio_a_vec);
|
|
503
|
-
__m512h log_ratio_b_vec = simsimd_avx512_f16_log2(ratio_b_vec);
|
|
504
|
-
|
|
505
|
-
// Instead of separate multiplication and addition, invoke the FMA
|
|
506
|
-
sum_a_vec = _mm512_maskz_fmadd_ph(nonzero_mask, a_vec, log_ratio_a_vec, sum_a_vec);
|
|
507
|
-
sum_b_vec = _mm512_maskz_fmadd_ph(nonzero_mask, b_vec, log_ratio_b_vec, sum_b_vec);
|
|
506
|
+
__m512h a_vec, b_vec;
|
|
507
|
+
|
|
508
|
+
simsimd_avx512_f16_js_cycle:
|
|
509
|
+
if (n < 32) {
|
|
510
|
+
__mmask32 mask = _bzhi_u32(0xFFFFFFFF, n);
|
|
511
|
+
a_vec = _mm512_castsi512_ph(_mm512_maskz_loadu_epi16(mask, a));
|
|
512
|
+
b_vec = _mm512_castsi512_ph(_mm512_maskz_loadu_epi16(mask, b));
|
|
513
|
+
n = 0;
|
|
514
|
+
} else {
|
|
515
|
+
a_vec = _mm512_castsi512_ph(_mm512_loadu_epi16(a));
|
|
516
|
+
b_vec = _mm512_castsi512_ph(_mm512_loadu_epi16(b));
|
|
517
|
+
a += 32, b += 32, n -= 32;
|
|
508
518
|
}
|
|
519
|
+
__m512h m_vec = _mm512_mul_ph(_mm512_add_ph(a_vec, b_vec), _mm512_set1_ph((_Float16)0.5f));
|
|
520
|
+
__mmask32 nonzero_mask_a = _mm512_cmp_ph_mask(a_vec, epsilon_vec, _CMP_GE_OQ);
|
|
521
|
+
__mmask32 nonzero_mask_b = _mm512_cmp_ph_mask(b_vec, epsilon_vec, _CMP_GE_OQ);
|
|
522
|
+
__mmask32 nonzero_mask = nonzero_mask_a & nonzero_mask_b;
|
|
523
|
+
__m512h m_recip_approx = _mm512_rcp_ph(m_vec);
|
|
524
|
+
__m512h ratio_a_vec = _mm512_mul_ph(a_vec, m_recip_approx);
|
|
525
|
+
__m512h ratio_b_vec = _mm512_mul_ph(b_vec, m_recip_approx);
|
|
526
|
+
__m512h log_ratio_a_vec = simsimd_avx512_f16_log2(ratio_a_vec);
|
|
527
|
+
__m512h log_ratio_b_vec = simsimd_avx512_f16_log2(ratio_b_vec);
|
|
528
|
+
sum_a_vec = _mm512_maskz_fmadd_ph(nonzero_mask, a_vec, log_ratio_a_vec, sum_a_vec);
|
|
529
|
+
sum_b_vec = _mm512_maskz_fmadd_ph(nonzero_mask, b_vec, log_ratio_b_vec, sum_b_vec);
|
|
530
|
+
if (n)
|
|
531
|
+
goto simsimd_avx512_f16_js_cycle;
|
|
532
|
+
|
|
509
533
|
simsimd_f32_t log2_normalizer = 0.693147181f;
|
|
510
534
|
return _mm512_reduce_add_ph(_mm512_add_ph(sum_a_vec, sum_b_vec)) * 0.5f * log2_normalizer;
|
|
511
535
|
}
|
|
@@ -74,6 +74,7 @@ typedef enum {
|
|
|
74
74
|
simsimd_cap_x86_avx2fp16_k = 1 << 22, ///< x86 AVX2 with FP16 capability
|
|
75
75
|
simsimd_cap_x86_avx512fp16_k = 1 << 23, ///< x86 AVX512 with FP16 capability
|
|
76
76
|
simsimd_cap_x86_avx512vpopcntdq_k = 1 << 24, ///< x86 AVX512 VPOPCNTDQ instruction capability
|
|
77
|
+
simsimd_cap_x86_avx512vnni_k = 1 << 25, ///< x86 AVX512 VNNI instruction capability
|
|
77
78
|
|
|
78
79
|
} simsimd_capability_t;
|
|
79
80
|
|
|
@@ -112,42 +113,50 @@ inline static simsimd_capability_t simsimd_capabilities() {
|
|
|
112
113
|
/// The states of 4 registers populated for a specific "cpuid" assmebly call
|
|
113
114
|
union four_registers_t {
|
|
114
115
|
int array[4];
|
|
115
|
-
struct {
|
|
116
|
+
struct separate_t {
|
|
116
117
|
unsigned eax, ebx, ecx, edx;
|
|
117
|
-
};
|
|
118
|
+
} named;
|
|
118
119
|
} info1, info7;
|
|
119
120
|
|
|
120
121
|
#ifdef _MSC_VER
|
|
121
122
|
__cpuidex(info1.array, 1, 0);
|
|
122
123
|
__cpuidex(info7.array, 7, 0);
|
|
123
124
|
#else
|
|
124
|
-
__asm__ __volatile__("cpuid"
|
|
125
|
-
|
|
125
|
+
__asm__ __volatile__("cpuid"
|
|
126
|
+
: "=a"(info1.named.eax), "=b"(info1.named.ebx), "=c"(info1.named.ecx), "=d"(info1.named.edx)
|
|
127
|
+
: "a"(1), "c"(0));
|
|
128
|
+
__asm__ __volatile__("cpuid"
|
|
129
|
+
: "=a"(info7.named.eax), "=b"(info7.named.ebx), "=c"(info7.named.ecx), "=d"(info7.named.edx)
|
|
130
|
+
: "a"(7), "c"(0));
|
|
126
131
|
#endif
|
|
127
132
|
|
|
128
133
|
// Check for AVX2 (Function ID 7, EBX register)
|
|
129
134
|
// https://github.com/llvm/llvm-project/blob/50598f0ff44f3a4e75706f8c53f3380fe7faa896/clang/lib/Headers/cpuid.h#L148
|
|
130
|
-
unsigned supports_avx2 = (info7.ebx & 0x00000020) != 0;
|
|
135
|
+
unsigned supports_avx2 = (info7.named.ebx & 0x00000020) != 0;
|
|
131
136
|
// Check for F16C (Function ID 1, ECX register)
|
|
132
137
|
// https://github.com/llvm/llvm-project/blob/50598f0ff44f3a4e75706f8c53f3380fe7faa896/clang/lib/Headers/cpuid.h#L107
|
|
133
|
-
unsigned supports_f16c = (info1.ecx & 0x20000000) != 0;
|
|
138
|
+
unsigned supports_f16c = (info1.named.ecx & 0x20000000) != 0;
|
|
134
139
|
// Check for AVX512F (Function ID 7, EBX register)
|
|
135
140
|
// https://github.com/llvm/llvm-project/blob/50598f0ff44f3a4e75706f8c53f3380fe7faa896/clang/lib/Headers/cpuid.h#L155
|
|
136
|
-
unsigned supports_avx512f = (info7.ebx & 0x00010000) != 0;
|
|
141
|
+
unsigned supports_avx512f = (info7.named.ebx & 0x00010000) != 0;
|
|
137
142
|
// Check for AVX512FP16 (Function ID 7, EDX register)
|
|
138
143
|
// https://github.com/llvm/llvm-project/blob/50598f0ff44f3a4e75706f8c53f3380fe7faa896/clang/lib/Headers/cpuid.h#L198C9-L198C23
|
|
139
|
-
unsigned supports_avx512fp16 = (info7.edx & 0x00800000) != 0;
|
|
144
|
+
unsigned supports_avx512fp16 = (info7.named.edx & 0x00800000) != 0;
|
|
140
145
|
// Check for VPOPCNTDQ (Function ID 1, ECX register)
|
|
141
146
|
// https://github.com/llvm/llvm-project/blob/50598f0ff44f3a4e75706f8c53f3380fe7faa896/clang/lib/Headers/cpuid.h#L182C30-L182C40
|
|
142
|
-
unsigned supports_avx512vpopcntdq = (info1.ecx & 0x00004000) != 0;
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
(
|
|
149
|
-
(
|
|
150
|
-
|
|
147
|
+
unsigned supports_avx512vpopcntdq = (info1.named.ecx & 0x00004000) != 0;
|
|
148
|
+
// Check for VNNI (Function ID 1, ECX register)
|
|
149
|
+
// https://github.com/llvm/llvm-project/blob/50598f0ff44f3a4e75706f8c53f3380fe7faa896/clang/lib/Headers/cpuid.h#L180
|
|
150
|
+
unsigned supports_avx512vnni = (info1.named.ecx & 0x00000800) != 0;
|
|
151
|
+
|
|
152
|
+
return (simsimd_capability_t)( //
|
|
153
|
+
(simsimd_cap_x86_avx2_k * supports_avx2) | //
|
|
154
|
+
(simsimd_cap_x86_avx512_k * supports_avx512f) | //
|
|
155
|
+
(simsimd_cap_x86_avx2fp16_k * (supports_avx2 && supports_f16c)) | //
|
|
156
|
+
(simsimd_cap_x86_avx512fp16_k * (supports_avx512fp16 && supports_avx512f)) | //
|
|
157
|
+
(simsimd_cap_x86_avx512vpopcntdq_k * (supports_avx512vpopcntdq)) | //
|
|
158
|
+
(simsimd_cap_x86_avx512vnni_k * (supports_avx512vnni)) | //
|
|
159
|
+
(simsimd_cap_serial_k));
|
|
151
160
|
|
|
152
161
|
#endif // SIMSIMD_TARGET_X86
|
|
153
162
|
|
|
@@ -319,11 +328,11 @@ inline static void simsimd_find_metric_punned( //
|
|
|
319
328
|
}
|
|
320
329
|
#endif
|
|
321
330
|
#if SIMSIMD_TARGET_X86_AVX512
|
|
322
|
-
if (viable &
|
|
331
|
+
if (viable & simsimd_cap_x86_avx512vnni_k)
|
|
323
332
|
switch (kind) {
|
|
324
|
-
case simsimd_metric_ip_k: *m = (simsimd_metric_punned_t)&simsimd_avx512_i8_ip, *c =
|
|
325
|
-
case simsimd_metric_cos_k: *m = (simsimd_metric_punned_t)&simsimd_avx512_i8_cos, *c =
|
|
326
|
-
case simsimd_metric_l2sq_k: *m = (simsimd_metric_punned_t)&simsimd_avx512_i8_l2sq, *c =
|
|
333
|
+
case simsimd_metric_ip_k: *m = (simsimd_metric_punned_t)&simsimd_avx512_i8_ip, *c = simsimd_cap_x86_avx512vnni_k; return;
|
|
334
|
+
case simsimd_metric_cos_k: *m = (simsimd_metric_punned_t)&simsimd_avx512_i8_cos, *c = simsimd_cap_x86_avx512vnni_k; return;
|
|
335
|
+
case simsimd_metric_l2sq_k: *m = (simsimd_metric_punned_t)&simsimd_avx512_i8_l2sq, *c = simsimd_cap_x86_avx512vnni_k; return;
|
|
327
336
|
default: break;
|
|
328
337
|
}
|
|
329
338
|
#endif
|