usearch 2.10.5 → 2.11.1
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/README.md +54 -15
- package/c/README.md +232 -19
- package/c/lib.cpp +134 -37
- package/c/test.c +4 -0
- package/c/usearch.h +99 -4
- package/cpp/README.md +12 -1
- package/cpp/test.cpp +14 -2
- package/include/usearch/index.hpp +9 -9
- package/include/usearch/index_dense.hpp +38 -15
- package/include/usearch/index_plugins.hpp +89 -27
- package/javascript/dist/cjs/usearch.js +5 -5
- package/javascript/dist/esm/usearch.js +2 -2
- package/javascript/usearch.ts +2 -2
- package/package.json +5 -5
- package/prebuilds/darwin-arm64+x64/usearch.node +0 -0
- package/prebuilds/linux-arm64/usearch.node +0 -0
- package/prebuilds/linux-x64/usearch.node +0 -0
- package/prebuilds/win32-ia32/usearch.node +0 -0
- package/prebuilds/win32-x64/usearch.node +0 -0
package/README.md
CHANGED
|
@@ -94,9 +94,10 @@ USearch is compact and broadly compatible without sacrificing performance, prima
|
|
|
94
94
|
| Supported metrics ² | 9 fixed metrics | any metric | extendible |
|
|
95
95
|
| Supported languages ³ | C++, Python | 10 languages | portable |
|
|
96
96
|
| Supported ID types ⁴ | 32-bit, 64-bit | 32-bit, 40-bit, 64-bit | efficient |
|
|
97
|
-
|
|
|
98
|
-
|
|
|
99
|
-
|
|
|
97
|
+
| Filtering ⁵ | ban-lists | any predicates | composable |
|
|
98
|
+
| Required dependencies ⁶ | BLAS, OpenMP | - | light-weight |
|
|
99
|
+
| Bindings ⁷ | SWIG | Native | low-latency |
|
|
100
|
+
| Python binding size ⁸ | [~ 10 MB][faiss-weight] | [< 1 MB][usearch-weight] | deployable |
|
|
100
101
|
|
|
101
102
|
[sloc]: https://en.wikipedia.org/wiki/Source_lines_of_code
|
|
102
103
|
[faiss-weight]: https://pypi.org/project/faiss-cpu/#files
|
|
@@ -107,9 +108,10 @@ USearch is compact and broadly compatible without sacrificing performance, prima
|
|
|
107
108
|
> ² User-defined metrics allow you to customize your search for various applications, from GIS to creating custom metrics for composite embeddings from multiple AI models or hybrid full-text and semantic search.
|
|
108
109
|
> ³ With USearch, you can reuse the same preconstructed index in various programming languages.
|
|
109
110
|
> ⁴ The 40-bit integer allows you to store 4B+ vectors without allocating 8 bytes for every neighbor reference in the proximity graph.
|
|
110
|
-
> ⁵
|
|
111
|
-
> ⁶
|
|
112
|
-
> ⁷
|
|
111
|
+
> ⁵ With USearch the index can be combined with arbitrary external containers, like Bloom filters or third-party databases, to filter out irrelevant keys during index traversal.
|
|
112
|
+
> ⁶ Lack of obligatory dependencies makes USearch much more portable.
|
|
113
|
+
> ⁷ Native bindings introduce lower call latencies than more straightforward approaches.
|
|
114
|
+
> ⁸ Lighter bindings make downloads and deployments faster.
|
|
113
115
|
|
|
114
116
|
[intel-benchmarks]: https://www.unum.cloud/blog/2023-11-07-scaling-vector-search-with-intel
|
|
115
117
|
|
|
@@ -192,23 +194,59 @@ When compared to FAISS's `IndexFlatL2` in Google Colab, __[USearch may offer up
|
|
|
192
194
|
- `faiss.IndexFlatL2`: __55.3 ms__.
|
|
193
195
|
- `usearch.index.search`: __2.54 ms__.
|
|
194
196
|
|
|
195
|
-
## User-Defined
|
|
197
|
+
## User-Defined Metrics
|
|
196
198
|
|
|
197
|
-
While most vector search packages concentrate on just
|
|
198
|
-
This flexibility allows you to customize your search for various applications, from computing geospatial coordinates with the rare [Haversine][haversine] distance to creating custom metrics for composite embeddings from multiple AI models.
|
|
199
|
+
While most vector search packages concentrate on just two metrics, "Inner Product distance" and "Euclidean distance", USearch allows arbitrary user-defined metrics.
|
|
200
|
+
This flexibility allows you to customize your search for various applications, from computing geospatial coordinates with the rare [Haversine][haversine] distance to creating custom metrics for composite embeddings from multiple AI models, like joint image-text embeddings.
|
|
201
|
+
You can use [Numba][numba], [Cppyy][cppyy], or [PeachPy][peachpy] to define your [custom metric even in Python](https://unum-cloud.github.io/usearch/python#user-defined-metrics-and-jit-in-python):
|
|
199
202
|
|
|
200
|
-
|
|
203
|
+
```py
|
|
204
|
+
from numba import cfunc, types, carray
|
|
205
|
+
from usearch.index import Index, MetricKind, MetricSignature, CompiledMetric
|
|
206
|
+
|
|
207
|
+
@cfunc(types.float32(types.CPointer(types.float32), types.CPointer(types.float32)))
|
|
208
|
+
def python_inner_product(a, b):
|
|
209
|
+
a_array = carray(a, ndim)
|
|
210
|
+
b_array = carray(b, ndim)
|
|
211
|
+
c = 0.0
|
|
212
|
+
for i in range(ndim):
|
|
213
|
+
c += a_array[i] * b_array[i]
|
|
214
|
+
return 1 - c
|
|
215
|
+
|
|
216
|
+
metric = CompiledMetric(pointer=python_inner_product.address, kind=MetricKind.IP, signature=MetricSignature.ArrayArray)
|
|
217
|
+
index = Index(ndim=ndim, metric=metric, dtype=np.float32)
|
|
218
|
+
```
|
|
201
219
|
|
|
202
|
-
|
|
220
|
+
Similar effect is even easier to achieve in C, C++, and Rust interfaces.
|
|
221
|
+
Moreover, unlike older approaches indexing high-dimensional spaces, like KD-Trees and Locality Sensitive Hashing, HNSW doesn't require vectors to be identical in length.
|
|
203
222
|
They only have to be comparable.
|
|
204
|
-
So you can apply it in [obscure][obscure] applications, like searching for similar sets or fuzzy text matching, using [GZip][gzip-similarity] as a distance function.
|
|
205
|
-
|
|
206
|
-
> Read more about [JIT and UDF in USearch Python SDK](https://unum-cloud.github.io/usearch/python#user-defined-metrics-and-jit-in-python).
|
|
223
|
+
So you can apply it in [obscure][obscure] applications, like searching for similar sets or fuzzy text matching, using [GZip][gzip-similarity] compression-ratio as a distance function.
|
|
207
224
|
|
|
208
225
|
[haversine]: https://ashvardanian.com/posts/abusing-vector-search#geo-spatial-indexing
|
|
209
226
|
[obscure]: https://ashvardanian.com/posts/abusing-vector-search
|
|
210
227
|
[gzip-similarity]: https://twitter.com/LukeGessler/status/1679211291292889100?s=20
|
|
211
228
|
|
|
229
|
+
[numba]: https://numba.readthedocs.io/en/stable/reference/jit-compilation.html#c-callbacks
|
|
230
|
+
[cppyy]: https://cppyy.readthedocs.io/en/latest/
|
|
231
|
+
[peachpy]: https://github.com/Maratyszcza/PeachPy
|
|
232
|
+
|
|
233
|
+
## Filtering and Predicate Functions
|
|
234
|
+
|
|
235
|
+
Sometimes you may want to cross-reference search-results against some external database or filter them based on some criteria.
|
|
236
|
+
In most engines, you'd have to manually perform paging requests, successively filtering the results.
|
|
237
|
+
In USearch you can simply pass a predicate function to the search method, which will be applied directly during graph traversal.
|
|
238
|
+
In Rust that would look like this:
|
|
239
|
+
|
|
240
|
+
```rust
|
|
241
|
+
let is_odd = |key: Key| key % 2 == 1;
|
|
242
|
+
let query = vec![0.2, 0.1, 0.2, 0.1, 0.3];
|
|
243
|
+
let results = index.filtered_search(&query, 10, is_odd).unwrap();
|
|
244
|
+
assert!(
|
|
245
|
+
results.keys.iter().all(|&key| key % 2 == 1),
|
|
246
|
+
"All keys must be odd"
|
|
247
|
+
);
|
|
248
|
+
```
|
|
249
|
+
|
|
212
250
|
## Memory Efficiency, Downcasting, and Quantization
|
|
213
251
|
|
|
214
252
|
Training a quantization model and dimension-reduction is a common approach to accelerate vector search.
|
|
@@ -315,6 +353,7 @@ In some cases, like Batch operations, feature parity is meaningless, as the host
|
|
|
315
353
|
| Save, load, view | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
|
|
316
354
|
| User-defined metrics | ✅ | ✅ | ✅ | ❌ | ❌ | ❌ | ❌ | ❌ |
|
|
317
355
|
| Batch operations | ❌ | ✅ | ❌ | ❌ | ✅ | ❌ | ❌ | ❌ |
|
|
356
|
+
| Filter predicates | ✅ | ❌ | ✅ | ❌ | ❌ | ✅ | ❌ | ❌ |
|
|
318
357
|
| Joins | ✅ | ✅ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ |
|
|
319
358
|
| Variable-length vectors | ✅ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ |
|
|
320
359
|
| 4B+ capacities | ✅ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ |
|
|
@@ -432,7 +471,7 @@ doi = {10.5281/zenodo.7949416},
|
|
|
432
471
|
author = {Vardanian, Ash},
|
|
433
472
|
title = {{USearch by Unum Cloud}},
|
|
434
473
|
url = {https://github.com/unum-cloud/usearch},
|
|
435
|
-
version = {2.
|
|
474
|
+
version = {2.11.1},
|
|
436
475
|
year = {2023},
|
|
437
476
|
month = oct,
|
|
438
477
|
}
|
package/c/README.md
CHANGED
|
@@ -2,33 +2,246 @@
|
|
|
2
2
|
|
|
3
3
|
## Installation
|
|
4
4
|
|
|
5
|
-
|
|
6
|
-
|
|
5
|
+
USearch vector-search engine can be integrated into your project using CMake.
|
|
6
|
+
Alternatively, you can download one of the precompiled binaries from the [releases page](https://github.com/unum-cloud/usearch/releases).
|
|
7
7
|
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
8
|
+
## Quickstart
|
|
9
|
+
|
|
10
|
+
```c
|
|
11
|
+
#include <stdio.h> // For printing.
|
|
12
|
+
#include <assert.h> // For assertions!
|
|
13
|
+
#include <usearch/usearch.h> // For the win 🚀
|
|
14
|
+
|
|
15
|
+
int main() {
|
|
16
|
+
|
|
17
|
+
// Construct:
|
|
18
|
+
size_t dimensions = 128;
|
|
19
|
+
usearch_error_t error = NULL;
|
|
20
|
+
usearch_init_options_t opts = {
|
|
21
|
+
.metric_kind = usearch_metric_cos_k,
|
|
22
|
+
.scalar_kind = usearch_scalar_f16_k,
|
|
23
|
+
.dimensions = dimensions,
|
|
24
|
+
.expansion_add = 0, // for defaults
|
|
25
|
+
.expansion_search = 0 // for defaults
|
|
26
|
+
};
|
|
27
|
+
usearch_index_t index = usearch_init(&opts, &error);
|
|
28
|
+
|
|
29
|
+
size_t vectors_count = 1000;
|
|
30
|
+
usearch_reserve(index, vectors_count, &error);
|
|
31
|
+
if (error) goto cleanup;
|
|
32
|
+
|
|
33
|
+
// Populate:
|
|
34
|
+
float vector[dimensions]; // don't forget to fill the vector with data
|
|
35
|
+
usearch_add(index, 42, &vector[0], usearch_scalar_f32_k, &error);
|
|
36
|
+
if (error) goto cleanup;
|
|
37
|
+
|
|
38
|
+
// Check up:
|
|
39
|
+
assert(usearch_size(index, &error) == vectors_count);
|
|
40
|
+
assert(usearch_capacity(index, &error) == vectors_count);
|
|
41
|
+
assert(usearch_contains(index, 42, &error));
|
|
42
|
+
|
|
43
|
+
// Search:
|
|
44
|
+
usearch_key_t found_keys[10];
|
|
45
|
+
usearch_distance_t found_distances[10];
|
|
46
|
+
size_t found_count = usearch_search(
|
|
47
|
+
index, &vector[0], usearch_scalar_f32_k, 10,
|
|
48
|
+
&found_keys[0], &found_distances[0], &error);
|
|
49
|
+
|
|
50
|
+
usearch_free(index, &error);
|
|
51
|
+
|
|
52
|
+
cleanup:
|
|
53
|
+
if (error) fprintf(stderr, "Error: %s\n", error);
|
|
54
|
+
if (index) usearch_free(index, &error);
|
|
55
|
+
return error ? 1 : 0;
|
|
56
|
+
}
|
|
12
57
|
```
|
|
13
58
|
|
|
14
|
-
##
|
|
59
|
+
## Serialization
|
|
60
|
+
|
|
61
|
+
To save and load the index from disk, use the following methods:
|
|
62
|
+
|
|
63
|
+
```c
|
|
64
|
+
usearch_save(index, "index.usearch", &error);
|
|
65
|
+
usearch_load(index, "index.usearch", &error);
|
|
66
|
+
usearch_view(index, "index.usearch", &error);
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
Before saving them do disk, you can determine exactly how much disk space the index will take:
|
|
70
|
+
|
|
71
|
+
```c
|
|
72
|
+
size_t bytes = usearch_serialized_length(index, &error);
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
With a known size, you can serialize the index into an in-memory a buffer:
|
|
76
|
+
|
|
77
|
+
```c
|
|
78
|
+
void* buffer = malloc(bytes);
|
|
79
|
+
usearch_save_buffer(index, buffer, bytes, &error);
|
|
80
|
+
usearch_load_buffer(index, buffer, bytes, &error);
|
|
81
|
+
usearch_view_buffer(index, buffer, bytes, &error);
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
One can also retrieve index metadata from the buffer or from disk:
|
|
15
85
|
|
|
16
86
|
```c
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
87
|
+
usearch_init_options_t opts;
|
|
88
|
+
usearch_metadata("index.usearch", &opts, &error);
|
|
89
|
+
usearch_metadata_buffer(buffer, bytes, &opts, &error);
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
## Metrics
|
|
93
|
+
|
|
94
|
+
USearch comes pre-packaged with SimSIMD, bringing over 100 SIMD-accelerated distance kernels for x86 and ARM architectures.
|
|
95
|
+
That includes:
|
|
96
|
+
|
|
97
|
+
- `usearch_metric_cos_k` - Cosine Similarity metric, defined as `Cos = 1 - sum(a[i] * b[i]) / (sqrt(sum(a[i]^2) * sqrt(sum(b[i]^2)))`.
|
|
98
|
+
- `usearch_metric_ip_k` - Inner Product metric, defined as `IP = 1 - sum(a[i] * b[i])`.
|
|
99
|
+
- `usearch_metric_l2sq_k` - Squared Euclidean Distance metric, defined as `L2 = sum((a[i] - b[i])^2)`.
|
|
100
|
+
- `usearch_metric_haversine_k` - Haversine (Great Circle) Distance metric.
|
|
101
|
+
- `usearch_metric_divergence_k` - Jensen Shannon Divergence metric.
|
|
102
|
+
- `usearch_metric_pearson_k` - Pearson Correlation metric.
|
|
103
|
+
- `usearch_metric_hamming_k` - Bit-level Hamming Distance metric, defined as the number of differing bits.
|
|
104
|
+
- `usearch_metric_tanimoto_k` - Bit-level Tanimoto (Jaccard) metric, defined as the number of intersecting bits divided by the number of union bits.
|
|
105
|
+
- `usearch_metric_sorensen_k` - Bit-level Sorensen metric.
|
|
106
|
+
|
|
107
|
+
### User-Defined Metrics
|
|
108
|
+
|
|
109
|
+
You can also define your own metrics by implementing the `usearch_metric_t` interface:
|
|
110
|
+
|
|
111
|
+
```c
|
|
112
|
+
simsimd_distance_t callback(void const* a, void const* b, void* state) {
|
|
113
|
+
// Your custom metric implementation here
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
void callback_state = NULL;
|
|
117
|
+
usearch_change_metric(index, callback, callback_state, usearch_metric_unknown_k, &error);
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
You can always revert back to one of the native metrics by calling:
|
|
121
|
+
|
|
122
|
+
```c
|
|
123
|
+
usearch_change_metric_kind(index, usearch_metric_cos_k, &error);
|
|
124
|
+
```
|
|
21
125
|
|
|
22
|
-
|
|
23
|
-
usearch_add(index, 42, &vector[0], usearch_scalar_f32_k, &error);
|
|
126
|
+
## Filtering with Predicates
|
|
24
127
|
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
128
|
+
Sometimes you may want to cross-reference search-results against some external database or filter them based on some criteria.
|
|
129
|
+
In most engines, you'd have to manually perform paging requests, successively filtering the results.
|
|
130
|
+
In USearch you can simply pass a predicate function to the search method, which will be applied directly during graph traversal.
|
|
131
|
+
|
|
132
|
+
```c
|
|
133
|
+
int is_odd(usearch_key_t key, void* state) {
|
|
134
|
+
return key % 2;
|
|
135
|
+
}
|
|
28
136
|
|
|
29
137
|
usearch_key_t found_keys[10];
|
|
30
|
-
|
|
31
|
-
|
|
138
|
+
usearch_distance_t found_distances[10];
|
|
139
|
+
usearch_filtered_search(
|
|
140
|
+
index, &query[0], usearch_scalar_f32_k, 10,
|
|
141
|
+
&is_odd, NULL, // no state needed for this callback
|
|
142
|
+
&found_keys[0], &found_distances[0], &error);
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
## Extracting, Updating, and Removing Values
|
|
146
|
+
|
|
147
|
+
It is generally not recommended to use HNSW indexes in case of frequent removals or major distribution shifts.
|
|
148
|
+
For small updates, you can use the following methods:
|
|
149
|
+
|
|
150
|
+
```c
|
|
151
|
+
float recovered_vector[dimensions];
|
|
152
|
+
size_t count_retrieved = usearch_get(index, 42, 1,
|
|
153
|
+
&recovered_vector[0], usearch_scalar_f32_k, &error);
|
|
154
|
+
assert(count_retrieved <= 1);
|
|
155
|
+
|
|
156
|
+
size_t count_renamed = usearch_rename(index, 42, 43, &error);
|
|
157
|
+
size_t count_removed = usearch_remove(index, 43, &error);
|
|
158
|
+
assert(count_renamed == count_removed);
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
USearch can also be used for multi-indicies, where each key may map to multiple vectors.
|
|
162
|
+
That's a common case when implementing semantic search for long documents, where the entire documents can't fit into one input sequence in a transformer-model, and you have to split them into chunks.
|
|
163
|
+
|
|
164
|
+
```c
|
|
165
|
+
float many_vectors[10][dimensions];
|
|
166
|
+
size_t count_retrieved = usearch_get(index, 42, 10,
|
|
167
|
+
&many_vectors[0][0], usearch_scalar_f32_k, &error);
|
|
168
|
+
assert(count_retrieved <= 10);
|
|
169
|
+
```
|
|
170
|
+
|
|
171
|
+
## Exact Search
|
|
172
|
+
|
|
173
|
+
USearch exposes its internal SIMD-accelerated distance functions for exact search.
|
|
174
|
+
To invoke them for a pair of vectors, use the following methods:
|
|
175
|
+
|
|
176
|
+
```c
|
|
177
|
+
float vector_a[dimensions], vector_b[dimensions];
|
|
178
|
+
usearch_distance_t distance = usearch_distance(
|
|
179
|
+
&vector_a[0], &vector_b[0], usearch_scalar_f32_k, dimensions, usearch_metric_cos_k, &error);
|
|
180
|
+
```
|
|
181
|
+
|
|
182
|
+
Alternatively, you can benefir from faster thread-pools and priority queues for parallel exact batch-search.
|
|
32
183
|
|
|
33
|
-
|
|
184
|
+
```c
|
|
185
|
+
size_t threads = 0;
|
|
186
|
+
size_t top_k = 10;
|
|
187
|
+
size_t dataset_count = 1000, queries_count = 10;
|
|
188
|
+
simsimd_f16_t dataset[dataset_count][dimensions];
|
|
189
|
+
simsimd_f16_t queries[queries_count][dimensions];
|
|
190
|
+
|
|
191
|
+
usearch_key_t resulting_keys[queries_count][top_k];
|
|
192
|
+
usearch_distance_t resulting_distances[queries_count][top_k];
|
|
193
|
+
|
|
194
|
+
usearch_exact_search(
|
|
195
|
+
&dataset[0][0], dataset_count, dimensions * sizeof(simsimd_f16_t),
|
|
196
|
+
&queries[0][0], queries_count, dimensions * sizeof(simsimd_f16_t),
|
|
197
|
+
usearch_scalar_f16_k, top_k, threads,
|
|
198
|
+
&resulting_keys[0][0], sizeof(usearch_key_t) * top_k,
|
|
199
|
+
&resulting_distances[0][0], sizeof(usearch_distance_t) * top_k,
|
|
200
|
+
&error);
|
|
201
|
+
```
|
|
202
|
+
|
|
203
|
+
## Concurrency and Parallelism
|
|
204
|
+
|
|
205
|
+
USearch manages a pool of "thread local" data-structures to avoid contention and improve performance.
|
|
206
|
+
By default, as many of these structures are created as there are logical cores on the machine.
|
|
207
|
+
Then, one can construct the index in parallel using many cores using third-party thread-pools or tools like OpenACC and OpenMP.
|
|
208
|
+
|
|
209
|
+
```c
|
|
210
|
+
#pragma omp parallel for
|
|
211
|
+
for (size_t i = 0; i < 1000; i++) {
|
|
212
|
+
usearch_add(index, i, &vector[0], usearch_scalar_f32_k, &error);
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
#pragma omp parallel for
|
|
216
|
+
for (size_t i = 0; i < 1000; i++) {
|
|
217
|
+
usearch_key_t found_keys[10];
|
|
218
|
+
usearch_distance_t found_distances[10];
|
|
219
|
+
size_t found_matches = usearch_search(index,
|
|
220
|
+
&vector[0], usearch_scalar_f32_k, 10,
|
|
221
|
+
&found_keys[0], &found_distances[0], &error);
|
|
222
|
+
}
|
|
34
223
|
```
|
|
224
|
+
|
|
225
|
+
## Performance Tuning
|
|
226
|
+
|
|
227
|
+
To optimize the performance of the index, you can adjust the expansion values used during index creation and search operations.
|
|
228
|
+
Higher expansion values will lead to better search accuracy at the cost of slightly increased memory usage, but potentially much higher search times.
|
|
229
|
+
Following methods are available to adjust the expansion values:
|
|
230
|
+
|
|
231
|
+
```c
|
|
232
|
+
printf("Connectivity: %zu\n", usearch_connectivity(index, &error)); // can't change
|
|
233
|
+
printf("Add expansion: %zu\n", usearch_expansion_add(index, &error)); // can change
|
|
234
|
+
printf("Search expansion: %zu\n", usearch_expansion_search(index, &error)); // can change
|
|
235
|
+
usearch_change_expansion_add(index, 32, &error);
|
|
236
|
+
usearch_change_expansion_search(index, 32, &error);
|
|
237
|
+
```
|
|
238
|
+
|
|
239
|
+
Optimizing hardware utilization, you may want to check the SIMD hardware acceleration capabilities of the index and memory consumption.
|
|
240
|
+
The first will print the codename of the most advanced SIMD instruction set supported by the CPU and used by the index.
|
|
241
|
+
The second will print the memory usage of the index in bytes.
|
|
242
|
+
|
|
243
|
+
```c
|
|
244
|
+
printf("Hardware acceleration: %s\n", usearch_hardware_acceleration(index, &error));
|
|
245
|
+
printf("Memory usage: %zu\n", usearch_memory_usage(index, &error));
|
|
246
|
+
```
|
|
247
|
+
|