Skip to content
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.

Commit d8ee83b

Browse files
committedApr 2, 2025·
Reworking the approach to enabling/disabling exceptions
Now there is no dependency on Abseil (using custom Status/StatusOr classes) and functions that are part of the interface and return Status/StatusOr are named with a NoExceptions suffix.
1 parent 168c1c4 commit d8ee83b

18 files changed

+314
-243
lines changed
 

‎CMakeLists.txt

Lines changed: 30 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -7,34 +7,38 @@ include(GNUInstallDirs)
77
include(CheckCXXCompilerFlag)
88
include(FetchContent)
99

10+
# These example/test targets catch exceptions, so exceptions should always be
11+
# enabled building these files even if they are disabled in other targets.
12+
set(HNSWLIB_TARGETS_REQUIRING_EXCEPTIONS
13+
example_mt_filter
14+
example_mt_replace_deleted
15+
example_mt_search
16+
multiThread_replace_test
17+
test_updates)
18+
19+
1020
function(add_example_or_test TARGET_NAME ...)
1121
add_executable(${ARGV})
1222
target_link_libraries("${TARGET_NAME}" ${common_link_libraries})
13-
target_include_directories("${TARGET_NAME}" PUBLIC "${abseil-cpp_SOURCE_DIR}")
14-
if(HNSWLIB_USE_ABSEIL)
15-
target_compile_definitions("${TARGET_NAME}" PUBLIC HNSWLIB_USE_ABSEIL)
23+
list(FIND HNSWLIB_TARGETS_REQUIRING_EXCEPTIONS "${TARGET_NAME}" _index)
24+
if(_index GREATER -1)
25+
if(NOT HNSWLIB_ENABLE_EXCEPTIONS)
26+
message("Enabling exceptions for target ${TARGET_NAME} as a special case")
27+
endif()
28+
set(should_enable_exceptions ON)
29+
else()
30+
set(should_enable_exceptions "${HNSWLIB_ENABLE_EXCEPTIONS}")
31+
endif()
32+
if(should_enable_exceptions)
33+
target_compile_options("${TARGET_NAME}" PUBLIC -fexceptions)
34+
else()
35+
target_compile_options("${TARGET_NAME}" PUBLIC -fno-exceptions)
1636
endif()
1737
endfunction()
1838

19-
option(HNSWLIB_USE_ABSEIL "Whether to use Abseil in hnswlib" OFF)
39+
option(HNSWLIB_ENABLE_EXCEPTIONS "Whether to enable exceptions in hnswlib" ON)
2040

21-
if(HNSWLIB_USE_ABSEIL)
22-
FetchContent_Declare(
23-
abseil-cpp
24-
GIT_REPOSITORY https://github.com/abseil/abseil-cpp
25-
GIT_TAG 20250127.1
26-
)
27-
28-
FetchContent_MakeAvailable(abseil-cpp)
29-
30-
if("${abseil-cpp_SOURCE_DIR}" STREQUAL "")
31-
message(FATAL "Failed to find Abseil source directory")
32-
endif()
33-
# Abseil requires C++14 or later.
34-
set(CMAKE_CXX_STANDARD 14)
35-
else()
36-
set(CMAKE_CXX_STANDARD 11)
37-
endif()
41+
set(CMAKE_CXX_STANDARD 11)
3842

3943
add_library(hnswlib INTERFACE)
4044
add_library(hnswlib::hnswlib ALIAS hnswlib)
@@ -83,18 +87,16 @@ if(HNSWLIB_EXAMPLES)
8387

8488
set(common_link_libraries
8589
hnswlib)
86-
if(HNSWLIB_USE_ABSEIL)
87-
list(APPEND common_link_libraries absl::status absl::statusor)
88-
endif()
8990

9091
set(EXAMPLE_NAMES
91-
example_search
9292
example_epsilon_search
93-
example_multivector_search
9493
example_filter
95-
example_replace_deleted
94+
example_mt_filter
95+
example_mt_replace_deleted
9696
example_mt_search
97-
example_mt_replace_deleted)
97+
example_multivector_search
98+
example_replace_deleted
99+
example_search)
98100

