Skip to main content

kevy_vector/
hnsw_stats.rs

1//! Read-only counters for [`Hnsw`] (child module via `#[path]`, the
2//! `segment_stats.rs` house pattern), split from `hnsw.rs` for the
3//! 500-LOC ceiling. The running-counter `stats()` and its walking
4//! reference live side by side so they cannot drift apart unnoticed.
5
6use super::{Hnsw, VectorStats};
7
8impl Hnsw {
9    /// Live (non-tombstoned) vectors — already tracked, so `O(1)`.
10    /// [`Self::stats`] walks every node and every link to estimate bytes;
11    /// a caller that only wants the count should not trigger that walk.
12    pub fn vectors(&self) -> u64 {
13        self.live
14    }
15
16    /// Counters — O(1): `links_total` and `tombstones`
17    /// are maintained at the three mutation sites (link push, shrink,
18    /// tombstoning) instead of walking every node per call (this ran
19    /// on every tiering tick). [`Self::recompute_stats`] is the
20    /// walking reference the tests hold them to.
21    pub fn stats(&self) -> VectorStats {
22        self.stats_from(self.links_total, self.tombstones)
23    }
24
25    fn stats_from(&self, links: u64, tombstones: u64) -> VectorStats {
26        let bytes_vec = (self.dim * 4) as u64;
27        let approx_bytes: u64 = self.nodes.len() as u64 * (bytes_vec + 40)
28            + links * 8
29            + self.live * 32;
30        VectorStats {
31            vectors: self.live,
32            tombstones,
33            links,
34            approx_bytes,
35            rebuild_recommended: !self.nodes.is_empty() && tombstones * 10 > self.nodes.len() as u64 * 3,
36        }
37    }
38
39    /// The walking reference — recomputes both counters from the live
40    /// graph. Test-only: production reads the running counters.
41    #[cfg(test)]
42    pub fn recompute_stats(&self) -> VectorStats {
43        let links: u64 = self.nodes.iter().map(|n| n.links.iter().map(Vec::len).sum::<usize>() as u64).sum();
44        let tombstones = self.nodes.iter().filter(|n| n.dead).count() as u64;
45        self.stats_from(links, tombstones)
46    }
47
48}