usearch 0.2.0 → 0.2.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/.github/workflows/release.yml +12 -1
- package/.vscode/launch.json +5 -4
- package/.vscode/settings.json +2 -1
- package/CMakeLists.txt +11 -6
- package/CMakeListsInstructions.md +39 -17
- package/Cargo.toml +1 -1
- package/Package.swift +40 -0
- package/README.md +24 -11
- package/VERSION +1 -1
- package/build.gradle +5 -1
- package/cmake/jemalloc.cmake +0 -2
- package/conanfile.py +18 -0
- package/include/usearch/usearch.hpp +335 -227
- package/javascript/lib.cpp +8 -2
- package/objc/USearchObjective.mm +150 -0
- package/objc/include/USearchObjective.h +69 -0
- package/package.json +1 -1
- package/pyproject.toml +18 -4
- package/python/lib.cpp +6 -6
- package/python/test.py +4 -5
- package/python/usearch/__init__.py +0 -0
- package/rust/lib.cpp +13 -2
- package/setup.py +8 -3
- package/simsimd/include/simsimd/simsimd.h +15 -0
- package/src/advanced.hpp +217 -117
- package/src/bench.cpp +236 -81
- package/swift/Index+Sugar.swift +70 -0
- package/swift/Test.swift +20 -0
- package/swift/USearch.swift +8 -0
- package/java/gradle/wrapper/gradle-wrapper.jar +0 -0
- package/java/gradle/wrapper/gradle-wrapper.properties +0 -6
- package/java/gradlew +0 -244
package/src/advanced.hpp
CHANGED
|
@@ -1,8 +1,18 @@
|
|
|
1
1
|
#pragma once
|
|
2
|
-
|
|
3
|
-
#
|
|
4
|
-
#
|
|
5
|
-
|
|
2
|
+
|
|
3
|
+
#if defined(WIN32) || defined(_WIN32) || defined(__WIN32__) || defined(__NT__)
|
|
4
|
+
#define _USE_MATH_DEFINES
|
|
5
|
+
|
|
6
|
+
#define WINDOWS
|
|
7
|
+
#endif
|
|
8
|
+
|
|
9
|
+
#include <stdlib.h> // `aligned_alloc`
|
|
10
|
+
|
|
11
|
+
#include <functional> // `std::function`
|
|
12
|
+
#include <numeric> // `std::iota`
|
|
13
|
+
#include <stdexcept> // `std::invalid_argument`
|
|
14
|
+
#include <thread> // `std::thread`
|
|
15
|
+
#include <vector> // `std::vector`
|
|
6
16
|
|
|
7
17
|
#if __linux__
|
|
8
18
|
#include <sys/auxv.h>
|
|
@@ -43,11 +53,15 @@ template <typename at> inline at clamp(at v, at lo, at hi) noexcept {
|
|
|
43
53
|
return usearch::clamp(v, lo, hi, std::less<at>{});
|
|
44
54
|
}
|
|
45
55
|
|
|
46
|
-
class
|
|
47
|
-
class
|
|
56
|
+
class f8_bits_t;
|
|
57
|
+
class f16_bits_t;
|
|
48
58
|
|
|
49
59
|
inline float f16_to_f32(std::uint16_t u16) noexcept {
|
|
50
|
-
#if defined(
|
|
60
|
+
#if defined(__AVX512F__)
|
|
61
|
+
_Float16 f16;
|
|
62
|
+
std::memcpy(&f16, &u16, sizeof(std::uint16_t));
|
|
63
|
+
return float(f16);
|
|
64
|
+
#elif defined(__aarch64__)
|
|
51
65
|
__fp16 f16;
|
|
52
66
|
std::memcpy(&f16, &u16, sizeof(std::uint16_t));
|
|
53
67
|
return float(f16);
|
|
@@ -57,7 +71,12 @@ inline float f16_to_f32(std::uint16_t u16) noexcept {
|
|
|
57
71
|
}
|
|
58
72
|
|
|
59
73
|
inline std::uint16_t f32_to_f16(float f32) noexcept {
|
|
60
|
-
#if defined(
|
|
74
|
+
#if defined(__AVX512F__)
|
|
75
|
+
_Float16 f16 = _Float16(f32);
|
|
76
|
+
std::uint16_t u16;
|
|
77
|
+
std::memcpy(&u16, &f16, sizeof(std::uint16_t));
|
|
78
|
+
return u16;
|
|
79
|
+
#elif defined(__aarch64__)
|
|
61
80
|
__fp16 f16 = __fp16(f32);
|
|
62
81
|
std::uint16_t u16;
|
|
63
82
|
std::memcpy(&u16, &f16, sizeof(std::uint16_t));
|
|
@@ -67,81 +86,89 @@ inline std::uint16_t f32_to_f16(float f32) noexcept {
|
|
|
67
86
|
#endif
|
|
68
87
|
}
|
|
69
88
|
|
|
70
|
-
class
|
|
89
|
+
class f16_bits_t {
|
|
71
90
|
std::uint16_t uint16_{};
|
|
72
91
|
|
|
73
92
|
public:
|
|
74
|
-
inline
|
|
75
|
-
inline
|
|
76
|
-
inline
|
|
77
|
-
inline
|
|
78
|
-
inline
|
|
93
|
+
inline f16_bits_t() noexcept : uint16_(0) {}
|
|
94
|
+
inline f16_bits_t(f16_bits_t&&) = default;
|
|
95
|
+
inline f16_bits_t& operator=(f16_bits_t&&) = default;
|
|
96
|
+
inline f16_bits_t(f16_bits_t const&) = default;
|
|
97
|
+
inline f16_bits_t& operator=(f16_bits_t const&) = default;
|
|
79
98
|
|
|
80
99
|
inline operator float() const noexcept { return f16_to_f32(uint16_); }
|
|
81
100
|
|
|
82
|
-
inline
|
|
83
|
-
inline
|
|
84
|
-
inline
|
|
85
|
-
|
|
86
|
-
inline
|
|
87
|
-
inline
|
|
88
|
-
inline
|
|
89
|
-
inline
|
|
90
|
-
inline
|
|
91
|
-
inline
|
|
92
|
-
inline
|
|
93
|
-
inline
|
|
94
|
-
inline
|
|
95
|
-
inline
|
|
96
|
-
inline
|
|
97
|
-
inline
|
|
98
|
-
|
|
99
|
-
inline
|
|
101
|
+
inline f16_bits_t(f8_bits_t) noexcept;
|
|
102
|
+
inline f16_bits_t(float v) noexcept : uint16_(f32_to_f16(v)) {}
|
|
103
|
+
inline f16_bits_t(double v) noexcept : uint16_(f32_to_f16(v)) {}
|
|
104
|
+
|
|
105
|
+
inline f16_bits_t operator+(f16_bits_t other) const noexcept { return {float(*this) + float(other)}; }
|
|
106
|
+
inline f16_bits_t operator-(f16_bits_t other) const noexcept { return {float(*this) - float(other)}; }
|
|
107
|
+
inline f16_bits_t operator*(f16_bits_t other) const noexcept { return {float(*this) * float(other)}; }
|
|
108
|
+
inline f16_bits_t operator/(f16_bits_t other) const noexcept { return {float(*this) / float(other)}; }
|
|
109
|
+
inline f16_bits_t operator+(float other) const noexcept { return {float(*this) + other}; }
|
|
110
|
+
inline f16_bits_t operator-(float other) const noexcept { return {float(*this) - other}; }
|
|
111
|
+
inline f16_bits_t operator*(float other) const noexcept { return {float(*this) * other}; }
|
|
112
|
+
inline f16_bits_t operator/(float other) const noexcept { return {float(*this) / other}; }
|
|
113
|
+
inline f16_bits_t operator+(double other) const noexcept { return {float(*this) + other}; }
|
|
114
|
+
inline f16_bits_t operator-(double other) const noexcept { return {float(*this) - other}; }
|
|
115
|
+
inline f16_bits_t operator*(double other) const noexcept { return {float(*this) * other}; }
|
|
116
|
+
inline f16_bits_t operator/(double other) const noexcept { return {float(*this) / other}; }
|
|
117
|
+
|
|
118
|
+
inline f16_bits_t& operator+=(float v) noexcept {
|
|
100
119
|
uint16_ = f32_to_f16(v + f16_to_f32(uint16_));
|
|
101
120
|
return *this;
|
|
102
121
|
}
|
|
103
122
|
|
|
104
|
-
inline
|
|
123
|
+
inline f16_bits_t& operator-=(float v) noexcept {
|
|
105
124
|
uint16_ = f32_to_f16(v - f16_to_f32(uint16_));
|
|
106
125
|
return *this;
|
|
107
126
|
}
|
|
108
127
|
|
|
109
|
-
inline
|
|
128
|
+
inline f16_bits_t& operator*=(float v) noexcept {
|
|
110
129
|
uint16_ = f32_to_f16(v * f16_to_f32(uint16_));
|
|
111
130
|
return *this;
|
|
112
131
|
}
|
|
113
132
|
|
|
114
|
-
inline
|
|
133
|
+
inline f16_bits_t& operator/=(float v) noexcept {
|
|
115
134
|
uint16_ = f32_to_f16(v / f16_to_f32(uint16_));
|
|
116
135
|
return *this;
|
|
117
136
|
}
|
|
118
137
|
};
|
|
119
138
|
|
|
120
|
-
|
|
139
|
+
/**
|
|
140
|
+
* @brief Numeric type for uniformly-distributed floating point
|
|
141
|
+
* values within [-1,1] range, quantized to integers [-100,100].
|
|
142
|
+
*/
|
|
143
|
+
class f8_bits_t {
|
|
121
144
|
std::int8_t int8_{};
|
|
122
145
|
|
|
123
146
|
public:
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
inline
|
|
129
|
-
|
|
130
|
-
inline operator
|
|
131
|
-
inline
|
|
132
|
-
inline operator
|
|
133
|
-
|
|
134
|
-
inline operator
|
|
135
|
-
inline operator
|
|
136
|
-
inline operator
|
|
137
|
-
|
|
138
|
-
inline
|
|
139
|
-
|
|
140
|
-
inline
|
|
141
|
-
|
|
147
|
+
constexpr static float divisor_k = 100.f;
|
|
148
|
+
constexpr static std::int8_t min_k = -100;
|
|
149
|
+
constexpr static std::int8_t max_k = 100;
|
|
150
|
+
|
|
151
|
+
inline f8_bits_t() noexcept : int8_(0) {}
|
|
152
|
+
inline f8_bits_t(f8_bits_t&&) = default;
|
|
153
|
+
inline f8_bits_t& operator=(f8_bits_t&&) = default;
|
|
154
|
+
inline f8_bits_t(f8_bits_t const&) = default;
|
|
155
|
+
inline f8_bits_t& operator=(f8_bits_t const&) = default;
|
|
156
|
+
|
|
157
|
+
inline operator float() const noexcept { return float(int8_) / divisor_k; }
|
|
158
|
+
inline operator f16_bits_t() const noexcept { return float(int8_) / divisor_k; }
|
|
159
|
+
inline operator double() const noexcept { return double(int8_) / divisor_k; }
|
|
160
|
+
inline explicit operator std::int8_t() const noexcept { return int8_; }
|
|
161
|
+
inline explicit operator std::int16_t() const noexcept { return int8_; }
|
|
162
|
+
inline explicit operator std::int32_t() const noexcept { return int8_; }
|
|
163
|
+
inline explicit operator std::int64_t() const noexcept { return int8_; }
|
|
164
|
+
|
|
165
|
+
inline f8_bits_t(float v) noexcept
|
|
166
|
+
: int8_(usearch::clamp<std::int8_t>(static_cast<std::int8_t>(v * divisor_k), min_k, max_k)) {}
|
|
167
|
+
inline f8_bits_t(double v) noexcept
|
|
168
|
+
: int8_(usearch::clamp<std::int8_t>(static_cast<std::int8_t>(v * divisor_k), min_k, max_k)) {}
|
|
142
169
|
};
|
|
143
170
|
|
|
144
|
-
inline
|
|
171
|
+
inline f16_bits_t::f16_bits_t(f8_bits_t v) noexcept : f16_bits_t(float(v)) {}
|
|
145
172
|
|
|
146
173
|
struct uuid_t {
|
|
147
174
|
std::uint8_t octets[16];
|
|
@@ -163,7 +190,7 @@ void multithreaded(std::size_t threads, std::size_t tasks, callback_at&& callbac
|
|
|
163
190
|
for (std::size_t thread_idx = 0; thread_idx != threads; ++thread_idx) {
|
|
164
191
|
threads_pool.emplace_back([=]() {
|
|
165
192
|
for (std::size_t task_idx = thread_idx * tasks_per_thread;
|
|
166
|
-
task_idx < std::min(tasks, thread_idx * tasks_per_thread + tasks_per_thread); ++task_idx)
|
|
193
|
+
task_idx < (std::min)(tasks, thread_idx * tasks_per_thread + tasks_per_thread); ++task_idx)
|
|
167
194
|
callback(thread_idx, task_idx);
|
|
168
195
|
});
|
|
169
196
|
}
|
|
@@ -201,7 +228,7 @@ enum class accuracy_t {
|
|
|
201
228
|
f32_k,
|
|
202
229
|
f16_k,
|
|
203
230
|
f64_k,
|
|
204
|
-
|
|
231
|
+
f8_k,
|
|
205
232
|
};
|
|
206
233
|
|
|
207
234
|
inline char const* accuracy_name(accuracy_t accuracy) noexcept {
|
|
@@ -209,7 +236,7 @@ inline char const* accuracy_name(accuracy_t accuracy) noexcept {
|
|
|
209
236
|
case accuracy_t::f32_k: return "f32";
|
|
210
237
|
case accuracy_t::f16_k: return "f16";
|
|
211
238
|
case accuracy_t::f64_k: return "f64";
|
|
212
|
-
case accuracy_t::
|
|
239
|
+
case accuracy_t::f8_k: return "f8";
|
|
213
240
|
default: return "";
|
|
214
241
|
}
|
|
215
242
|
}
|
|
@@ -227,10 +254,10 @@ inline accuracy_t accuracy_from_name(char const* name, std::size_t len) {
|
|
|
227
254
|
accuracy = accuracy_t::f64_k;
|
|
228
255
|
else if (str_equals(name, len, "f16"))
|
|
229
256
|
accuracy = accuracy_t::f16_k;
|
|
230
|
-
else if (str_equals(name, len, "
|
|
231
|
-
accuracy = accuracy_t::
|
|
257
|
+
else if (str_equals(name, len, "f8"))
|
|
258
|
+
accuracy = accuracy_t::f8_k;
|
|
232
259
|
else
|
|
233
|
-
throw std::invalid_argument("Unknown type, choose: f32, f16, f64,
|
|
260
|
+
throw std::invalid_argument("Unknown type, choose: f32, f16, f64, f8");
|
|
234
261
|
return accuracy;
|
|
235
262
|
}
|
|
236
263
|
|
|
@@ -283,7 +310,7 @@ inline std::size_t bytes_per_scalar(accuracy_t accuracy) noexcept {
|
|
|
283
310
|
case accuracy_t::f32_k: return 4;
|
|
284
311
|
case accuracy_t::f16_k: return 2;
|
|
285
312
|
case accuracy_t::f64_k: return 8;
|
|
286
|
-
case accuracy_t::
|
|
313
|
+
case accuracy_t::f8_k: return 1;
|
|
287
314
|
default: return 0;
|
|
288
315
|
}
|
|
289
316
|
}
|
|
@@ -318,26 +345,10 @@ template <> struct cast_gt<f64_t, f64_t> {
|
|
|
318
345
|
bool operator()(byte_t const*, std::size_t, byte_t*) noexcept { return false; }
|
|
319
346
|
};
|
|
320
347
|
|
|
321
|
-
template <> struct cast_gt<
|
|
348
|
+
template <> struct cast_gt<f16_bits_t, f16_bits_t> {
|
|
322
349
|
bool operator()(byte_t const*, std::size_t, byte_t*) noexcept { return false; }
|
|
323
350
|
};
|
|
324
351
|
|
|
325
|
-
struct ip_i8q100_converted_t {
|
|
326
|
-
float operator()(i8q100_converted_t const* a, i8q100_converted_t const* b, std::size_t d) const noexcept {
|
|
327
|
-
std::int64_t ab = 0;
|
|
328
|
-
#if defined(__GNUC__)
|
|
329
|
-
#pragma GCC ivdep
|
|
330
|
-
#elif defined(__clang__)
|
|
331
|
-
#pragma clang loop vectorize(enable)
|
|
332
|
-
#elif defined(_OPENMP)
|
|
333
|
-
#pragma omp simd reduction(+ : ab)
|
|
334
|
-
#endif
|
|
335
|
-
for (std::size_t i = 0; i < d; ++i)
|
|
336
|
-
ab += std::int16_t(a[i]) * std::int16_t(b[i]);
|
|
337
|
-
return 1.f - ab / 10000.f;
|
|
338
|
-
}
|
|
339
|
-
};
|
|
340
|
-
|
|
341
352
|
/**
|
|
342
353
|
* @brief Oversimplified type-punned index for equidimensional floating-point
|
|
343
354
|
* vectors with automatic down-casting and hardware acceleration.
|
|
@@ -368,10 +379,10 @@ class auto_index_gt {
|
|
|
368
379
|
accuracy_t accuracy_ = accuracy_t::f32_k;
|
|
369
380
|
isa_t acceleration_ = isa_t::auto_k;
|
|
370
381
|
|
|
371
|
-
|
|
372
|
-
mutable std::vector<byte_t> cast_buffer_;
|
|
382
|
+
index_t* index_{};
|
|
383
|
+
mutable std::vector<byte_t> cast_buffer_{};
|
|
373
384
|
struct casts_t {
|
|
374
|
-
cast_t
|
|
385
|
+
cast_t from_f8{};
|
|
375
386
|
cast_t from_f16{};
|
|
376
387
|
cast_t from_f32{};
|
|
377
388
|
cast_t from_f64{};
|
|
@@ -380,23 +391,42 @@ class auto_index_gt {
|
|
|
380
391
|
punned_stateful_metric_t root_metric_;
|
|
381
392
|
config_t root_config_;
|
|
382
393
|
|
|
394
|
+
mutable std::vector<std::size_t> available_threads_;
|
|
395
|
+
mutable std::mutex available_threads_mutex_;
|
|
396
|
+
|
|
383
397
|
public:
|
|
384
398
|
auto_index_gt() = default;
|
|
385
|
-
auto_index_gt(auto_index_gt&& other)
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
399
|
+
auto_index_gt(auto_index_gt&& other) { swap(other); }
|
|
400
|
+
auto_index_gt& operator=(auto_index_gt&& other) noexcept {
|
|
401
|
+
swap(other);
|
|
402
|
+
return *this;
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
~auto_index_gt() { aligned_index_free(index_); }
|
|
406
|
+
|
|
407
|
+
void swap(auto_index_gt& other) noexcept {
|
|
408
|
+
std::swap(dimensions_, other.dimensions_);
|
|
409
|
+
std::swap(casted_vector_bytes_, other.casted_vector_bytes_);
|
|
410
|
+
std::swap(accuracy_, other.accuracy_);
|
|
411
|
+
std::swap(acceleration_, other.acceleration_);
|
|
412
|
+
std::swap(index_, other.index_);
|
|
413
|
+
std::swap(cast_buffer_, other.cast_buffer_);
|
|
414
|
+
std::swap(casts_, other.casts_);
|
|
415
|
+
std::swap(root_metric_, other.root_metric_);
|
|
416
|
+
std::swap(root_config_, other.root_config_);
|
|
417
|
+
std::swap(available_threads_, other.available_threads_);
|
|
418
|
+
}
|
|
390
419
|
|
|
391
420
|
std::size_t dimensions() const noexcept { return dimensions_; }
|
|
392
421
|
std::size_t connectivity() const noexcept { return index_->connectivity(); }
|
|
393
422
|
std::size_t size() const noexcept { return index_->size(); }
|
|
394
423
|
std::size_t capacity() const noexcept { return index_->capacity(); }
|
|
424
|
+
config_t const& config() const noexcept { return root_config_; }
|
|
395
425
|
void clear() noexcept { return index_->clear(); }
|
|
396
426
|
|
|
397
427
|
isa_t acceleration() const noexcept { return acceleration_; }
|
|
398
428
|
std::size_t concurrency() const noexcept {
|
|
399
|
-
return std::min(root_config_.max_threads_add, root_config_.max_threads_search);
|
|
429
|
+
return (std::min)(root_config_.max_threads_add, root_config_.max_threads_search);
|
|
400
430
|
}
|
|
401
431
|
|
|
402
432
|
void save(char const* path) const { index_->save(path); }
|
|
@@ -405,13 +435,21 @@ class auto_index_gt {
|
|
|
405
435
|
void reserve(std::size_t capacity) { index_->reserve(capacity); }
|
|
406
436
|
|
|
407
437
|
// clang-format off
|
|
408
|
-
void add(label_t label,
|
|
409
|
-
void add(label_t label, f32_t const* vector
|
|
410
|
-
void add(label_t label, f64_t const* vector
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
438
|
+
void add(label_t label, f16_bits_t const* vector) { return add(label, vector, true, casts_.from_f16); }
|
|
439
|
+
void add(label_t label, f32_t const* vector) { return add(label, vector, true, casts_.from_f32); }
|
|
440
|
+
void add(label_t label, f64_t const* vector) { return add(label, vector, true, casts_.from_f64); }
|
|
441
|
+
|
|
442
|
+
void add(label_t label, f16_bits_t const* vector, std::size_t thread, bool copy) { return add(label, vector, thread, copy, casts_.from_f16); }
|
|
443
|
+
void add(label_t label, f32_t const* vector, std::size_t thread, bool copy) { return add(label, vector, thread, copy, casts_.from_f32); }
|
|
444
|
+
void add(label_t label, f64_t const* vector, std::size_t thread, bool copy) { return add(label, vector, thread, copy, casts_.from_f64); }
|
|
445
|
+
|
|
446
|
+
std::size_t search(f16_bits_t const* vector, std::size_t wanted, label_t* matches, distance_t* distances) const { return search(vector, wanted, matches, distances, casts_.from_f16); }
|
|
447
|
+
std::size_t search(f32_t const* vector, std::size_t wanted, label_t* matches, distance_t* distances) const { return search(vector, wanted, matches, distances, casts_.from_f32); }
|
|
448
|
+
std::size_t search(f64_t const* vector, std::size_t wanted, label_t* matches, distance_t* distances) const { return search(vector, wanted, matches, distances, casts_.from_f64); }
|
|
449
|
+
|
|
450
|
+
std::size_t search(f16_bits_t const* vector, std::size_t wanted, label_t* matches, distance_t* distances, std::size_t thread) const { return search(vector, wanted, matches, distances, thread, casts_.from_f16); }
|
|
451
|
+
std::size_t search(f32_t const* vector, std::size_t wanted, label_t* matches, distance_t* distances, std::size_t thread) const { return search(vector, wanted, matches, distances, thread, casts_.from_f32); }
|
|
452
|
+
std::size_t search(f64_t const* vector, std::size_t wanted, label_t* matches, distance_t* distances, std::size_t thread) const { return search(vector, wanted, matches, distances, thread, casts_.from_f64); }
|
|
415
453
|
|
|
416
454
|
static auto_index_gt ip(std::size_t dimensions, accuracy_t accuracy = accuracy_t::f16_k, config_t config = {}) { return make(dimensions, accuracy, ip_metric(dimensions, accuracy), make_casts(accuracy), config); }
|
|
417
455
|
static auto_index_gt l2(std::size_t dimensions, accuracy_t accuracy = accuracy_t::f16_k, config_t config = {}) { return make(dimensions, accuracy, l2_metric(dimensions, accuracy), make_casts(accuracy), config); }
|
|
@@ -437,12 +475,41 @@ class auto_index_gt {
|
|
|
437
475
|
|
|
438
476
|
result.root_metric_ = root_metric_;
|
|
439
477
|
result.root_config_ = root_config_;
|
|
440
|
-
|
|
478
|
+
index_t* raw = aligned_index_alloc();
|
|
479
|
+
new (raw) index_t(root_config_, root_metric_);
|
|
480
|
+
result.index_ = raw;
|
|
441
481
|
|
|
442
482
|
return result;
|
|
443
483
|
}
|
|
444
484
|
|
|
445
485
|
private:
|
|
486
|
+
static index_t* aligned_index_alloc() noexcept {
|
|
487
|
+
return (index_t*)aligned_alloc(64, 64 * divide_round_up<64>(sizeof(index_t)));
|
|
488
|
+
}
|
|
489
|
+
|
|
490
|
+
static void aligned_index_free(index_t* raw) noexcept { free(raw); }
|
|
491
|
+
|
|
492
|
+
struct thread_lock_t {
|
|
493
|
+
auto_index_gt const& parent;
|
|
494
|
+
std::size_t thread_id;
|
|
495
|
+
|
|
496
|
+
~thread_lock_t() { parent.thread_unlock(thread_id); }
|
|
497
|
+
};
|
|
498
|
+
|
|
499
|
+
thread_lock_t thread_lock() const {
|
|
500
|
+
available_threads_mutex_.lock();
|
|
501
|
+
std::size_t thread_id = available_threads_.back();
|
|
502
|
+
available_threads_.pop_back();
|
|
503
|
+
available_threads_mutex_.unlock();
|
|
504
|
+
return {*this, thread_id};
|
|
505
|
+
}
|
|
506
|
+
|
|
507
|
+
void thread_unlock(std::size_t thread_id) const {
|
|
508
|
+
available_threads_mutex_.lock();
|
|
509
|
+
available_threads_.push_back(thread_id);
|
|
510
|
+
available_threads_mutex_.unlock();
|
|
511
|
+
}
|
|
512
|
+
|
|
446
513
|
template <typename scalar_at>
|
|
447
514
|
void add(label_t label, scalar_at const* vector, std::size_t thread, bool copy, cast_t const& cast) {
|
|
448
515
|
byte_t const* vector_data = reinterpret_cast<byte_t const*>(vector);
|
|
@@ -473,29 +540,54 @@ class auto_index_gt {
|
|
|
473
540
|
return index_->search({vector_data, vector_bytes}, wanted, matches, distances, thread);
|
|
474
541
|
}
|
|
475
542
|
|
|
543
|
+
template <typename scalar_at> void add(label_t label, scalar_at const* vector, bool copy, cast_t const& cast) {
|
|
544
|
+
thread_lock_t lock = thread_lock();
|
|
545
|
+
return add(label, vector, lock.thread_id, copy, cast);
|
|
546
|
+
}
|
|
547
|
+
|
|
548
|
+
template <typename scalar_at>
|
|
549
|
+
std::size_t search( //
|
|
550
|
+
scalar_at const* vector, std::size_t wanted, //
|
|
551
|
+
label_t* matches, distance_t* distances, //
|
|
552
|
+
cast_t const& cast) const {
|
|
553
|
+
thread_lock_t lock = thread_lock();
|
|
554
|
+
return search(vector, wanted, matches, distances, lock.thread_id, cast);
|
|
555
|
+
}
|
|
556
|
+
|
|
476
557
|
static auto_index_gt make( //
|
|
477
558
|
std::size_t dimensions, accuracy_t accuracy, //
|
|
478
559
|
metric_and_meta_t metric_and_meta, casts_t casts, //
|
|
479
560
|
config_t config) {
|
|
480
561
|
|
|
481
|
-
std::size_t
|
|
562
|
+
std::size_t hardware_threads = std::thread::hardware_concurrency();
|
|
563
|
+
config.max_threads_add = config.max_threads_add ? config.max_threads_add : hardware_threads;
|
|
564
|
+
config.max_threads_search = config.max_threads_search ? config.max_threads_search : hardware_threads;
|
|
565
|
+
std::size_t max_threads = (std::max)(config.max_threads_add, config.max_threads_search);
|
|
482
566
|
auto_index_gt result;
|
|
483
567
|
result.dimensions_ = dimensions;
|
|
484
568
|
result.accuracy_ = accuracy;
|
|
485
569
|
result.casted_vector_bytes_ = bytes_per_scalar(accuracy) * dimensions;
|
|
486
570
|
result.cast_buffer_.resize(max_threads * result.casted_vector_bytes_);
|
|
487
571
|
result.casts_ = casts;
|
|
488
|
-
result.index_.reset(new index_t(config, metric_and_meta.metric));
|
|
489
572
|
result.acceleration_ = metric_and_meta.acceleration;
|
|
490
573
|
result.root_metric_ = metric_and_meta.metric;
|
|
491
574
|
result.root_config_ = config;
|
|
575
|
+
|
|
576
|
+
// Fill the thread IDs.
|
|
577
|
+
result.available_threads_.resize(max_threads);
|
|
578
|
+
std::iota(result.available_threads_.begin(), result.available_threads_.end(), 0ul);
|
|
579
|
+
|
|
580
|
+
// Availible since C11, but only C++17, so we use the C version.
|
|
581
|
+
index_t* raw = aligned_index_alloc();
|
|
582
|
+
new (raw) index_t(config, metric_and_meta.metric);
|
|
583
|
+
result.index_ = raw;
|
|
492
584
|
return result;
|
|
493
585
|
}
|
|
494
586
|
|
|
495
587
|
template <typename to_scalar_at> static casts_t make_casts() {
|
|
496
588
|
casts_t result;
|
|
497
|
-
result.
|
|
498
|
-
result.from_f16 = cast_gt<
|
|
589
|
+
result.from_f8 = cast_gt<f8_bits_t, to_scalar_at>{};
|
|
590
|
+
result.from_f16 = cast_gt<f16_bits_t, to_scalar_at>{};
|
|
499
591
|
result.from_f32 = cast_gt<f32_t, to_scalar_at>{};
|
|
500
592
|
result.from_f64 = cast_gt<f64_t, to_scalar_at>{};
|
|
501
593
|
return result;
|
|
@@ -505,8 +597,8 @@ class auto_index_gt {
|
|
|
505
597
|
switch (accuracy) {
|
|
506
598
|
case accuracy_t::f64_k: return make_casts<f64_t>();
|
|
507
599
|
case accuracy_t::f32_k: return make_casts<f32_t>();
|
|
508
|
-
case accuracy_t::f16_k: return make_casts<
|
|
509
|
-
case accuracy_t::
|
|
600
|
+
case accuracy_t::f16_k: return make_casts<f16_bits_t>();
|
|
601
|
+
case accuracy_t::f8_k: return make_casts<f8_bits_t>();
|
|
510
602
|
default: return {};
|
|
511
603
|
}
|
|
512
604
|
}
|
|
@@ -525,7 +617,7 @@ class auto_index_gt {
|
|
|
525
617
|
if (dimensions % 4 == 0)
|
|
526
618
|
return {
|
|
527
619
|
pun_metric<simsimd_f32_t>([](simsimd_f32_t const* a, simsimd_f32_t const* b, size_t d) noexcept {
|
|
528
|
-
return 1 - simsimd_dot_f32x4avx2(a, b, d);
|
|
620
|
+
return 1.f - simsimd_dot_f32x4avx2(a, b, d);
|
|
529
621
|
}),
|
|
530
622
|
isa_t::avx2_k,
|
|
531
623
|
};
|
|
@@ -533,14 +625,14 @@ class auto_index_gt {
|
|
|
533
625
|
if (supports_arm_sve())
|
|
534
626
|
return {
|
|
535
627
|
pun_metric<simsimd_f32_t>([](simsimd_f32_t const* a, simsimd_f32_t const* b, size_t d) noexcept {
|
|
536
|
-
return 1 - simsimd_dot_f32sve(a, b, d);
|
|
628
|
+
return 1.f - simsimd_dot_f32sve(a, b, d);
|
|
537
629
|
}),
|
|
538
630
|
isa_t::sve_k,
|
|
539
631
|
};
|
|
540
632
|
if (dimensions % 4 == 0)
|
|
541
633
|
return {
|
|
542
634
|
pun_metric<simsimd_f32_t>([](simsimd_f32_t const* a, simsimd_f32_t const* b, size_t d) noexcept {
|
|
543
|
-
return 1 - simsimd_dot_f32x4neon(a, b, d);
|
|
635
|
+
return 1.f - simsimd_dot_f32x4neon(a, b, d);
|
|
544
636
|
}),
|
|
545
637
|
isa_t::neon_k,
|
|
546
638
|
};
|
|
@@ -553,31 +645,39 @@ class auto_index_gt {
|
|
|
553
645
|
(void)dimensions;
|
|
554
646
|
#if defined(USEARCH_USE_SIMD)
|
|
555
647
|
#if defined(__x86_64__)
|
|
648
|
+
if (dimensions % 32 == 0)
|
|
649
|
+
return {
|
|
650
|
+
pun_metric<simsimd_f16_t>([](simsimd_f16_t const* a, simsimd_f16_t const* b, size_t d) noexcept {
|
|
651
|
+
return 1.f - simsimd_dot_f16x16avx512(a, b, d);
|
|
652
|
+
}),
|
|
653
|
+
isa_t::avx512_k,
|
|
654
|
+
};
|
|
556
655
|
#elif defined(__aarch64__)
|
|
557
656
|
if (supports_arm_sve())
|
|
558
657
|
return {
|
|
559
658
|
pun_metric<simsimd_f16_t>([](simsimd_f16_t const* a, simsimd_f16_t const* b, size_t d) noexcept {
|
|
560
|
-
return 1 - simsimd_dot_f16sve(a, b, d);
|
|
659
|
+
return 1.f - simsimd_dot_f16sve(a, b, d);
|
|
561
660
|
}),
|
|
562
661
|
isa_t::sve_k,
|
|
563
662
|
};
|
|
564
663
|
if (dimensions % 8 == 0)
|
|
565
664
|
return {
|
|
566
665
|
pun_metric<simsimd_f16_t>([](simsimd_f16_t const* a, simsimd_f16_t const* b, size_t d) noexcept {
|
|
567
|
-
return 1 - simsimd_dot_f16x8neon(a, b, d);
|
|
666
|
+
return 1.f - simsimd_dot_f16x8neon(a, b, d);
|
|
568
667
|
}),
|
|
569
668
|
isa_t::neon_k,
|
|
570
669
|
};
|
|
571
670
|
#endif
|
|
572
671
|
#endif
|
|
573
|
-
return {pun_metric<
|
|
672
|
+
return {pun_metric<f16_bits_t>(ip_gt<f16_bits_t, f32_t>{}), isa_t::auto_k};
|
|
574
673
|
}
|
|
575
674
|
|
|
576
675
|
static metric_and_meta_t ip_metric(std::size_t dimensions, accuracy_t accuracy) {
|
|
577
676
|
switch (accuracy) {
|
|
578
|
-
|
|
677
|
+
// The tow most common numeric types for the most common metric have afew optimized versions:
|
|
579
678
|
case accuracy_t::f16_k: return ip_metric_f16(dimensions);
|
|
580
679
|
case accuracy_t::f32_k: return ip_metric_f32(dimensions);
|
|
680
|
+
case accuracy_t::f8_k: return {pun_metric<f8_bits_t>(ip_gt<f8_bits_t, f32_t>{}), isa_t::auto_k};
|
|
581
681
|
case accuracy_t::f64_k: return {pun_metric<f64_t>(ip_gt<f64_t>{}), isa_t::auto_k};
|
|
582
682
|
default: return {};
|
|
583
683
|
}
|
|
@@ -585,18 +685,18 @@ class auto_index_gt {
|
|
|
585
685
|
|
|
586
686
|
static metric_and_meta_t l2_metric(std::size_t, accuracy_t accuracy) {
|
|
587
687
|
switch (accuracy) {
|
|
588
|
-
case accuracy_t::
|
|
589
|
-
case accuracy_t::f16_k: return {pun_metric<
|
|
590
|
-
case accuracy_t::f32_k: return {pun_metric<f32_t>(
|
|
591
|
-
case accuracy_t::f64_k: return {pun_metric<f64_t>(
|
|
688
|
+
case accuracy_t::f8_k: return {pun_metric<f8_bits_t>(l2sq_gt<f8_bits_t, f32_t>{}), isa_t::auto_k};
|
|
689
|
+
case accuracy_t::f16_k: return {pun_metric<f16_bits_t>(l2sq_gt<f16_bits_t, f32_t>{}), isa_t::auto_k};
|
|
690
|
+
case accuracy_t::f32_k: return {pun_metric<f32_t>(l2sq_gt<f32_t>{}), isa_t::auto_k};
|
|
691
|
+
case accuracy_t::f64_k: return {pun_metric<f64_t>(l2sq_gt<f64_t>{}), isa_t::auto_k};
|
|
592
692
|
default: return {};
|
|
593
693
|
}
|
|
594
694
|
}
|
|
595
695
|
|
|
596
696
|
static metric_and_meta_t cos_metric(std::size_t, accuracy_t accuracy) {
|
|
597
697
|
switch (accuracy) {
|
|
598
|
-
case accuracy_t::
|
|
599
|
-
case accuracy_t::f16_k: return {pun_metric<
|
|
698
|
+
case accuracy_t::f8_k: return {pun_metric<f8_bits_t>(cos_gt<f8_bits_t, f32_t>{}), isa_t::auto_k};
|
|
699
|
+
case accuracy_t::f16_k: return {pun_metric<f16_bits_t>(cos_gt<f16_bits_t, f32_t>{}), isa_t::auto_k};
|
|
600
700
|
case accuracy_t::f32_k: return {pun_metric<f32_t>(cos_gt<f32_t>{}), isa_t::auto_k};
|
|
601
701
|
case accuracy_t::f64_k: return {pun_metric<f64_t>(cos_gt<f64_t>{}), isa_t::auto_k};
|
|
602
702
|
default: return {};
|
|
@@ -605,8 +705,8 @@ class auto_index_gt {
|
|
|
605
705
|
|
|
606
706
|
static metric_and_meta_t haversine_metric(accuracy_t accuracy) {
|
|
607
707
|
switch (accuracy) {
|
|
608
|
-
case accuracy_t::
|
|
609
|
-
case accuracy_t::f16_k: return {pun_metric<
|
|
708
|
+
case accuracy_t::f8_k: return {pun_metric<f8_bits_t>(haversine_gt<f8_bits_t, f32_t>{}), isa_t::auto_k};
|
|
709
|
+
case accuracy_t::f16_k: return {pun_metric<f16_bits_t>(haversine_gt<f16_bits_t, f32_t>{}), isa_t::auto_k};
|
|
610
710
|
case accuracy_t::f32_k: return {pun_metric<f32_t>(haversine_gt<f32_t>{}), isa_t::auto_k};
|
|
611
711
|
case accuracy_t::f64_k: return {pun_metric<f64_t>(haversine_gt<f64_t>{}), isa_t::auto_k};
|
|
612
712
|
default: return {};
|