usearch 0.2.4 → 0.8.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/.clang-tidy +60 -0
- package/.github/ISSUE_TEMPLATE/bug_report.yml +90 -0
- package/.github/ISSUE_TEMPLATE/config.yml +5 -0
- package/.github/ISSUE_TEMPLATE/feature_request.yml +64 -0
- package/.github/workflows/release.yml +47 -20
- package/.github/workflows/update_version.sh +1 -0
- package/.vscode/launch.json +22 -0
- package/.vscode/settings.json +9 -1
- package/.vscode/tasks.json +4 -4
- package/CITATION.cff +11 -0
- package/CMakeLists.txt +108 -95
- package/CODE_OF_CONDUCT.md +128 -0
- package/CONTRIBUTING.md +12 -0
- package/Cargo.toml +1 -1
- package/Dockerfile +14 -0
- package/README.md +21 -12
- package/VERSION +1 -1
- package/binding.gyp +13 -9
- package/build.rs +2 -1
- package/cmake/config.cmake.in +15 -0
- package/cmake/pkg-config.pc.in +4 -0
- package/cmake/usearchConfigVersion.cmake.in +20 -0
- package/conanfile.py +1 -1
- package/docs/{Benchmarking.md → benchmarks.md} +11 -2
- package/docs/benchmarks.rst +5 -0
- package/docs/{Compilation.md → compilation.md} +54 -27
- package/docs/compilation.rst +5 -0
- package/docs/conf.dox +2668 -0
- package/docs/index.rst +7 -0
- package/include/usearch/usearch.hpp +833 -439
- package/java/cloud/unum/usearch/cloud_unum_usearch_Index.cpp +17 -16
- package/javascript/lib.cpp +94 -20
- package/javascript/test.js +1 -1
- package/objc/USearchObjective.mm +47 -30
- package/objc/include/USearchObjective.h +18 -26
- package/package.json +1 -1
- package/pyproject.toml +3 -2
- package/python/lib.cpp +216 -130
- package/python/{bench.py → scripts/bench.py} +6 -1
- package/python/scripts/bench_params.py +112 -0
- package/python/{test.py → scripts/test.py} +32 -13
- package/python/usearch/client.py +100 -14
- package/python/usearch/server.py +104 -23
- package/rust/lib.cpp +20 -15
- package/rust/lib.hpp +5 -5
- package/rust/lib.rs +3 -3
- package/setup.py +7 -0
- package/src/CMakeLists.txt +107 -0
- package/src/bench.cpp +54 -19
- package/src/{advanced.hpp → punned.hpp} +235 -203
- package/src/test.cpp +62 -0
- package/swift/Index+Sugar.swift +1 -0
- package/swift/Test.swift +2 -1
- package/wolfram/lib.cpp +14 -14
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
cmake_minimum_required(VERSION 3.1)
|
|
2
|
+
|
|
3
|
+
option(USEARCH_USE_OPENMP "Use OpenMP for a thread pool" OFF)
|
|
4
|
+
option(USEARCH_USE_SIMSIMD "Use SimSIMD hardware-accelerated metrics" OFF)
|
|
5
|
+
option(USEARCH_USE_JEMALLOC "Use JeMalloc for faster memory allocations" OFF)
|
|
6
|
+
|
|
7
|
+
# Make "Release" by default
|
|
8
|
+
if(NOT CMAKE_BUILD_TYPE)
|
|
9
|
+
set(CMAKE_BUILD_TYPE Release)
|
|
10
|
+
endif()
|
|
11
|
+
|
|
12
|
+
# List of all possible compiler IDs:
|
|
13
|
+
# https://cmake.org/cmake/help/latest/variable/CMAKE_LANG_COMPILER_ID.html
|
|
14
|
+
if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
|
|
15
|
+
set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -fsanitize=address")
|
|
16
|
+
set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -Wall -Wextra -Wno-conversion -Wno-unknown-pragmas")
|
|
17
|
+
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -march=native")
|
|
18
|
+
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fmax-errors=1")
|
|
19
|
+
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fopenmp")
|
|
20
|
+
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -pedantic")
|
|
21
|
+
|
|
22
|
+
elseif(CMAKE_CXX_COMPILER_ID MATCHES "Clang")
|
|
23
|
+
set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -Wfatal-errors")
|
|
24
|
+
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fopenmp")
|
|
25
|
+
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -pedantic")
|
|
26
|
+
|
|
27
|
+
elseif(CMAKE_CXX_COMPILER_ID STREQUAL "NVIDIA" OR CMAKE_CXX_COMPILER_ID STREQUAL "NVHPC")
|
|
28
|
+
set(CMAKE_CUDA_FLAGS "${CMAKE_CUDA_FLAGS} --expt-relaxed-constexpr --extended-lambda")
|
|
29
|
+
set(CMAKE_CUDA_FLAGS "${CMAKE_CUDA_FLAGS} -gencode arch=compute_86,code=sm_86")
|
|
30
|
+
set_property(SOURCE bench.cpp PROPERTY LANGUAGE CUDA)
|
|
31
|
+
set_target_properties(bench PROPERTIES POSITION_INDEPENDENT_CODE ON)
|
|
32
|
+
set_target_properties(bench PROPERTIES CUDA_ARCHITECTURES "86")
|
|
33
|
+
|
|
34
|
+
elseif(CMAKE_CXX_COMPILER_ID STREQUAL "IntelLLVM")
|
|
35
|
+
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -w")
|
|
36
|
+
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -ferror-limit=1")
|
|
37
|
+
|
|
38
|
+
elseif(CMAKE_CXX_COMPILER_ID MATCHES "MSVC")
|
|
39
|
+
set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} /DEBUG")
|
|
40
|
+
set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} /O2")
|
|
41
|
+
set(CMAKE_CXX_FLAGS_RELWITHDEBINFO "${CMAKE_CXX_FLAGS_RELWITHDEBINFO} /O2 /DEBUG")
|
|
42
|
+
else()
|
|
43
|
+
set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -g -O0")
|
|
44
|
+
set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -O3")
|
|
45
|
+
set(CMAKE_CXX_FLAGS_RELWITHDEBINFO "${CMAKE_CXX_FLAGS_RELWITHDEBINFO} -O3 -pg -g")
|
|
46
|
+
endif()
|
|
47
|
+
|
|
48
|
+
if(APPLE)
|
|
49
|
+
set(CMAKE_THREAD_LIBS_INIT "-lpthread")
|
|
50
|
+
set(CMAKE_HAVE_THREADS_LIBRARY 1)
|
|
51
|
+
set(CMAKE_USE_WIN32_THREADS_INIT 0)
|
|
52
|
+
set(CMAKE_USE_PTHREADS_INIT 1)
|
|
53
|
+
set(THREADS_PREFER_PTHREAD_FLAG ON)
|
|
54
|
+
endif()
|
|
55
|
+
|
|
56
|
+
find_package(Threads REQUIRED)
|
|
57
|
+
|
|
58
|
+
if(${USEARCH_USE_OPENMP})
|
|
59
|
+
find_package(OpenMP REQUIRED)
|
|
60
|
+
include_directories(${OPENMP_INCLUDE_DIRS})
|
|
61
|
+
endif()
|
|
62
|
+
|
|
63
|
+
if(${USEARCH_USE_JEMALLOC})
|
|
64
|
+
include(jemalloc)
|
|
65
|
+
endif()
|
|
66
|
+
|
|
67
|
+
include(clipp)
|
|
68
|
+
|
|
69
|
+
set(USEARCH_PUNNED_INCLUDE_DIRS
|
|
70
|
+
"${CMAKE_CURRENT_SOURCE_DIR}/../include"
|
|
71
|
+
"${CMAKE_CURRENT_SOURCE_DIR}/../fp16/include"
|
|
72
|
+
"${CMAKE_CURRENT_SOURCE_DIR}/../simsimd/include"
|
|
73
|
+
"${CMAKE_CURRENT_SOURCE_DIR}/"
|
|
74
|
+
)
|
|
75
|
+
|
|
76
|
+
if(${USEARCH_BUILD_TEST})
|
|
77
|
+
add_executable(test test.cpp)
|
|
78
|
+
target_link_libraries(test PRIVATE Threads::Threads)
|
|
79
|
+
target_include_directories(test PRIVATE ${USEARCH_PUNNED_INCLUDE_DIRS})
|
|
80
|
+
set_target_properties(test PROPERTIES RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR})
|
|
81
|
+
|
|
82
|
+
if(${CMAKE_VERSION} VERSION_EQUAL 3.13 OR ${CMAKE_VERSION} VERSION_GREATER 3.13)
|
|
83
|
+
include(CTest)
|
|
84
|
+
enable_testing()
|
|
85
|
+
add_test(NAME test COMMAND test)
|
|
86
|
+
endif()
|
|
87
|
+
endif()
|
|
88
|
+
|
|
89
|
+
if(${USEARCH_BUILD_BENCHMARK})
|
|
90
|
+
add_executable(bench bench.cpp)
|
|
91
|
+
target_link_libraries(bench PRIVATE Threads::Threads)
|
|
92
|
+
target_include_directories(bench PRIVATE ${USEARCH_PUNNED_INCLUDE_DIRS})
|
|
93
|
+
set_target_properties(bench PROPERTIES RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR})
|
|
94
|
+
|
|
95
|
+
if(${USEARCH_USE_SIMSIMD})
|
|
96
|
+
target_compile_definitions(bench PRIVATE USEARCH_USE_SIMSIMD=1)
|
|
97
|
+
endif()
|
|
98
|
+
|
|
99
|
+
if(${USEARCH_USE_OPENMP})
|
|
100
|
+
target_compile_definitions(bench PRIVATE USEARCH_USE_OPENMP=1)
|
|
101
|
+
target_link_libraries(bench PRIVATE ${OPENMP_LIBRARIES})
|
|
102
|
+
endif()
|
|
103
|
+
|
|
104
|
+
if(${USEARCH_USE_JEMALLOC})
|
|
105
|
+
target_link_libraries(bench PRIVATE ${JEMALLOC_LIBRARIES})
|
|
106
|
+
endif()
|
|
107
|
+
endif()
|
package/src/bench.cpp
CHANGED
|
@@ -29,6 +29,7 @@
|
|
|
29
29
|
#include <iostream> // `std::cerr`
|
|
30
30
|
#include <numeric> // `std::iota`
|
|
31
31
|
#include <stdexcept> // `std::invalid_argument`
|
|
32
|
+
#include <string> // `std::to_string`
|
|
32
33
|
#include <thread> // `std::thread::hardware_concurrency()`
|
|
33
34
|
#include <variant> // `std::monostate`
|
|
34
35
|
|
|
@@ -37,12 +38,13 @@
|
|
|
37
38
|
|
|
38
39
|
#include <simsimd/simsimd.h>
|
|
39
40
|
|
|
40
|
-
#include "
|
|
41
|
+
#include "punned.hpp"
|
|
41
42
|
|
|
42
43
|
using namespace unum::usearch;
|
|
43
44
|
using namespace unum;
|
|
44
45
|
|
|
45
46
|
using vector_id_t = std::uint32_t;
|
|
47
|
+
using vector_view_t = span_gt<float const>;
|
|
46
48
|
|
|
47
49
|
template <typename element_at>
|
|
48
50
|
std::size_t offset_of(element_at const* begin, element_at const* end, element_at v) noexcept {
|
|
@@ -226,7 +228,7 @@ struct running_stats_printer_t {
|
|
|
226
228
|
running_stats_printer_t(std::size_t n, char const* msg) {
|
|
227
229
|
std::printf("%s. %zu items\n", msg, n);
|
|
228
230
|
total = n;
|
|
229
|
-
start_time = std::chrono::high_resolution_clock::now();
|
|
231
|
+
last_printed_time = start_time = std::chrono::high_resolution_clock::now();
|
|
230
232
|
}
|
|
231
233
|
|
|
232
234
|
~running_stats_printer_t() {
|
|
@@ -271,12 +273,15 @@ struct running_stats_printer_t {
|
|
|
271
273
|
template <typename index_at, typename vector_id_at, typename real_at>
|
|
272
274
|
void index_many(index_at& native, std::size_t n, vector_id_at const* ids, real_at const* vectors, std::size_t dims) {
|
|
273
275
|
|
|
274
|
-
using span_t = span_gt<float const>;
|
|
275
276
|
running_stats_printer_t printer{n, "Indexing"};
|
|
276
277
|
|
|
277
278
|
#pragma omp parallel for schedule(static, 32)
|
|
278
279
|
for (std::size_t i = 0; i < n; ++i) {
|
|
279
|
-
|
|
280
|
+
add_config_t config;
|
|
281
|
+
config.thread = omp_get_thread_num();
|
|
282
|
+
config.store_vector = true;
|
|
283
|
+
vector_view_t vector{vectors + dims * i, dims};
|
|
284
|
+
native.add(ids[i], vector, config);
|
|
280
285
|
printer.progress++;
|
|
281
286
|
if (omp_get_thread_num() == 0)
|
|
282
287
|
printer.refresh();
|
|
@@ -284,18 +289,39 @@ void index_many(index_at& native, std::size_t n, vector_id_at const* ids, real_a
|
|
|
284
289
|
}
|
|
285
290
|
|
|
286
291
|
template <typename index_at, typename vector_id_at, typename real_at>
|
|
287
|
-
void search_many(
|
|
288
|
-
|
|
292
|
+
void search_many( //
|
|
293
|
+
index_at& native, std::size_t n, real_at const* vectors, std::size_t dims, std::size_t wanted, vector_id_at* ids,
|
|
294
|
+
real_at* distances) {
|
|
289
295
|
|
|
290
|
-
|
|
291
|
-
running_stats_printer_t printer{n,
|
|
296
|
+
std::string name = "Search " + std::to_string(wanted);
|
|
297
|
+
running_stats_printer_t printer{n, name.c_str()};
|
|
292
298
|
|
|
293
299
|
#pragma omp parallel for schedule(static, 32)
|
|
294
300
|
for (std::size_t i = 0; i < n; ++i) {
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
301
|
+
search_config_t config;
|
|
302
|
+
config.thread = omp_get_thread_num();
|
|
303
|
+
vector_view_t vector{vectors + dims * i, dims};
|
|
304
|
+
native.search(vector, wanted, config).dump_to(ids + wanted * i, distances + wanted * i);
|
|
305
|
+
printer.progress++;
|
|
306
|
+
if (omp_get_thread_num() == 0)
|
|
307
|
+
printer.refresh();
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
template <typename index_at, typename vector_id_at, typename real_at>
|
|
312
|
+
void paginate_many( //
|
|
313
|
+
index_at& native, std::size_t n, real_at const* vectors, std::size_t dims, std::size_t wanted,
|
|
314
|
+
vector_id_at const* hints) {
|
|
315
|
+
|
|
316
|
+
std::string name = "Paginate " + std::to_string(wanted);
|
|
317
|
+
running_stats_printer_t printer{n, name.c_str()};
|
|
318
|
+
|
|
319
|
+
#pragma omp parallel for schedule(static, 32)
|
|
320
|
+
for (std::size_t i = 0; i < n; ++i) {
|
|
321
|
+
search_config_t config;
|
|
322
|
+
config.thread = omp_get_thread_num();
|
|
323
|
+
vector_view_t vector{vectors + dims * i, dims};
|
|
324
|
+
native.search_around(hints[i], vector, wanted, config);
|
|
299
325
|
printer.progress++;
|
|
300
326
|
if (omp_get_thread_num() == 0)
|
|
301
327
|
printer.refresh();
|
|
@@ -333,6 +359,15 @@ static void single_shot(dataset_at& dataset, index_at& index, bool construct = t
|
|
|
333
359
|
|
|
334
360
|
std::printf("Recall@1 %.2f %%\n", recall_at_1 * 100.f / dataset.queries_count());
|
|
335
361
|
std::printf("Recall %.2f %%\n", recall_full * 100.f / dataset.queries_count());
|
|
362
|
+
|
|
363
|
+
// Paginate
|
|
364
|
+
std::vector<vector_id_t> hints(dataset.queries_count());
|
|
365
|
+
for (std::size_t i = 0; i != hints.size(); ++i)
|
|
366
|
+
hints[i] = dataset.neighborhood(i)[0];
|
|
367
|
+
paginate_many(index, dataset.queries_count(), dataset.query(0), dataset.dimensions(), 10, hints.data());
|
|
368
|
+
paginate_many(index, dataset.queries_count(), dataset.query(0), dataset.dimensions(), 100, hints.data());
|
|
369
|
+
paginate_many(index, dataset.queries_count(), dataset.query(0), dataset.dimensions(), 1000, hints.data());
|
|
370
|
+
|
|
336
371
|
std::printf("------------\n");
|
|
337
372
|
std::printf("\n");
|
|
338
373
|
}
|
|
@@ -385,9 +420,9 @@ struct args_t {
|
|
|
385
420
|
std::string path_neighbors;
|
|
386
421
|
std::string path_output = "last.usearch";
|
|
387
422
|
|
|
388
|
-
std::size_t connectivity =
|
|
389
|
-
std::size_t expansion_add =
|
|
390
|
-
std::size_t expansion_search =
|
|
423
|
+
std::size_t connectivity = default_connectivity();
|
|
424
|
+
std::size_t expansion_add = default_expansion_add();
|
|
425
|
+
std::size_t expansion_search = default_expansion_search();
|
|
391
426
|
std::size_t threads = std::thread::hardware_concurrency();
|
|
392
427
|
|
|
393
428
|
std::size_t vectors_to_skip = 0;
|
|
@@ -410,7 +445,7 @@ template <typename index_at, typename dataset_at> //
|
|
|
410
445
|
index_at type_punned_index_for_metric(dataset_at& dataset, args_t const& args, config_t config, accuracy_t accuracy) {
|
|
411
446
|
if (args.metric_l2) {
|
|
412
447
|
std::printf("-- Metric: Euclidean\n");
|
|
413
|
-
return index_at::
|
|
448
|
+
return index_at::l2sq(dataset.dimensions(), accuracy, config);
|
|
414
449
|
} else if (args.metric_cos) {
|
|
415
450
|
std::printf("-- Metric: Angular\n");
|
|
416
451
|
return index_at::cos(dataset.dimensions(), accuracy, config);
|
|
@@ -481,7 +516,7 @@ void run_big_or_small(dataset_at& dataset, args_t const& args, config_t config)
|
|
|
481
516
|
run_typed<index_gt<ip_gt<float>, std::size_t, neighbor_id_at>>(dataset, args, config);
|
|
482
517
|
}
|
|
483
518
|
} else
|
|
484
|
-
run_type_punned<
|
|
519
|
+
run_type_punned<punned_gt<std::size_t, neighbor_id_at>>(dataset, args, config);
|
|
485
520
|
}
|
|
486
521
|
|
|
487
522
|
void report_alternative_setups() {
|
|
@@ -558,7 +593,7 @@ int main(int argc, char** argv) {
|
|
|
558
593
|
option("--f8quant").set(args.quantize_f8).doc("Enable `f8_t` quantization")),
|
|
559
594
|
( //
|
|
560
595
|
option("--ip").set(args.metric_ip).doc("Choose Inner Product metric") |
|
|
561
|
-
option("--
|
|
596
|
+
option("--l2sq").set(args.metric_l2).doc("Choose L2 Euclidean metric") |
|
|
562
597
|
option("--cos").set(args.metric_cos).doc("Choose Angular metric") |
|
|
563
598
|
option("--haversine").set(args.metric_haversine).doc("Choose Haversine metric")),
|
|
564
599
|
option("-h", "--help").set(args.help).doc("Print this help information on this tool and exit"));
|
|
@@ -572,7 +607,7 @@ int main(int argc, char** argv) {
|
|
|
572
607
|
exit(0);
|
|
573
608
|
}
|
|
574
609
|
|
|
575
|
-
// Instead of relying on `multithreaded` from "
|
|
610
|
+
// Instead of relying on `multithreaded` from "punned.hpp" we will use OpenMP
|
|
576
611
|
// to better estimate statistics between tasks batches, without having to recreate
|
|
577
612
|
// the threads.
|
|
578
613
|
omp_set_dynamic(true);
|