Skip to content
9 changes: 7 additions & 2 deletions src/VecSim/algorithms/hnsw/hnsw.h
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,11 @@ template <typename DistType>
using candidatesLabelsMaxHeap = vecsim_stl::abstract_priority_queue<DistType, labelType>;
using graphNodeType = pair<idType, ushort>; // 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)
Expand All @@ -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<idType> *incomingEdges;
Expand All @@ -94,7 +98,7 @@ struct LevelData {

struct ElementGraphData {
size_t toplevel;
std::mutex neighborsGuard;
elem_mutex_t neighborsGuard;
LevelData *others;
LevelData level0;

Expand All @@ -113,6 +117,7 @@ struct ElementGraphData {
}
~ElementGraphData() = delete; // Should be destroyed using `destroyGraphData`
};
#pragma pack() // restore default packing

//////////////////////////////////// HNSW index implementation ////////////////////////////////////

Expand Down
32 changes: 32 additions & 0 deletions src/VecSim/utils/vecsim_stl.h
Original file line number Diff line number Diff line change
Expand Up @@ -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<uint8_t> state{unlocked};

static constexpr uint8_t unlocked = 0;
static constexpr uint8_t locked = 0b01;
static constexpr uint8_t sleeper = 0b10;
};
#endif
} // namespace vecsim_stl