diff --git a/src/VecSim/algorithms/hnsw/hnsw.h b/src/VecSim/algorithms/hnsw/hnsw.h index 096dcc985..74c803a17 100644 --- a/src/VecSim/algorithms/hnsw/hnsw.h +++ b/src/VecSim/algorithms/hnsw/hnsw.h @@ -48,6 +48,11 @@ template using candidatesLabelsMaxHeap = vecsim_stl::abstract_priority_queue; using graphNodeType = pair; // represented as: (element_id, level) +#if ONE_BYTE_MUTEX_AVAILABLE == 1 +using elem_mutex_t = vecsim_stl::one_byte_mutex; +#else +using elem_mutex_t = std::mutex; +#endif ////////////////////////////////////// Auxiliary HNSW structs ////////////////////////////////////// // Vectors flags (for marking a specific vector) @@ -74,7 +79,6 @@ struct ElementMetaData { ElementMetaData(labelType label = SIZE_MAX) noexcept : label(label), flags(IN_PROCESS) {} }; -#pragma pack() // restore default packing struct LevelData { vecsim_stl::vector *incomingEdges; @@ -94,7 +98,7 @@ struct LevelData { struct ElementGraphData { size_t toplevel; - std::mutex neighborsGuard; + elem_mutex_t neighborsGuard; LevelData *others; LevelData level0; @@ -113,6 +117,7 @@ struct ElementGraphData { } ~ElementGraphData() = delete; // Should be destroyed using `destroyGraphData` }; +#pragma pack() // restore default packing //////////////////////////////////// HNSW index implementation //////////////////////////////////// diff --git a/src/VecSim/utils/vecsim_stl.h b/src/VecSim/utils/vecsim_stl.h index c5380c36a..87aa37e3c 100644 --- a/src/VecSim/utils/vecsim_stl.h +++ b/src/VecSim/utils/vecsim_stl.h @@ -91,4 +91,36 @@ class unordered_set alloc) {} }; +#if defined(__clang__) +#define CLANG_VERSION (__clang_major__ * 100 + __clang_minor__ * 10 + __clang_patchlevel__) +#if (CLANG_VERSION >= 1316) // clang 13.1.6 +#define ONE_BYTE_MUTEX_AVAILABLE 1 +#endif +#elif (__GNUC__ >= 11) +#define ONE_BYTE_MUTEX_AVAILABLE 1 +#else +#define ONE_BYTE_MUTEX_AVAILABLE 0 +#endif + +#if ONE_BYTE_MUTEX_AVAILABLE != 0 +struct one_byte_mutex { + void lock() { + if (state.exchange(locked, std::memory_order_acquire) == unlocked) + return; + while (state.exchange(sleeper, std::memory_order_acquire) != unlocked) + state.wait(sleeper, std::memory_order_relaxed); + } + void unlock() { + if (state.exchange(unlocked, std::memory_order_release) == sleeper) + state.notify_one(); + } + +private: + std::atomic state{unlocked}; + + static constexpr uint8_t unlocked = 0; + static constexpr uint8_t locked = 0b01; + static constexpr uint8_t sleeper = 0b10; +}; +#endif } // namespace vecsim_stl