usearch 2.5.0 → 2.6.0
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 +4 -3
- package/.vscode/settings.json +1 -0
- package/CITATION.cff +1 -1
- package/Cargo.toml +1 -1
- package/README.md +1 -1
- package/conanfile.py +1 -1
- package/cpp/CMakeLists.txt +4 -0
- package/csharp/nuget/nuget-package.props +1 -1
- package/include/usearch/index.hpp +1 -1
- package/include/usearch/index_dense.hpp +12 -5
- package/include/usearch/index_plugins.hpp +3 -7
- package/package.json +1 -1
- package/python/lib.cpp +51 -28
- package/python/scripts/bench_exact.py +44 -0
- package/python/scripts/test_tooling.py +6 -5
- package/python/usearch/index.py +13 -8
- package/setup.py +8 -2
- package/wasmer.toml +1 -1
|
@@ -591,8 +591,10 @@ jobs:
|
|
|
591
591
|
GITHUB_PACKAGES_SOURCE: https://nuget.pkg.github.com/${{ github.repository_owner }}/index.json
|
|
592
592
|
steps:
|
|
593
593
|
- uses: actions/checkout@v3
|
|
594
|
+
with:
|
|
595
|
+
ref: 'main'
|
|
594
596
|
- run: git submodule update --init --recursive
|
|
595
|
-
|
|
597
|
+
|
|
596
598
|
- name: Download usearch libs artifact
|
|
597
599
|
uses: actions/download-artifact@v3
|
|
598
600
|
with:
|
|
@@ -606,8 +608,7 @@ jobs:
|
|
|
606
608
|
|
|
607
609
|
- name: Pack project
|
|
608
610
|
run: |
|
|
609
|
-
|
|
610
|
-
dotnet pack "${{ env.SOLUTION }}" -c Release -p:Version="$VERSION_CONTENT" --output ${{ env.NUGET_PACKAGES }}
|
|
611
|
+
dotnet pack "${{ env.SOLUTION }}" -c Release --output ${{ env.NUGET_PACKAGES }}
|
|
611
612
|
working-directory: ${{ github.workspace }}
|
|
612
613
|
|
|
613
614
|
- name: Publish to NuGet
|
package/.vscode/settings.json
CHANGED
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.
|
|
10
|
+
version = '2.6.0'
|
|
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/cpp/CMakeLists.txt
CHANGED
|
@@ -20,6 +20,8 @@ if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
|
|
|
20
20
|
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -march=native")
|
|
21
21
|
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fmax-errors=1")
|
|
22
22
|
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -pedantic")
|
|
23
|
+
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fdiagnostics-color=always")
|
|
24
|
+
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -ftime-report")
|
|
23
25
|
|
|
24
26
|
if(${USEARCH_USE_OPENMP})
|
|
25
27
|
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fopenmp")
|
|
@@ -32,6 +34,8 @@ elseif(CMAKE_CXX_COMPILER_ID MATCHES "Clang")
|
|
|
32
34
|
set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -fsanitize=address -fsanitize=leak -fsanitize=alignment -fsanitize=undefined")
|
|
33
35
|
set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -Wfatal-errors")
|
|
34
36
|
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -pedantic")
|
|
37
|
+
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fcolor-diagnostics")
|
|
38
|
+
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -ftime-trace")
|
|
35
39
|
|
|
36
40
|
if(${USEARCH_USE_OPENMP})
|
|
37
41
|
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fopenmp")
|
|
@@ -1,15 +1,18 @@
|
|
|
1
1
|
#pragma once
|
|
2
2
|
#include <stdlib.h> // `aligned_alloc`
|
|
3
3
|
|
|
4
|
-
#include <functional>
|
|
5
|
-
#include <numeric>
|
|
6
|
-
#include <
|
|
7
|
-
#include <
|
|
8
|
-
#include <vector> // `std::vector`
|
|
4
|
+
#include <functional> // `std::function`
|
|
5
|
+
#include <numeric> // `std::iota`
|
|
6
|
+
#include <thread> // `std::thread`
|
|
7
|
+
#include <vector> // `std::vector`
|
|
9
8
|
|
|
10
9
|
#include <usearch/index.hpp>
|
|
11
10
|
#include <usearch/index_plugins.hpp>
|
|
12
11
|
|
|
12
|
+
#if defined(USEARCH_DEFINED_CPP17)
|
|
13
|
+
#include <shared_mutex> // `std::shared_mutex`
|
|
14
|
+
#endif
|
|
15
|
+
|
|
13
16
|
namespace unum {
|
|
14
17
|
namespace usearch {
|
|
15
18
|
|
|
@@ -383,7 +386,11 @@ class index_dense_gt {
|
|
|
383
386
|
/// @brief Mutex, controlling concurrent access to `available_threads_`.
|
|
384
387
|
mutable std::mutex available_threads_mutex_;
|
|
385
388
|
|
|
389
|
+
#if defined(USEARCH_DEFINED_CPP17)
|
|
390
|
+
using shared_mutex_t = std::shared_mutex;
|
|
391
|
+
#else
|
|
386
392
|
using shared_mutex_t = unfair_shared_mutex_t;
|
|
393
|
+
#endif
|
|
387
394
|
using shared_lock_t = shared_lock_gt<shared_mutex_t>;
|
|
388
395
|
using unique_lock_t = std::unique_lock<shared_mutex_t>;
|
|
389
396
|
|
|
@@ -42,14 +42,10 @@
|
|
|
42
42
|
|
|
43
43
|
#if USEARCH_USE_SIMSIMD
|
|
44
44
|
#if defined(USEARCH_DEFINED_LINUX)
|
|
45
|
-
#define SIMSIMD_TARGET_X86_AVX2 1
|
|
46
|
-
#define SIMSIMD_TARGET_X86_AVX512 1
|
|
47
|
-
#define SIMSIMD_TARGET_ARM_NEON 1
|
|
48
|
-
#define SIMSIMD_TARGET_ARM_SVE 1
|
|
49
45
|
#include <simsimd/simsimd.h>
|
|
50
46
|
#elif defined(USEARCH_DEFINED_APPLE)
|
|
51
|
-
#define
|
|
52
|
-
#define
|
|
47
|
+
#define SIMSIMD_TARGET_X86_AVX512 0
|
|
48
|
+
#define SIMSIMD_TARGET_ARM_SVE 0
|
|
53
49
|
#include <simsimd/simsimd.h>
|
|
54
50
|
#endif
|
|
55
51
|
#endif
|
|
@@ -1606,7 +1602,7 @@ class flat_hash_multi_set_gt {
|
|
|
1606
1602
|
auto slot_pointer = bucket_pointer + sizeof(bucket_header_t) + sizeof(element_t) * in_bucket_index;
|
|
1607
1603
|
return {
|
|
1608
1604
|
*reinterpret_cast<bucket_header_t*>(bucket_pointer),
|
|
1609
|
-
std::uint64_t(1ull) << in_bucket_index,
|
|
1605
|
+
static_cast<std::uint64_t>(1ull) << in_bucket_index,
|
|
1610
1606
|
*reinterpret_cast<element_t*>(slot_pointer),
|
|
1611
1607
|
};
|
|
1612
1608
|
}
|
package/package.json
CHANGED
package/python/lib.cpp
CHANGED
|
@@ -526,55 +526,76 @@ static void search_typed_brute_force( //
|
|
|
526
526
|
byte_t const* dataset_data = reinterpret_cast<byte_t const*>(dataset_info.ptr);
|
|
527
527
|
byte_t const* queries_data = reinterpret_cast<byte_t const*>(queries_info.ptr);
|
|
528
528
|
for (std::size_t query_idx = 0; query_idx != queries_count; ++query_idx)
|
|
529
|
-
counts_py1d(query_idx) =
|
|
529
|
+
counts_py1d(query_idx) = wanted;
|
|
530
530
|
|
|
531
531
|
if (!threads)
|
|
532
532
|
threads = std::thread::hardware_concurrency();
|
|
533
533
|
|
|
534
534
|
std::size_t tasks_count = static_cast<std::size_t>(dataset_count * queries_count);
|
|
535
|
-
|
|
536
|
-
if (!query_mutexes)
|
|
537
|
-
throw std::bad_alloc();
|
|
535
|
+
executor_default_t executor{threads};
|
|
538
536
|
|
|
539
537
|
// Progress status
|
|
540
538
|
progress_t progress_{progress};
|
|
541
539
|
std::atomic<std::size_t> processed{0};
|
|
542
540
|
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
541
|
+
struct dense_key_and_distance_t {
|
|
542
|
+
u32_t offset;
|
|
543
|
+
f32_t distance;
|
|
544
|
+
};
|
|
545
|
+
auto less = [](dense_key_and_distance_t const& a, dense_key_and_distance_t const& b) {
|
|
546
|
+
return a.distance < b.distance;
|
|
547
|
+
};
|
|
548
|
+
|
|
549
|
+
// Allocate temporary memory to store the distance matrix
|
|
550
|
+
// Previous version didn't need temporary memory, but the performance was much lower.
|
|
551
|
+
// In the new design we keep two buffers - original and transposed, as in-place transpositions
|
|
552
|
+
// of non-rectangular matrixes is expensive.
|
|
553
|
+
std::vector<dense_key_and_distance_t> keys_and_distances(tasks_count * 2);
|
|
554
|
+
dense_key_and_distance_t* keys_and_distances_per_dataset = keys_and_distances.data();
|
|
555
|
+
dense_key_and_distance_t* keys_and_distances_per_query = keys_and_distances_per_dataset + tasks_count;
|
|
556
|
+
executor.dynamic(dataset_count, [&](std::size_t thread_idx, std::size_t dataset_idx) {
|
|
548
557
|
byte_t const* dataset = dataset_data + dataset_idx * dataset_info.strides[0];
|
|
558
|
+
for (std::size_t query_idx = 0; query_idx != queries_count; ++query_idx) {
|
|
549
559
|
byte_t const* query = queries_data + query_idx * queries_info.strides[0];
|
|
550
560
|
distance_t distance = metric(dataset, query);
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
dense_key_t* keys = &keys_py2d(query_idx, 0);
|
|
555
|
-
distance_t* distances = &distances_py2d(query_idx, 0);
|
|
556
|
-
std::size_t& matches = reinterpret_cast<std::size_t&>(counts_py1d(query_idx));
|
|
557
|
-
if (matches == wanted)
|
|
558
|
-
if (distances[wanted - 1] <= distance)
|
|
559
|
-
return true;
|
|
560
|
-
|
|
561
|
-
std::size_t offset = std::lower_bound(distances, distances + matches, distance) - distances;
|
|
562
|
-
|
|
563
|
-
std::size_t count_worse = matches - offset - (wanted == matches);
|
|
564
|
-
std::memmove(keys + offset + 1, keys + offset, count_worse * sizeof(dense_key_t));
|
|
565
|
-
std::memmove(distances + offset + 1, distances + offset, count_worse * sizeof(distance_t));
|
|
566
|
-
keys[offset] = static_cast<dense_key_t>(dataset_idx);
|
|
567
|
-
distances[offset] = distance;
|
|
568
|
-
matches += matches != wanted;
|
|
561
|
+
std::size_t task_idx = dataset_idx * queries_count + query_idx;
|
|
562
|
+
keys_and_distances_per_dataset[task_idx].offset = static_cast<u32_t>(dataset_idx);
|
|
563
|
+
keys_and_distances_per_dataset[task_idx].distance = static_cast<f32_t>(distance);
|
|
569
564
|
}
|
|
570
565
|
|
|
571
566
|
// We don't want to check for signals from multiple threads
|
|
572
|
-
|
|
567
|
+
processed += queries_count;
|
|
573
568
|
if (thread_idx == 0)
|
|
574
569
|
if (PyErr_CheckSignals() != 0 || !progress_(processed.load(), tasks_count))
|
|
575
570
|
return false;
|
|
576
571
|
return true;
|
|
577
572
|
});
|
|
573
|
+
if (processed.load() != tasks_count)
|
|
574
|
+
return;
|
|
575
|
+
|
|
576
|
+
// Transpose in a single thread to avoid contention writing into the same memory buffers
|
|
577
|
+
for (std::size_t query_idx = 0; query_idx != queries_count; ++query_idx) {
|
|
578
|
+
for (std::size_t dataset_idx = 0; dataset_idx != dataset_count; ++dataset_idx) {
|
|
579
|
+
std::size_t from_idx = queries_count * dataset_idx + query_idx;
|
|
580
|
+
std::size_t to_idx = dataset_count * query_idx + dataset_idx;
|
|
581
|
+
keys_and_distances_per_query[to_idx] = keys_and_distances_per_dataset[from_idx];
|
|
582
|
+
}
|
|
583
|
+
}
|
|
584
|
+
|
|
585
|
+
// Partial-sort every query result
|
|
586
|
+
executor.fixed(queries_count, [&](std::size_t, std::size_t query_idx) {
|
|
587
|
+
dense_key_t* keys = &keys_py2d(query_idx, 0);
|
|
588
|
+
distance_t* distances = &distances_py2d(query_idx, 0);
|
|
589
|
+
auto start = keys_and_distances_per_query + dataset_count * query_idx;
|
|
590
|
+
if (wanted > 1) {
|
|
591
|
+
std::partial_sort(start, start + wanted, start + dataset_count, less);
|
|
592
|
+
for (std::size_t i = 0; i != wanted; ++i)
|
|
593
|
+
keys[i] = static_cast<dense_key_t>(start[i].offset), distances[i] = start[i].distance;
|
|
594
|
+
} else {
|
|
595
|
+
auto max_it = std::max_element(start, start + dataset_count, less);
|
|
596
|
+
keys[0] = static_cast<dense_key_t>(max_it->offset), distances[0] = max_it->distance;
|
|
597
|
+
}
|
|
598
|
+
});
|
|
578
599
|
|
|
579
600
|
// At the end report the latest numbers, because the reporter thread may be finished earlier
|
|
580
601
|
progress_(processed.load(), tasks_count);
|
|
@@ -602,6 +623,8 @@ static py::tuple search_many_brute_force( //
|
|
|
602
623
|
Py_ssize_t queries_dimensions = queries_info.shape[1];
|
|
603
624
|
if (dataset_dimensions != queries_dimensions)
|
|
604
625
|
throw std::invalid_argument("The number of vector dimensions doesn't match!");
|
|
626
|
+
if (wanted > dataset_count)
|
|
627
|
+
throw std::invalid_argument("You can't request more matches than in the dataset!");
|
|
605
628
|
|
|
606
629
|
scalar_kind_t dataset_kind = numpy_string_to_kind(dataset_info.format);
|
|
607
630
|
scalar_kind_t queries_kind = numpy_string_to_kind(queries_info.format);
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
from time import time
|
|
2
|
+
|
|
3
|
+
from usearch.index import search, MetricKind, ScalarKind
|
|
4
|
+
from usearch.compiled import hardware_acceleration
|
|
5
|
+
from faiss import IndexFlatL2, knn
|
|
6
|
+
import numpy as np
|
|
7
|
+
import fire
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
def run(
|
|
11
|
+
n: int = 10**5,
|
|
12
|
+
q: int = 10,
|
|
13
|
+
k: int = 100,
|
|
14
|
+
ndim: int = 256,
|
|
15
|
+
half: bool = False,
|
|
16
|
+
):
|
|
17
|
+
usearch_dtype = ScalarKind.F16 if half else ScalarKind.F32
|
|
18
|
+
acceleration = hardware_acceleration(
|
|
19
|
+
dtype=usearch_dtype,
|
|
20
|
+
ndim=ndim,
|
|
21
|
+
metric_kind=MetricKind.L2sq,
|
|
22
|
+
)
|
|
23
|
+
print("Hardware acceleration in USearch: ", acceleration)
|
|
24
|
+
|
|
25
|
+
x = np.random.random((n, ndim))
|
|
26
|
+
x = x.astype(np.float16) if half else x.astype(np.float32)
|
|
27
|
+
|
|
28
|
+
start = time()
|
|
29
|
+
_ = search(
|
|
30
|
+
x,
|
|
31
|
+
x[:q],
|
|
32
|
+
k,
|
|
33
|
+
MetricKind.L2sq,
|
|
34
|
+
exact=True,
|
|
35
|
+
).keys
|
|
36
|
+
print("USearch: ", time() - start)
|
|
37
|
+
|
|
38
|
+
start = time()
|
|
39
|
+
_ = knn(x, x[:q], k)[1]
|
|
40
|
+
print("FAISS: ", time() - start)
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
if __name__ == "__main__":
|
|
44
|
+
fire.Fire(run)
|
|
@@ -11,7 +11,7 @@ from usearch.index import Match, Matches, BatchMatches, Index, Indexes
|
|
|
11
11
|
|
|
12
12
|
|
|
13
13
|
dimensions = [3, 97, 256]
|
|
14
|
-
batch_sizes = [1, 77]
|
|
14
|
+
batch_sizes = [1, 77, 100]
|
|
15
15
|
|
|
16
16
|
|
|
17
17
|
@pytest.mark.parametrize("rows", batch_sizes)
|
|
@@ -56,15 +56,16 @@ def test_exact_search(rows: int, cols: int):
|
|
|
56
56
|
:param int cols: The number of columns in the matrix.
|
|
57
57
|
"""
|
|
58
58
|
original = np.random.rand(rows, cols)
|
|
59
|
-
|
|
59
|
+
keys = np.arange(rows)
|
|
60
|
+
matches: BatchMatches = search(original, original, min(10, rows), exact=True)
|
|
60
61
|
top_matches = (
|
|
61
62
|
[int(m.keys[0]) for m in matches] if rows > 1 else int(matches.keys[0])
|
|
62
63
|
)
|
|
63
|
-
assert np.all(top_matches ==
|
|
64
|
+
assert np.all(top_matches == keys)
|
|
64
65
|
|
|
65
|
-
matches: Matches = search(original, original[
|
|
66
|
+
matches: Matches = search(original, original[-1], min(10, rows), exact=True)
|
|
66
67
|
top_match = int(matches.keys[0])
|
|
67
|
-
assert top_match ==
|
|
68
|
+
assert top_match == keys[-1]
|
|
68
69
|
|
|
69
70
|
|
|
70
71
|
def test_matches_creation_and_methods():
|
package/python/usearch/index.py
CHANGED
|
@@ -180,7 +180,7 @@ def _search_in_compiled(
|
|
|
180
180
|
vectors = vectors.reshape(1, len(vectors))
|
|
181
181
|
count_vectors = vectors.shape[0]
|
|
182
182
|
|
|
183
|
-
def
|
|
183
|
+
def distill_batch(
|
|
184
184
|
batch_matches: BatchMatches,
|
|
185
185
|
) -> Union[BatchMatches, Matches]:
|
|
186
186
|
return batch_matches[0] if count_vectors == 1 else batch_matches
|
|
@@ -188,22 +188,22 @@ def _search_in_compiled(
|
|
|
188
188
|
# Create progress bar if needed
|
|
189
189
|
if log:
|
|
190
190
|
name = log if isinstance(log, str) else "Search"
|
|
191
|
-
|
|
191
|
+
progress_bar = tqdm(
|
|
192
192
|
desc=name,
|
|
193
193
|
total=count_vectors,
|
|
194
194
|
unit="vector",
|
|
195
195
|
)
|
|
196
196
|
|
|
197
197
|
def update_progress_bar(processed: int, total: int) -> bool:
|
|
198
|
-
|
|
198
|
+
progress_bar.update(processed - progress_bar.n)
|
|
199
199
|
return progress if progress else True
|
|
200
200
|
|
|
201
201
|
tuple_ = compiled_callable(vectors, progress=update_progress_bar, **kwargs)
|
|
202
|
-
|
|
202
|
+
progress_bar.close()
|
|
203
203
|
else:
|
|
204
204
|
tuple_ = compiled_callable(vectors, **kwargs)
|
|
205
205
|
|
|
206
|
-
return
|
|
206
|
+
return distill_batch(BatchMatches(*tuple_))
|
|
207
207
|
|
|
208
208
|
|
|
209
209
|
def _add_to_compiled(
|
|
@@ -871,6 +871,10 @@ class Index:
|
|
|
871
871
|
def serialized_length(self) -> int:
|
|
872
872
|
return self._compiled.serialized_length
|
|
873
873
|
|
|
874
|
+
@property
|
|
875
|
+
def metric_kind(self) -> Union[MetricKind, CompiledMetric]:
|
|
876
|
+
return self._metric_jit if self._metric_jit.kind else self._metric_kind
|
|
877
|
+
|
|
874
878
|
@property
|
|
875
879
|
def metric(self) -> Union[MetricKind, CompiledMetric]:
|
|
876
880
|
return self._metric_jit if self._metric_jit else self._metric_kind
|
|
@@ -1190,6 +1194,7 @@ class Index:
|
|
|
1190
1194
|
"size": self.size,
|
|
1191
1195
|
"jit": self.jit,
|
|
1192
1196
|
"hardware_acceleration": self.hardware_acceleration,
|
|
1197
|
+
"metric_kind": self.metric_kind,
|
|
1193
1198
|
"dtype": self.dtype,
|
|
1194
1199
|
"path": self.path,
|
|
1195
1200
|
"compiled_with_openmp": USES_OPENMP,
|
|
@@ -1204,7 +1209,7 @@ class Index:
|
|
|
1204
1209
|
return f.format(
|
|
1205
1210
|
self.dtype,
|
|
1206
1211
|
self.ndim,
|
|
1207
|
-
self.
|
|
1212
|
+
self.metric_kind,
|
|
1208
1213
|
self.connectivity,
|
|
1209
1214
|
self.expansion_add,
|
|
1210
1215
|
self.expansion_search,
|
|
@@ -1225,7 +1230,7 @@ class Index:
|
|
|
1225
1230
|
"- config",
|
|
1226
1231
|
f"-- data type: {self.dtype}",
|
|
1227
1232
|
f"-- dimensions: {self.ndim}",
|
|
1228
|
-
f"-- metric: {self.
|
|
1233
|
+
f"-- metric: {self.metric_kind}",
|
|
1229
1234
|
f"-- connectivity: {self.connectivity}",
|
|
1230
1235
|
f"-- expansion on addition:{self.expansion_add} candidates",
|
|
1231
1236
|
f"-- expansion on search: {self.expansion_search} candidates",
|
|
@@ -1233,7 +1238,7 @@ class Index:
|
|
|
1233
1238
|
f"-- uses OpenMP: {USES_OPENMP}",
|
|
1234
1239
|
f"-- uses SimSIMD: {USES_SIMSIMD}",
|
|
1235
1240
|
f"-- supports half-precision: {USES_NATIVE_F16}",
|
|
1236
|
-
f"-- uses hardware
|
|
1241
|
+
f"-- uses hardware acceleration: {self.hardware_acceleration}",
|
|
1237
1242
|
"- state",
|
|
1238
1243
|
f"-- size: {self.size:,} vectors",
|
|
1239
1244
|
f"-- memory usage: {self.memory_usage:,} bytes",
|
package/setup.py
CHANGED
|
@@ -12,8 +12,11 @@ macros_args = []
|
|
|
12
12
|
if sys.platform == "linux":
|
|
13
13
|
compile_args.append("-std=c++17")
|
|
14
14
|
compile_args.append("-O3") # Maximize performance
|
|
15
|
-
compile_args.append("-g") # Simplify debugging
|
|
16
15
|
compile_args.append("-Wno-unknown-pragmas")
|
|
16
|
+
compile_args.append("-fdiagnostics-color=always")
|
|
17
|
+
|
|
18
|
+
# Simplify debugging, but the normal `-g` may make builds much longer!
|
|
19
|
+
compile_args.append("-g1")
|
|
17
20
|
|
|
18
21
|
macros_args.append(("USEARCH_USE_NATIVE_F16", "0"))
|
|
19
22
|
macros_args.append(("USEARCH_USE_SIMSIMD", "1"))
|
|
@@ -27,9 +30,12 @@ if sys.platform == "darwin":
|
|
|
27
30
|
compile_args.append("-mmacosx-version-min=10.15")
|
|
28
31
|
compile_args.append("-std=c++17")
|
|
29
32
|
compile_args.append("-O3") # Maximize performance
|
|
30
|
-
compile_args.append("-
|
|
33
|
+
compile_args.append("-fcolor-diagnostics")
|
|
31
34
|
compile_args.append("-Wno-unknown-pragmas")
|
|
32
35
|
|
|
36
|
+
# Simplify debugging, but the normal `-g` may make builds much longer!
|
|
37
|
+
compile_args.append("-g1")
|
|
38
|
+
|
|
33
39
|
# Linking OpenMP requires additional preparation in CIBuildWheel
|
|
34
40
|
# macros_args.append(("USEARCH_USE_OPENMP", "1"))
|
|
35
41
|
# compile_args.append("-Xpreprocessor -fopenmp")
|