oxirs_vec/hnsw/
stats.rs

1//! Performance statistics for HNSW operations
2
3use std::sync::atomic::{AtomicU64, Ordering as AtomicOrdering};
4
5/// Performance statistics for HNSW operations
6#[derive(Debug)]
7pub struct HnswPerformanceStats {
8    pub total_searches: AtomicU64,
9    pub total_insertions: AtomicU64,
10    pub total_deletions: AtomicU64,
11    pub total_updates: AtomicU64,
12    pub avg_search_time_us: AtomicU64, // Store as microseconds, will convert to f64 when needed
13    pub avg_distance_calculations: AtomicU64, // Store as integer, will convert to f64 when needed
14    pub cache_hits: AtomicU64,
15    pub cache_misses: AtomicU64,
16    pub simd_operations: AtomicU64,
17    pub parallel_searches: AtomicU64,
18    pub parallel_operations: AtomicU64,
19    pub prefetch_operations: AtomicU64,
20    pub memory_allocations: AtomicU64,
21    pub lock_contentions: AtomicU64,
22}
23
24impl Default for HnswPerformanceStats {
25    fn default() -> Self {
26        Self {
27            total_searches: AtomicU64::new(0),
28            total_insertions: AtomicU64::new(0),
29            total_deletions: AtomicU64::new(0),
30            total_updates: AtomicU64::new(0),
31            avg_search_time_us: AtomicU64::new(0),
32            avg_distance_calculations: AtomicU64::new(0),
33            cache_hits: AtomicU64::new(0),
34            cache_misses: AtomicU64::new(0),
35            simd_operations: AtomicU64::new(0),
36            parallel_searches: AtomicU64::new(0),
37            parallel_operations: AtomicU64::new(0),
38            prefetch_operations: AtomicU64::new(0),
39            memory_allocations: AtomicU64::new(0),
40            lock_contentions: AtomicU64::new(0),
41        }
42    }
43}
44
45impl Clone for HnswPerformanceStats {
46    fn clone(&self) -> Self {
47        Self {
48            total_searches: AtomicU64::new(self.total_searches.load(AtomicOrdering::Relaxed)),
49            total_insertions: AtomicU64::new(self.total_insertions.load(AtomicOrdering::Relaxed)),
50            total_deletions: AtomicU64::new(self.total_deletions.load(AtomicOrdering::Relaxed)),
51            total_updates: AtomicU64::new(self.total_updates.load(AtomicOrdering::Relaxed)),
52            avg_search_time_us: AtomicU64::new(
53                self.avg_search_time_us.load(AtomicOrdering::Relaxed),
54            ),
55            avg_distance_calculations: AtomicU64::new(
56                self.avg_distance_calculations.load(AtomicOrdering::Relaxed),
57            ),
58            cache_hits: AtomicU64::new(self.cache_hits.load(AtomicOrdering::Relaxed)),
59            cache_misses: AtomicU64::new(self.cache_misses.load(AtomicOrdering::Relaxed)),
60            simd_operations: AtomicU64::new(self.simd_operations.load(AtomicOrdering::Relaxed)),
61            parallel_searches: AtomicU64::new(self.parallel_searches.load(AtomicOrdering::Relaxed)),
62            parallel_operations: AtomicU64::new(
63                self.parallel_operations.load(AtomicOrdering::Relaxed),
64            ),
65            prefetch_operations: AtomicU64::new(
66                self.prefetch_operations.load(AtomicOrdering::Relaxed),
67            ),
68            memory_allocations: AtomicU64::new(
69                self.memory_allocations.load(AtomicOrdering::Relaxed),
70            ),
71            lock_contentions: AtomicU64::new(self.lock_contentions.load(AtomicOrdering::Relaxed)),
72        }
73    }
74}
75
76impl HnswPerformanceStats {
77    /// Get total searches as u64
78    pub fn get_total_searches(&self) -> u64 {
79        self.total_searches.load(AtomicOrdering::Relaxed)
80    }
81
82    /// Get average search time as f64 microseconds
83    pub fn get_avg_search_time_us(&self) -> f64 {
84        self.avg_search_time_us.load(AtomicOrdering::Relaxed) as f64
85    }
86
87    /// Get average distance calculations as f64
88    pub fn get_avg_distance_calculations(&self) -> f64 {
89        self.avg_distance_calculations.load(AtomicOrdering::Relaxed) as f64
90    }
91
92    /// Get cache hit ratio
93    pub fn cache_hit_ratio(&self) -> f64 {
94        let hits = self.cache_hits.load(AtomicOrdering::Relaxed);
95        let misses = self.cache_misses.load(AtomicOrdering::Relaxed);
96        if hits + misses == 0 {
97            0.0
98        } else {
99            hits as f64 / (hits + misses) as f64
100        }
101    }
102
103    /// Get average search time in microseconds
104    pub fn avg_search_time(&self) -> u64 {
105        self.avg_search_time_us.load(AtomicOrdering::Relaxed)
106    }
107
108    /// Get parallel operation efficiency ratio
109    pub fn parallel_efficiency(&self) -> f64 {
110        let total = self.total_searches.load(AtomicOrdering::Relaxed);
111        let parallel = self.parallel_operations.load(AtomicOrdering::Relaxed);
112        if total == 0 {
113            0.0
114        } else {
115            parallel as f64 / total as f64
116        }
117    }
118}