use super::super::layer::NodeId;
use super::super::ordered_float::OrderedFloat;
use std::cell::RefCell;
use std::cmp::Reverse;
use std::collections::BinaryHeap;
#[derive(Default)]
pub(crate) struct BitVecVisited {
pub(in crate::index::hnsw::native::graph) words: Vec<u64>,
}
impl BitVecVisited {
#[inline]
pub(crate) fn with_capacity(capacity: usize) -> Self {
let word_count = capacity.div_ceil(64);
Self {
words: vec![0_u64; word_count],
}
}
#[allow(dead_code)] #[inline]
pub(crate) fn contains(&self, id: usize) -> bool {
let word_idx = id / 64;
let bit_idx = id % 64;
self.words
.get(word_idx)
.is_some_and(|word| word & (1_u64 << bit_idx) != 0)
}
#[inline]
pub(crate) fn insert(&mut self, id: usize) -> bool {
self.ensure_capacity(id);
let word_idx = id / 64;
let bit_idx = id % 64;
let mask = 1_u64 << bit_idx;
let was_unset = self.words[word_idx] & mask == 0;
self.words[word_idx] |= mask;
was_unset
}
#[inline]
pub(crate) fn clear(&mut self) {
self.words.fill(0);
}
#[inline]
pub(in crate::index::hnsw::native::graph) fn ensure_capacity(&mut self, id: usize) {
let required = id / 64 + 1;
if required > self.words.len() {
self.words.resize(required, 0);
}
}
}
#[inline]
pub(super) fn should_prefetch(dimension: usize) -> bool {
let vector_bytes = dimension * std::mem::size_of::<f32>();
vector_bytes >= 2 * crate::simd_native::L2_CACHE_LINE_BYTES
}
pub(super) const POOL_MAX: usize = 4;
pub(super) type CandidateHeap = BinaryHeap<Reverse<(OrderedFloat, NodeId)>>;
pub(super) type ResultHeap = BinaryHeap<(OrderedFloat, NodeId)>;
thread_local! {
pub(super) static VISITED_POOL: RefCell<Vec<BitVecVisited>> = const { RefCell::new(Vec::new()) };
pub(super) static CANDIDATE_HEAP_POOL: RefCell<Vec<CandidateHeap>> = const { RefCell::new(Vec::new()) };
pub(super) static RESULT_HEAP_POOL: RefCell<Vec<ResultHeap>> = const { RefCell::new(Vec::new()) };
}
#[inline]
pub(super) fn acquire_visited_set(capacity_hint: usize) -> BitVecVisited {
VISITED_POOL.with(|pool| {
let mut set = pool
.borrow_mut()
.pop()
.unwrap_or_else(|| BitVecVisited::with_capacity(capacity_hint));
if capacity_hint > 0 {
set.ensure_capacity(capacity_hint.saturating_sub(1));
}
set
})
}
#[inline]
pub(super) fn release_visited_set(mut set: BitVecVisited) {
set.clear();
VISITED_POOL.with(|pool| {
let mut pool = pool.borrow_mut();
if pool.len() < POOL_MAX {
pool.push(set);
}
});
}
#[inline]
pub(super) fn acquire_candidate_heap() -> CandidateHeap {
CANDIDATE_HEAP_POOL.with(|pool| pool.borrow_mut().pop().unwrap_or_default())
}
#[inline]
pub(super) fn release_candidate_heap(mut heap: CandidateHeap) {
heap.clear();
CANDIDATE_HEAP_POOL.with(|pool| {
let mut pool = pool.borrow_mut();
if pool.len() < POOL_MAX {
pool.push(heap);
}
});
}
#[inline]
pub(super) fn acquire_result_heap() -> ResultHeap {
RESULT_HEAP_POOL.with(|pool| pool.borrow_mut().pop().unwrap_or_default())
}
#[inline]
pub(super) fn release_result_heap(mut heap: ResultHeap) {
heap.clear();
RESULT_HEAP_POOL.with(|pool| {
let mut pool = pool.borrow_mut();
if pool.len() < POOL_MAX {
pool.push(heap);
}
});
}