Skip to content

Commit cb91c4c

Browse files
committed
replacing ndoes mutex in one byte mutex
1 parent dab086d commit cb91c4c

File tree

2 files changed

+42
-21
lines changed

2 files changed

+42
-21
lines changed

src/VecSim/algorithms/hnsw/hnsw.h

Lines changed: 23 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -103,7 +103,7 @@ class HNSWIndex : public VecSimIndexAbstract<DistType>,
103103
mutable VisitedNodesHandlerPool visited_nodes_handler_pool;
104104
mutable std::mutex entry_point_guard_;
105105
mutable std::mutex index_data_guard_;
106-
mutable vecsim_stl::vector<std::mutex> element_neighbors_locks_;
106+
mutable vecsim_stl::vector<vecsim_stl::one_byte_mutex> element_neighbors_locks_;
107107

108108
#ifdef BUILD_TESTS
109109
#include "VecSim/algorithms/hnsw/hnsw_base_tests_friends.h"
@@ -159,11 +159,10 @@ class HNSWIndex : public VecSimIndexAbstract<DistType>,
159159
const std::pair<DistType, idType> &neighbor_data,
160160
idType *new_node_neighbors_list,
161161
idType *neighbor_neighbors_list,
162-
std::unique_lock<std::mutex> &node_lock,
163-
std::unique_lock<std::mutex> &neighbor_lock);
164-
inline idType mutuallyConnectNewElement(idType new_node_id,
165-
candidatesMaxHeap<DistType> &top_candidates,
166-
size_t level);
162+
std::unique_lock<vecsim_stl::one_byte_mutex> &node_lock,
163+
std::unique_lock<vecsim_stl::one_byte_mutex> &neighbor_lock);
164+
idType mutuallyConnectNewElement(idType new_node_id,
165+
candidatesMaxHeap<DistType> &top_candidates, size_t level);
167166
template <bool with_timeout>
168167
void greedySearchLevel(const void *vector_data, size_t level, idType &curObj, DistType &curDist,
169168
void *timeoutCtx = nullptr, VecSimQueryResult_Code *rc = nullptr) const;
@@ -521,7 +520,7 @@ DistType HNSWIndex<DataType, DistType>::processCandidate(
521520
tag_t *elements_tags, vecsim_stl::abstract_priority_queue<DistType, Identifier> &top_candidates,
522521
candidatesMaxHeap<DistType> &candidate_set, DistType lowerBound) const {
523522

524-
std::unique_lock<std::mutex> lock(element_neighbors_locks_[curNodeId]);
523+
std::unique_lock<vecsim_stl::one_byte_mutex> lock(element_neighbors_locks_[curNodeId]);
525524
idType *node_links = get_linklist_at_level(curNodeId, layer);
526525
linkListSize links_num = getListCount(node_links);
527526

@@ -573,7 +572,7 @@ void HNSWIndex<DataType, DistType>::processCandidate_RangeSearch(
573572
tag_t *elements_tags, std::unique_ptr<vecsim_stl::abstract_results_container> &results,
574573
candidatesMaxHeap<DistType> &candidate_set, DistType dyn_range, double radius) const {
575574

576-
std::unique_lock<std::mutex> lock(element_neighbors_locks_[curNodeId]);
575+
std::unique_lock<vecsim_stl::one_byte_mutex> lock(element_neighbors_locks_[curNodeId]);
577576
idType *node_links = get_linklist_at_level(curNodeId, layer);
578577
linkListSize links_num = getListCount(node_links);
579578

@@ -703,7 +702,7 @@ template <typename DataType, typename DistType>
703702
void HNSWIndex<DataType, DistType>::revisitNeighborConnections(
704703
size_t level, idType new_node_id, const std::pair<DistType, idType> &neighbor_data,
705704
idType *new_node_neighbors_list, idType *neighbor_neighbors_list,
706-
std::unique_lock<std::mutex> &node_lock, std::unique_lock<std::mutex> &neighbor_lock) {
705+
std::unique_lock<vecsim_stl::one_byte_mutex> &node_lock, std::unique_lock<vecsim_stl::one_byte_mutex> &neighbor_lock) {
707706
// Note - expect that node_lock and neighbor_lock are locked at that point.
708707

709708
// Collect the existing neighbors and the new node as the neighbor's neighbors candidates.
@@ -760,9 +759,10 @@ void HNSWIndex<DataType, DistType>::revisitNeighborConnections(
760759

761760
std::sort(nodes_to_update.begin(), nodes_to_update.end());
762761
size_t nodes_to_update_count = nodes_to_update.size();
763-
std::unique_lock<std::mutex> locks[nodes_to_update_count];
762+
std::unique_lock<vecsim_stl::one_byte_mutex> locks[nodes_to_update_count];
764763
for (size_t i = 0; i < nodes_to_update_count; i++) {
765-
locks[i] = std::unique_lock<std::mutex>(element_neighbors_locks_[nodes_to_update[i]]);
764+
locks[i] = std::unique_lock<vecsim_stl::one_byte_mutex>(
765+
element_neighbors_locks_[nodes_to_update[i]]);
766766
}
767767

768768
auto *neighbour_incoming_edges = getIncomingEdgesPtr(selected_neighbor, level);
@@ -855,17 +855,19 @@ idType HNSWIndex<DataType, DistType>::mutuallyConnectNewElement(
855855

856856
for (auto &neighbor_data : selected_neighbors) {
857857
idType selected_neighbor = neighbor_data.second; // neighbor's id
858-
std::unique_lock<std::mutex> node_lock;
859-
std::unique_lock<std::mutex> neighbor_lock;
858+
std::unique_lock<vecsim_stl::one_byte_mutex> node_lock;
859+
std::unique_lock<vecsim_stl::one_byte_mutex> neighbor_lock;
860860
idType lower_id = (new_node_id < selected_neighbor) ? new_node_id : selected_neighbor;
861861
if (lower_id == new_node_id) {
862-
node_lock = std::unique_lock<std::mutex>(element_neighbors_locks_[new_node_id]);
863-
neighbor_lock =
864-
std::unique_lock<std::mutex>(element_neighbors_locks_[selected_neighbor]);
862+
node_lock =
863+
std::unique_lock<vecsim_stl::one_byte_mutex>(element_neighbors_locks_[new_node_id]);
864+
neighbor_lock = std::unique_lock<vecsim_stl::one_byte_mutex>(
865+
element_neighbors_locks_[selected_neighbor]);
865866
} else {
866-
neighbor_lock =
867-
std::unique_lock<std::mutex>(element_neighbors_locks_[selected_neighbor]);
868-
node_lock = std::unique_lock<std::mutex>(element_neighbors_locks_[new_node_id]);
867+
neighbor_lock = std::unique_lock<vecsim_stl::one_byte_mutex>(
868+
element_neighbors_locks_[selected_neighbor]);
869+
node_lock =
870+
std::unique_lock<vecsim_stl::one_byte_mutex>(element_neighbors_locks_[new_node_id]);
869871
}
870872

871873
// get the updated count - this may change between iterations due to releasing the lock.
@@ -1124,7 +1126,7 @@ void HNSWIndex<DataType, DistType>::greedySearchLevel(const void *vector_data, s
11241126
return;
11251127
}
11261128
changed = false;
1127-
std::unique_lock<std::mutex> lock(element_neighbors_locks_[curObj]);
1129+
std::unique_lock<vecsim_stl::one_byte_mutex> lock(element_neighbors_locks_[curObj]);
11281130
idType *node_links = get_linklist(curObj, level);
11291131
linkListSize links_count = getListCount(node_links);
11301132

@@ -1150,7 +1152,7 @@ void HNSWIndex<DataType, DistType>::resizeIndexInternal(size_t new_max_elements)
11501152
element_levels_.shrink_to_fit();
11511153
resizeLabelLookup(new_max_elements);
11521154
visited_nodes_handler_pool.resize(new_max_elements);
1153-
vecsim_stl::vector<std::mutex>(new_max_elements, this->allocator)
1155+
vecsim_stl::vector<vecsim_stl::one_byte_mutex>(new_max_elements, this->allocator)
11541156
.swap(element_neighbors_locks_);
11551157
// Reallocate base layer
11561158
char *data_level0_memory_new = (char *)this->allocator->reallocate(

src/VecSim/utils/vecsim_stl.h

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -91,4 +91,23 @@ class unordered_set
9191
alloc) {}
9292
};
9393

94+
struct one_byte_mutex {
95+
void lock() {
96+
if (state.exchange(locked, std::memory_order_acquire) == unlocked)
97+
return;
98+
while (state.exchange(sleeper, std::memory_order_acquire) != unlocked)
99+
state.wait(sleeper, std::memory_order_relaxed);
100+
}
101+
void unlock() {
102+
if (state.exchange(unlocked, std::memory_order_release) == sleeper)
103+
state.notify_one();
104+
}
105+
106+
private:
107+
std::atomic<uint8_t> state{unlocked};
108+
109+
static constexpr uint8_t unlocked = 0;
110+
static constexpr uint8_t locked = 0b01;
111+
static constexpr uint8_t sleeper = 0b10;
112+
};
94113
} // namespace vecsim_stl

0 commit comments

Comments
 (0)