usearch 0.2.2 → 0.2.3
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 +1 -1
- package/Cargo.toml +1 -1
- package/README.md +59 -26
- package/VERSION +1 -1
- package/conanfile.py +3 -2
- package/package.json +1 -1
- package/python/bench.py +67 -25
- package/python/lib.cpp +28 -5
- package/setup.py +11 -3
- package/src/advanced.hpp +13 -2
- package/src/bench.cpp +1 -1
package/Cargo.toml
CHANGED
package/README.md
CHANGED
|
@@ -17,30 +17,33 @@ Vector Search Engine<br/>
|
|
|
17
17
|
<a href="https://github.com/unum-cloud/usearch"><img height="25" src="https://github.com/unum-cloud/.github/raw/main/assets/github.svg" alt="GitHub"></a>
|
|
18
18
|
</p>
|
|
19
19
|
|
|
20
|
+
<p align="center">
|
|
21
|
+
Euclidean • Angular • Jaccard • Hamming • Haversine • User-Defined Metrics
|
|
22
|
+
<br/>
|
|
23
|
+
<a href="#c++">C++11</a> •
|
|
24
|
+
<a href="#python">Python</a> •
|
|
25
|
+
<a href="#javascript">JavaScript</a> •
|
|
26
|
+
<a href="#java">Java</a> •
|
|
27
|
+
<a href="#rust">Rust</a> •
|
|
28
|
+
<a href="#objective-c">Objective-C</a> •
|
|
29
|
+
<a href="#swift">Swift</a> •
|
|
30
|
+
<a href="#golang">GoLang</a> •
|
|
31
|
+
<a href="#wolfram">Wolfram</a>
|
|
32
|
+
<br/>
|
|
33
|
+
Linux • MacOS • Windows
|
|
34
|
+
</p>
|
|
35
|
+
|
|
20
36
|
- [x] [Single C++11 header][usearch-header] implementation, easily extendible.
|
|
21
37
|
- [x] [4B+](#go-beyond-4b-entries) sized space efficient point-clouds with `uint40_t`.
|
|
22
|
-
- [x] [Half-precision](#quantize-on-the-fly) support with [`
|
|
38
|
+
- [x] [Half-precision](#quantize-on-the-fly) support with [`fp16`](https://github.com/maratyszcza/fp16).
|
|
23
39
|
- [x] [View from disk](#view-larger-indexes-from-disk), without loading into RAM.
|
|
24
|
-
- [x] [
|
|
25
|
-
- [x] Euclidean, Dot-product, Cosine,
|
|
26
|
-
- [x] Jaccard, Hamming, Haversine.
|
|
27
|
-
- [x] Hardware-accelerated [`ashvardanian/simsimd`](https://github.com/ashvardanian/simsimd).
|
|
40
|
+
- [x] [User-defined](#define-custom-metrics) and hardware-accelerated metrics with [`simsimd`](https://github.com/ashvardanian/simsimd).
|
|
28
41
|
- [x] Variable dimensionality vectors.
|
|
29
42
|
- [x] Don't copy vectors if not needed.
|
|
30
43
|
- [x] [Bring your threads](#bring-your-threads), like OpenMP.
|
|
31
44
|
- [x] Multiple vectors per label.
|
|
32
|
-
- [x] [Python](#python) bindings: `pip install usearch`.
|
|
33
|
-
- [x] [JavaScript](#javascript) bindings: `npm install usearch`.
|
|
34
|
-
- [x] [Rust](#rust) bindings: `cargo add usearch`.
|
|
35
|
-
- [x] [Java](#java) bindings: `cloud.unum:usearch` on GitHub.
|
|
36
|
-
- [ ] GoLang bindings.
|
|
37
|
-
- [x] Wolfram language bindings.
|
|
38
|
-
- [x] For Linux: GCC, Clang.
|
|
39
|
-
- [x] For MacOS: Apple Clang.
|
|
40
|
-
- [ ] For Windows.
|
|
41
|
-
- [ ] Multi-index lookups in Python.
|
|
42
45
|
- [ ] Thread-safe `reserve`.
|
|
43
|
-
- [
|
|
46
|
+
- [x] Compatible with Linux, MacOS, and Windows.
|
|
44
47
|
- [x] AI + Vector Search = Semantic Search.
|
|
45
48
|
|
|
46
49
|
[usearch-header]: https://github.com/unum-cloud/usearch/blob/main/include/usearch/usearch.hpp
|
|
@@ -209,12 +212,14 @@ struct custom_metric_t {
|
|
|
209
212
|
|
|
210
213
|
### Python
|
|
211
214
|
|
|
215
|
+
```sh
|
|
216
|
+
pip install usearch
|
|
217
|
+
```
|
|
218
|
+
|
|
212
219
|
Python bindings are implemented with [`pybind/pybind11`](https://github.com/pybind/pybind11).
|
|
213
220
|
Assuming the presence of Global Interpreter Lock in Python, on large insertions we spawn threads in the C++ layer.
|
|
214
221
|
|
|
215
222
|
```python
|
|
216
|
-
$ pip install usearch
|
|
217
|
-
|
|
218
223
|
import numpy as np
|
|
219
224
|
from usearch.index import Index
|
|
220
225
|
|
|
@@ -232,6 +237,8 @@ index.add(42, vector)
|
|
|
232
237
|
matches, distances, count = index.search(vector, 10)
|
|
233
238
|
```
|
|
234
239
|
|
|
240
|
+
#### Batch Operations
|
|
241
|
+
|
|
235
242
|
Same can be done with batches, rather than single entries.
|
|
236
243
|
Under the hood, worker threads will be spawned, to parallelize the procedure.
|
|
237
244
|
|
|
@@ -240,17 +247,31 @@ n = 100
|
|
|
240
247
|
labels = np.array(range(n), dtype=np.longlong)
|
|
241
248
|
vectors = np.random.uniform(0, 0.3, (n, index.ndim)).astype(np.float32)
|
|
242
249
|
|
|
243
|
-
|
|
244
|
-
# Handy when build 1B+ indexes of memory-mapped files
|
|
245
|
-
index.add(labels, vectors, copy=True)
|
|
250
|
+
index.add(labels, vectors)
|
|
246
251
|
matches, distances, counts = index.search(vectors, 10)
|
|
247
252
|
```
|
|
248
253
|
|
|
254
|
+
#### Performance
|
|
255
|
+
|
|
256
|
+
To maximize performance, you can:
|
|
257
|
+
|
|
258
|
+
- adjust the number of threads passed to `index.add(threads=)`.
|
|
259
|
+
- downcast to half-precision with `Index(accuracy=)`.
|
|
260
|
+
- JIT-compile your metrics for hardware and pass to `Index(matric_pointer=)`.
|
|
261
|
+
|
|
262
|
+
For a dataset with 1 Million 256-dimensional embeddings at single precision, the performance would be:
|
|
263
|
+
|
|
264
|
+
- FAISS: 77'563 vectors/s.
|
|
265
|
+
- USearch precompiled for old hardware: 53'198 vectors/s (-32%).
|
|
266
|
+
- USearch with Numba JIT for target hardware: 88'658 vectors/s (+14%).
|
|
267
|
+
|
|
249
268
|
### JavaScript
|
|
250
269
|
|
|
251
|
-
```
|
|
252
|
-
|
|
270
|
+
```sh
|
|
271
|
+
npm install usearch
|
|
272
|
+
```
|
|
253
273
|
|
|
274
|
+
```js
|
|
254
275
|
var index = new usearch.Index({ metric: 'cos', connectivity: 16, dimensions: 2 })
|
|
255
276
|
assert.equal(index.connectivity(), 16)
|
|
256
277
|
assert.equal(index.dimensions(), 2)
|
|
@@ -266,12 +287,14 @@ assert.deepEqual(results.distances, new Float32Array([45, 130]))
|
|
|
266
287
|
|
|
267
288
|
### Rust
|
|
268
289
|
|
|
290
|
+
```sh
|
|
291
|
+
cargo add usearch
|
|
292
|
+
```
|
|
293
|
+
|
|
269
294
|
Being a systems-programming language, Rust has better control over memory management and concurrency, but lacks function overloading.
|
|
270
295
|
Aside from the `add` and `search`, it also provides `add_in_thread` and `search_in_thread` which let users identify the calling thread to use underlying temporary memory more efficiently.
|
|
271
296
|
|
|
272
297
|
```rust
|
|
273
|
-
// cargo add usearch
|
|
274
|
-
|
|
275
298
|
let quant: &str = "f16";
|
|
276
299
|
let index = new_ip(5, &quant, 0, 0, 0).unwrap();
|
|
277
300
|
|
|
@@ -305,6 +328,16 @@ assert!(new_haversine(&quant, 0, 0, 0).is_ok());
|
|
|
305
328
|
|
|
306
329
|
### Java
|
|
307
330
|
|
|
331
|
+
```xml
|
|
332
|
+
<dependency>
|
|
333
|
+
<groupId>cloud.unum.usearch</groupId>
|
|
334
|
+
<artifactId>usearch</artifactId>
|
|
335
|
+
<version>0.2.3</version>
|
|
336
|
+
</dependency>
|
|
337
|
+
```
|
|
338
|
+
|
|
339
|
+
Add that snippet to your `pom.xml` and hit `mvn install`.
|
|
340
|
+
|
|
308
341
|
```java
|
|
309
342
|
Index index = new Index.Config().metric("cos").dimensions(2).build();
|
|
310
343
|
float vec[] = {10, 20};
|
|
@@ -312,7 +345,7 @@ index.add(42, vec);
|
|
|
312
345
|
int[] labels = index.search(vec, 5);
|
|
313
346
|
```
|
|
314
347
|
|
|
315
|
-
###
|
|
348
|
+
### Swift
|
|
316
349
|
|
|
317
350
|
```swift
|
|
318
351
|
let index = Index.l2(dimensions: 4, connectivity: 8)
|
package/VERSION
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
0.2.
|
|
1
|
+
0.2.3
|
package/conanfile.py
CHANGED
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
from conans import ConanFile
|
|
2
2
|
|
|
3
|
+
|
|
3
4
|
class USearchConan(ConanFile):
|
|
4
|
-
|
|
5
|
+
|
|
5
6
|
name = 'USearch'
|
|
6
|
-
version = '0.2.
|
|
7
|
+
version = '0.2.3'
|
|
7
8
|
license = 'Apache 2.0'
|
|
8
9
|
url = 'https://github.com/unum-cloud/usearch'
|
|
9
10
|
description = 'Smaller & Faster Single-File Vector Search Engine from Unum'
|
package/package.json
CHANGED
package/python/bench.py
CHANGED
|
@@ -1,25 +1,28 @@
|
|
|
1
|
-
import
|
|
1
|
+
import time
|
|
2
2
|
import numpy as np
|
|
3
3
|
import fire
|
|
4
4
|
|
|
5
|
-
import faiss
|
|
6
|
-
import usearch
|
|
7
|
-
|
|
8
|
-
import struct
|
|
9
5
|
import numpy as np
|
|
6
|
+
from numba import cfunc, types, carray
|
|
7
|
+
|
|
8
|
+
from faiss import IndexHNSWFlat, IndexIVFPQ
|
|
9
|
+
from usearch.index import Index
|
|
10
|
+
from usearch.io import load_matrix
|
|
11
|
+
|
|
10
12
|
|
|
11
13
|
def measure(f) -> float:
|
|
12
|
-
a =
|
|
14
|
+
a = time.time_ns()
|
|
13
15
|
f()
|
|
14
|
-
b =
|
|
16
|
+
b = time.time_ns()
|
|
15
17
|
c = b - a
|
|
16
|
-
|
|
17
|
-
|
|
18
|
+
secs = c / (10 ** 9)
|
|
19
|
+
print(f'- Took: {secs:.2f} seconds')
|
|
20
|
+
return secs
|
|
18
21
|
|
|
19
22
|
|
|
20
23
|
def bench_faiss(index, vectors: np.array, queries: np.array, neighbors: np.array):
|
|
21
24
|
dt = measure(lambda: index.add(vectors))
|
|
22
|
-
print(f'-
|
|
25
|
+
print(f'- Performance: {vectors.shape[0]/dt:.2f} vectors/s')
|
|
23
26
|
|
|
24
27
|
|
|
25
28
|
def bench_usearch(index, vectors: np.array, queries: np.array, neighbors: np.array):
|
|
@@ -27,7 +30,26 @@ def bench_usearch(index, vectors: np.array, queries: np.array, neighbors: np.arr
|
|
|
27
30
|
assert len(index) == 0
|
|
28
31
|
dt = measure(lambda: index.add(labels, vectors))
|
|
29
32
|
assert len(index) == vectors.shape[0]
|
|
30
|
-
print(f'-
|
|
33
|
+
print(f'- Performance: {vectors.shape[0]/dt:.2f} vectors/s')
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
# Showcases how to use Numba to JIT-compile similarity measures for USearch.
|
|
37
|
+
# https://numba.readthedocs.io/en/stable/reference/jit-compilation.html#c-callbacks
|
|
38
|
+
|
|
39
|
+
signature_f32 = types.float32(
|
|
40
|
+
types.CPointer(types.float32),
|
|
41
|
+
types.CPointer(types.float32),
|
|
42
|
+
types.size_t, types.size_t)
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
@cfunc(signature_f32)
|
|
46
|
+
def inner_product_f32(a, b, n, m):
|
|
47
|
+
a_array = carray(a, n)
|
|
48
|
+
b_array = carray(b, n)
|
|
49
|
+
c = types.float32(0)
|
|
50
|
+
for i in range(n):
|
|
51
|
+
c += a_array[i] * b_array[i]
|
|
52
|
+
return types.float32(1 - c)
|
|
31
53
|
|
|
32
54
|
|
|
33
55
|
def main(
|
|
@@ -44,31 +66,51 @@ def main(
|
|
|
44
66
|
neighbors_mat = load_matrix(neighbors)
|
|
45
67
|
dim = vectors_mat.shape[1]
|
|
46
68
|
|
|
47
|
-
|
|
48
|
-
index =
|
|
69
|
+
print('USearch: HNSW')
|
|
70
|
+
index = Index(
|
|
71
|
+
ndim=dim,
|
|
72
|
+
expansion_add=expansion_add,
|
|
73
|
+
expansion_search=expansion_search,
|
|
74
|
+
connectivity=connectivity,
|
|
75
|
+
)
|
|
76
|
+
bench_usearch(index, vectors_mat, queries_mat, neighbors_mat)
|
|
77
|
+
|
|
78
|
+
print('USearch: HNSW + Numba JIT')
|
|
79
|
+
index = Index(
|
|
80
|
+
ndim=dim,
|
|
81
|
+
expansion_add=expansion_add,
|
|
82
|
+
expansion_search=expansion_search,
|
|
83
|
+
connectivity=connectivity,
|
|
84
|
+
metric_pointer=inner_product_f32.address,
|
|
85
|
+
)
|
|
86
|
+
bench_usearch(index, vectors_mat, queries_mat, neighbors_mat)
|
|
87
|
+
|
|
88
|
+
print('USearch: HNSW + Numba JIT + Half Precision')
|
|
89
|
+
index = Index(
|
|
90
|
+
ndim=dim,
|
|
91
|
+
dtype='f16',
|
|
92
|
+
expansion_add=expansion_add,
|
|
93
|
+
expansion_search=expansion_search,
|
|
94
|
+
connectivity=connectivity,
|
|
95
|
+
)
|
|
96
|
+
bench_usearch(index, vectors_mat, queries_mat, neighbors_mat)
|
|
97
|
+
|
|
98
|
+
print('FAISS: HNSW')
|
|
99
|
+
index = IndexHNSWFlat(dim, connectivity)
|
|
49
100
|
index.hnsw.efSearch = expansion_search
|
|
50
101
|
index.hnsw.efConstruction = expansion_add
|
|
51
102
|
bench_faiss(index, vectors_mat, queries_mat, neighbors_mat)
|
|
52
103
|
|
|
53
|
-
|
|
104
|
+
print('FAISS: HNSW + IVFPQ')
|
|
54
105
|
nlist = 10000 # Number of inverted lists (number of partitions or cells).
|
|
55
106
|
nsegment = 16 # Number of segments for PQ (number of subquantizers).
|
|
56
107
|
nbit = 8 # Number of bits to encode each segment.
|
|
57
|
-
coarse_quantizer =
|
|
58
|
-
index =
|
|
108
|
+
coarse_quantizer = IndexHNSWFlat(dim, connectivity)
|
|
109
|
+
index = IndexIVFPQ(coarse_quantizer, dim, nlist, nsegment, nbit)
|
|
59
110
|
index.train(vectors_mat)
|
|
60
111
|
index.nprobe = 10
|
|
61
112
|
bench_faiss(index, vectors_mat, queries_mat, neighbors_mat)
|
|
62
113
|
|
|
63
|
-
# Test USearch `Index`
|
|
64
|
-
index = usearch.Index(
|
|
65
|
-
ndim=dim,
|
|
66
|
-
expansion_add=expansion_add,
|
|
67
|
-
expansion_search=expansion_search,
|
|
68
|
-
connectivity=connectivity,
|
|
69
|
-
)
|
|
70
|
-
bench_usearch(index, vectors_mat, queries_mat, neighbors_mat)
|
|
71
|
-
|
|
72
114
|
|
|
73
115
|
if __name__ == '__main__':
|
|
74
116
|
fire.Fire(main)
|
package/python/lib.cpp
CHANGED
|
@@ -59,6 +59,25 @@ struct hash_index_w_meta_t : public hash_index_t {
|
|
|
59
59
|
span_gt<hash_word_t const> buffer() const noexcept { return {buffer_.data(), buffer_.size()}; }
|
|
60
60
|
};
|
|
61
61
|
|
|
62
|
+
template <typename scalar_at> punned_stateful_metric_t udf(std::size_t metric_uintptr) {
|
|
63
|
+
return [metric_uintptr](byte_t const* a_bytes, byte_t const* b_bytes, std::size_t n, std::size_t m) -> distance_t {
|
|
64
|
+
using metric_t = punned_distance_t (*)(scalar_at const*, scalar_at const*, std::size_t, std::size_t);
|
|
65
|
+
metric_t metric_ptr = reinterpret_cast<metric_t>(metric_uintptr);
|
|
66
|
+
scalar_at const* a = reinterpret_cast<scalar_at const*>(a_bytes);
|
|
67
|
+
scalar_at const* b = reinterpret_cast<scalar_at const*>(b_bytes);
|
|
68
|
+
return metric_ptr(a, b, n / sizeof(scalar_at), m / sizeof(scalar_at));
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
punned_stateful_metric_t udf(std::size_t metric_uintptr, accuracy_t accuracy) {
|
|
73
|
+
switch (accuracy) {
|
|
74
|
+
case accuracy_t::f8_k: return udf<f8_bits_t>(metric_uintptr);
|
|
75
|
+
case accuracy_t::f16_k: return udf<f16_bits_t>(metric_uintptr);
|
|
76
|
+
case accuracy_t::f32_k: return udf<f32_t>(metric_uintptr);
|
|
77
|
+
case accuracy_t::f64_k: return udf<f64_t>(metric_uintptr);
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
|
|
62
81
|
static native_index_t make_index( //
|
|
63
82
|
std::size_t dimensions, //
|
|
64
83
|
std::size_t capacity, //
|
|
@@ -79,9 +98,8 @@ static native_index_t make_index( //
|
|
|
79
98
|
config.max_threads_search = std::thread::hardware_concurrency();
|
|
80
99
|
|
|
81
100
|
accuracy_t accuracy = accuracy_from_name(scalar_type.c_str(), scalar_type.size());
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
return native_index_t::udf(dimensions, metric_ptr, accuracy, config);
|
|
101
|
+
if (metric_uintptr)
|
|
102
|
+
return native_index_t::udf(dimensions, udf(metric_uintptr, accuracy), accuracy, config);
|
|
85
103
|
else
|
|
86
104
|
return index_from_name<native_index_t>(metric.c_str(), metric.size(), dimensions, accuracy, config);
|
|
87
105
|
}
|
|
@@ -146,7 +164,9 @@ static void add_one_to_index(native_index_t& index, label_t label, py::buffer ve
|
|
|
146
164
|
throw std::invalid_argument("Incompatible scalars in the vector!");
|
|
147
165
|
}
|
|
148
166
|
|
|
149
|
-
static void add_many_to_index(
|
|
167
|
+
static void add_many_to_index( //
|
|
168
|
+
native_index_t& index, py::buffer labels, py::buffer vectors, //
|
|
169
|
+
bool copy, std::size_t threads = 0) {
|
|
150
170
|
|
|
151
171
|
py::buffer_info labels_info = labels.request();
|
|
152
172
|
py::buffer_info vectors_info = vectors.request();
|
|
@@ -393,7 +413,8 @@ PYBIND11_MODULE(index, m) {
|
|
|
393
413
|
py::arg("labels"), //
|
|
394
414
|
py::arg("vectors"), //
|
|
395
415
|
py::kw_only(), //
|
|
396
|
-
py::arg("copy") = true
|
|
416
|
+
py::arg("copy") = true, //
|
|
417
|
+
py::arg("threads") = 0 //
|
|
397
418
|
);
|
|
398
419
|
|
|
399
420
|
i.def( //
|
|
@@ -415,6 +436,8 @@ PYBIND11_MODULE(index, m) {
|
|
|
415
436
|
i.def_property_readonly("ndim", &native_index_t::dimensions);
|
|
416
437
|
i.def_property_readonly("connectivity", &native_index_t::connectivity);
|
|
417
438
|
i.def_property_readonly("capacity", &native_index_t::capacity);
|
|
439
|
+
i.def_property_readonly( //
|
|
440
|
+
"dtype", [](native_index_t const& index) -> std::string { return accuracy_name(index.accuracy()); });
|
|
418
441
|
|
|
419
442
|
i.def("save", &save_index<native_index_t>, py::arg("path"));
|
|
420
443
|
i.def("load", &load_index<native_index_t>, py::arg("path"));
|
package/setup.py
CHANGED
|
@@ -1,13 +1,20 @@
|
|
|
1
1
|
import os
|
|
2
2
|
import sys
|
|
3
|
-
from setuptools import setup
|
|
3
|
+
from setuptools import setup
|
|
4
4
|
from pybind11.setup_helpers import Pybind11Extension
|
|
5
5
|
|
|
6
6
|
|
|
7
7
|
compile_args = ['-std=c++11', '-O3']
|
|
8
|
+
link_args = []
|
|
9
|
+
|
|
10
|
+
if sys.platform == 'linux':
|
|
11
|
+
compile_args.append('-fopenmp')
|
|
12
|
+
link_args.append('-lgomp')
|
|
8
13
|
|
|
9
14
|
if sys.platform == 'darwin':
|
|
10
|
-
|
|
15
|
+
# MacOS 10.15 orhigher is needed for `aligned_alloc` support.
|
|
16
|
+
# https://github.com/unum-cloud/usearch/actions/runs/4975434891/jobs/8902603392
|
|
17
|
+
compile_args.append('-mmacosx-version-min=10.15')
|
|
11
18
|
|
|
12
19
|
if sys.platform == 'win32':
|
|
13
20
|
compile_args = ['/std:c++14', '/O2']
|
|
@@ -17,7 +24,8 @@ ext_modules = [
|
|
|
17
24
|
Pybind11Extension(
|
|
18
25
|
'usearch.index',
|
|
19
26
|
['python/lib.cpp'],
|
|
20
|
-
extra_compile_args=compile_args
|
|
27
|
+
extra_compile_args=compile_args,
|
|
28
|
+
extra_link_args=link_args,
|
|
21
29
|
),
|
|
22
30
|
]
|
|
23
31
|
|
package/src/advanced.hpp
CHANGED
|
@@ -14,6 +14,10 @@
|
|
|
14
14
|
#include <thread> // `std::thread`
|
|
15
15
|
#include <vector> // `std::vector`
|
|
16
16
|
|
|
17
|
+
#if defined(_OPENMP)
|
|
18
|
+
#include <omp.h> // `omp_get_num_threads()`
|
|
19
|
+
#endif
|
|
20
|
+
|
|
17
21
|
#if __linux__
|
|
18
22
|
#include <sys/auxv.h>
|
|
19
23
|
#endif
|
|
@@ -179,12 +183,18 @@ void multithreaded(std::size_t threads, std::size_t tasks, callback_at&& callbac
|
|
|
179
183
|
|
|
180
184
|
if (threads == 0)
|
|
181
185
|
threads = std::thread::hardware_concurrency();
|
|
182
|
-
if (threads == 1) {
|
|
186
|
+
if (threads == 1 || tasks <= 128) {
|
|
183
187
|
for (std::size_t task_idx = 0; task_idx < tasks; ++task_idx)
|
|
184
188
|
callback(0, task_idx);
|
|
185
189
|
return;
|
|
186
190
|
}
|
|
187
191
|
|
|
192
|
+
#if defined(_OPENMP)
|
|
193
|
+
omp_set_num_threads(threads);
|
|
194
|
+
#pragma omp parallel for schedule(dynamic)
|
|
195
|
+
for (std::size_t i = 0; i < tasks; ++i)
|
|
196
|
+
callback(omp_get_thread_num(), i);
|
|
197
|
+
#else
|
|
188
198
|
std::vector<std::thread> threads_pool;
|
|
189
199
|
std::size_t tasks_per_thread = (tasks / threads) + ((tasks % threads) != 0);
|
|
190
200
|
for (std::size_t thread_idx = 0; thread_idx != threads; ++thread_idx) {
|
|
@@ -194,9 +204,9 @@ void multithreaded(std::size_t threads, std::size_t tasks, callback_at&& callbac
|
|
|
194
204
|
callback(thread_idx, task_idx);
|
|
195
205
|
});
|
|
196
206
|
}
|
|
197
|
-
|
|
198
207
|
for (std::size_t thread_idx = 0; thread_idx != threads; ++thread_idx)
|
|
199
208
|
threads_pool[thread_idx].join();
|
|
209
|
+
#endif
|
|
200
210
|
}
|
|
201
211
|
|
|
202
212
|
/**
|
|
@@ -424,6 +434,7 @@ class auto_index_gt {
|
|
|
424
434
|
config_t const& config() const noexcept { return root_config_; }
|
|
425
435
|
void clear() noexcept { return index_->clear(); }
|
|
426
436
|
|
|
437
|
+
accuracy_t accuracy() const noexcept { return accuracy_; }
|
|
427
438
|
isa_t acceleration() const noexcept { return acceleration_; }
|
|
428
439
|
std::size_t concurrency() const noexcept {
|
|
429
440
|
return (std::min)(root_config_.max_threads_add, root_config_.max_threads_search);
|