99101
foreach(example_name IN LISTS EXAMPLE_NAMES)
100102
add_example_or_test("${example_name}" "examples/cpp/${example_name}.cpp")

‎examples/cpp/example_epsilon_search.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@ int main() {
5353
}
5454
std::cout << "Query #" << i << "\n";
5555
hnswlib::EpsilonSearchStopCondition<dist_t> stop_condition(epsilon2, min_num_candidates, max_elements);
56-
HNSWLIB_ASSIGN_VALUE_OR_THROW_IN_TEST(result, alg_hnsw->searchStopConditionClosest(query_data, stop_condition));
56+
auto result = alg_hnsw->searchStopConditionClosest(query_data, stop_condition);
5757
size_t num_vectors = result.size();
5858
std::cout << "Found " << num_vectors << " vectors\n";
5959
delete[] query_data;

‎examples/cpp/example_filter.cpp

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -45,8 +45,7 @@ int main() {
4545
// Query the elements for themselves with filter and check returned labels
4646
int k = 10;
4747
for (int i = 0; i < max_elements; i++) {
48-
HNSWLIB_ASSIGN_VALUE_OR_THROW_IN_TEST(
49-
result, alg_hnsw->searchKnnCloserFirst(data + i * dim, k, &pickIdsDivisibleByTwo));
48+
auto result = alg_hnsw->searchKnnCloserFirst(data + i * dim, k, &pickIdsDivisibleByTwo);
5049
for (auto item: result) {
5150
if (item.second % 2 == 1) std::cout << "Error: found odd label\n";
5251
}

‎examples/cpp/example_mt_filter.cpp

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -106,9 +106,7 @@ int main() {
106106
int k = 10;
107107
std::vector<hnswlib::labeltype> neighbors(max_elements * k);
108108
ParallelFor(0, max_elements, num_threads, [&](size_t row, size_t threadId) {
109-
HNSWLIB_ASSIGN_VALUE_OR_THROW_IN_TEST(
110-
result,
111-
alg_hnsw->searchKnn(data + dim * row, k, &pickIdsDivisibleByTwo));
109+
auto result = alg_hnsw->searchKnn(data + dim * row, k, &pickIdsDivisibleByTwo);
112110
for (int i = 0; i < k; i++) {
113111
hnswlib::labeltype label = result.top().second;
114112
result.pop();

‎examples/cpp/example_mt_search.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -89,7 +89,7 @@ int main() {
8989
// Query the elements for themselves and measure recall
9090
std::vector<hnswlib::labeltype> neighbors(max_elements);
9191
ParallelFor(0, max_elements, num_threads, [&](size_t row, size_t threadId) {
92-
HNSWLIB_ASSIGN_VALUE_OR_THROW_IN_TEST(result, alg_hnsw->searchKnn(data + dim * row, 1));
92+
auto result = alg_hnsw->searchKnn(data + dim * row, 1);
9393
hnswlib::labeltype label = result.top().second;
9494
neighbors[row] = label;
9595
});

‎examples/cpp/example_multivector_search.cpp

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -63,9 +63,7 @@ int main() {
6363
}
6464
std::cout << "Query #" << i << "\n";
6565
hnswlib::MultiVectorSearchStopCondition<docidtype, dist_t> stop_condition(space, num_docs, ef_collection);
66-
HNSWLIB_ASSIGN_VALUE_OR_THROW_IN_TEST(
67-
result,
68-
alg_hnsw->searchStopConditionClosest(query_data, stop_condition));
66+
auto result = alg_hnsw->searchStopConditionClosest(query_data, stop_condition);
6967
size_t num_vectors = result.size();
7068

7169
std::unordered_map<docidtype, size_t> doc_counter;

‎examples/cpp/example_search.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ int main() {
2929
// Query the elements for themselves and measure recall
3030
float correct = 0;
3131
for (int i = 0; i < max_elements; i++) {
32-
HNSWLIB_ASSIGN_VALUE_OR_THROW_IN_TEST(result, alg_hnsw->searchKnn(data + i * dim, 1));
32+
auto result = alg_hnsw->searchKnn(data + i * dim, 1);
3333
hnswlib::labeltype label = result.top().second;
3434
if (label == i) correct++;
3535
}
@@ -45,7 +45,7 @@ int main() {
4545
alg_hnsw = new hnswlib::HierarchicalNSW<float>(&space, hnsw_path);
4646
correct = 0;
4747
for (int i = 0; i < max_elements; i++) {
48-
HNSWLIB_ASSIGN_VALUE_OR_THROW_IN_TEST(result, alg_hnsw->searchKnn(data + i * dim, 1));
48+
auto result = alg_hnsw->searchKnn(data + i * dim, 1);
4949
hnswlib::labeltype label = result.top().second;
5050
if (label == i) correct++;
5151
}

‎hnswlib/bruteforce.h

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ class BruteforceSearch : public AlgorithmInterface<dist_t> {
5454
size_per_element_ = data_size_ + sizeof(labeltype);
5555
data_ = (char *) malloc(maxElements * size_per_element_);
5656
if (data_ == nullptr)
57-
throw std::runtime_error("Not enough memory: BruteforceSearch failed to allocate data");
57+
HNSWLIB_THROW_RUNTIME_ERROR("Not enough memory: BruteforceSearch failed to allocate data");
5858
cur_element_count = 0;
5959
}
6060

@@ -64,7 +64,7 @@ class BruteforceSearch : public AlgorithmInterface<dist_t> {
6464
}
6565

6666

67-
HNSWLIB_STATUS_TYPE addPoint(const void *datapoint, labeltype label, bool replace_deleted = false) override {
67+
Status addPointNoExceptions(const void *datapoint, labeltype label, bool replace_deleted = false) override {
6868
int idx;
6969
{
7070
std::unique_lock<std::mutex> lock(index_lock);
@@ -74,7 +74,7 @@ class BruteforceSearch : public AlgorithmInterface<dist_t> {
7474
idx = search->second;
7575
} else {
7676
if (cur_element_count >= maxelements_) {
77-
throw std::runtime_error("The number of elements exceeds the specified limit\n");
77+
HNSWLIB_THROW_RUNTIME_ERROR("The number of elements exceeds the specified limit\n");
7878
}
7979
idx = cur_element_count;
8080
dict_external_to_internal[label] = idx;
@@ -83,7 +83,7 @@ class BruteforceSearch : public AlgorithmInterface<dist_t> {
8383
}
8484
memcpy(data_ + size_per_element_ * idx + data_size_, &label, sizeof(labeltype));
8585
memcpy(data_ + size_per_element_ * idx, datapoint, data_size_);
86-
HNSWLIB_RETURN_OK_STATUS;
86+
return OkStatus();
8787
}
8888

8989

@@ -108,8 +108,8 @@ class BruteforceSearch : public AlgorithmInterface<dist_t> {
108108

109109

110110
using DistanceLabelPriorityQueue = typename AlgorithmInterface<dist_t>::DistanceLabelPriorityQueue;
111-
HNSWLIB_STATUS_OR_TYPE(DistanceLabelPriorityQueue)
112-
searchKnn(const void *query_data, size_t k, BaseFilterFunctor* isIdAllowed = nullptr) const override {
111+
StatusOr<DistanceLabelPriorityQueue>
112+
searchKnnNoExceptions(const void *query_data, size_t k, BaseFilterFunctor* isIdAllowed = nullptr) const override {
113113
assert(k <= cur_element_count);
114114
std::priority_queue<std::pair<dist_t, labeltype >> topResults;
115115
if (cur_element_count == 0) return topResults;
@@ -168,7 +168,7 @@ class BruteforceSearch : public AlgorithmInterface<dist_t> {
168168
size_per_element_ = data_size_ + sizeof(labeltype);
169169
data_ = (char *) malloc(maxelements_ * size_per_element_);
170170
if (data_ == nullptr)
171-
throw std::runtime_error("Not enough memory: loadIndex failed to allocate data");
171+
HNSWLIB_THROW_RUNTIME_ERROR("Not enough memory: loadIndex failed to allocate data");
172172

173173
input.read(data_, maxelements_ * size_per_element_);
174174

0 commit comments

Comments
 (0)