use std::sync::atomic::{AtomicUsize, Ordering};
pub const AND_CANDIDATES_SEEN_METRIC: &str = "and_candidates_seen";
pub const AND_CANDIDATES_PRUNED_BEFORE_RETURN_METRIC: &str = "and_candidates_pruned_before_return";
pub const AND_FULL_SCORES_METRIC: &str = "and_full_scores";
pub const FREQS_COLLECTED_METRIC: &str = "freqs_collected";
pub trait MetricsCollector: Send + Sync {
fn record_parts_loaded(&self, num_parts: usize);
fn record_part_load(&self) {
self.record_parts_loaded(1);
}
fn record_index_loads(&self, num_indexes: usize);
fn record_index_load(&self) {
self.record_index_loads(1);
}
fn record_comparisons(&self, num_comparisons: usize);
fn record_index_cache_hits(&self, _num_hits: usize) {}
fn record_index_cache_hit(&self) {
self.record_index_cache_hits(1);
}
fn record_index_cache_misses(&self, _num_misses: usize) {}
fn record_index_cache_miss(&self) {
self.record_index_cache_misses(1);
}
fn record_and_candidates_seen(&self, _num_candidates: usize) {}
fn record_and_candidates_pruned_before_return(&self, _num_candidates: usize) {}
fn record_and_full_scores(&self, _num_scores: usize) {}
fn record_freqs_collected(&self, _num_collections: usize) {}
fn io_stats(&self) -> Option<lance_io::scheduler::IoStats> {
None
}
}
pub struct NoOpMetricsCollector;
impl MetricsCollector for NoOpMetricsCollector {
fn record_parts_loaded(&self, _num_parts: usize) {}
fn record_index_loads(&self, _num_indexes: usize) {}
fn record_comparisons(&self, _num_comparisons: usize) {}
}
#[derive(Default)]
pub struct LocalMetricsCollector {
pub parts_loaded: AtomicUsize,
pub index_loads: AtomicUsize,
pub comparisons: AtomicUsize,
pub(crate) index_cache_hits: AtomicUsize,
pub(crate) index_cache_misses: AtomicUsize,
}
impl LocalMetricsCollector {
pub fn dump_into(self, other: &dyn MetricsCollector) {
other.record_parts_loaded(self.parts_loaded.load(Ordering::Relaxed));
other.record_index_loads(self.index_loads.load(Ordering::Relaxed));
other.record_comparisons(self.comparisons.load(Ordering::Relaxed));
other.record_index_cache_hits(self.index_cache_hits.load(Ordering::Relaxed));
other.record_index_cache_misses(self.index_cache_misses.load(Ordering::Relaxed));
}
pub fn index_cache_hits(&self) -> usize {
self.index_cache_hits.load(Ordering::Relaxed)
}
pub fn index_cache_misses(&self) -> usize {
self.index_cache_misses.load(Ordering::Relaxed)
}
}
impl MetricsCollector for LocalMetricsCollector {
fn record_parts_loaded(&self, num_parts: usize) {
self.parts_loaded.fetch_add(num_parts, Ordering::Relaxed);
}
fn record_index_loads(&self, num_indexes: usize) {
self.index_loads.fetch_add(num_indexes, Ordering::Relaxed);
}
fn record_comparisons(&self, num_comparisons: usize) {
self.comparisons
.fetch_add(num_comparisons, Ordering::Relaxed);
}
fn record_index_cache_hits(&self, num_hits: usize) {
self.index_cache_hits.fetch_add(num_hits, Ordering::Relaxed);
}
fn record_index_cache_misses(&self, num_misses: usize) {
self.index_cache_misses
.fetch_add(num_misses, Ordering::Relaxed);
}
}
#[cfg(test)]
mod tests {
use super::*;
struct SumSink {
parts: AtomicUsize,
loads: AtomicUsize,
comparisons: AtomicUsize,
hits: AtomicUsize,
misses: AtomicUsize,
}
impl MetricsCollector for SumSink {
fn record_parts_loaded(&self, n: usize) {
self.parts.fetch_add(n, Ordering::Relaxed);
}
fn record_index_loads(&self, n: usize) {
self.loads.fetch_add(n, Ordering::Relaxed);
}
fn record_comparisons(&self, n: usize) {
self.comparisons.fetch_add(n, Ordering::Relaxed);
}
fn record_index_cache_hits(&self, n: usize) {
self.hits.fetch_add(n, Ordering::Relaxed);
}
fn record_index_cache_misses(&self, n: usize) {
self.misses.fetch_add(n, Ordering::Relaxed);
}
}
#[test]
fn local_metrics_collector_forwards_cache_counts() {
let local = LocalMetricsCollector::default();
local.record_index_cache_hit();
local.record_index_cache_hit();
local.record_index_cache_misses(3);
local.record_part_load();
local.record_index_load();
local.record_comparisons(5);
let sink = SumSink {
parts: AtomicUsize::new(0),
loads: AtomicUsize::new(0),
comparisons: AtomicUsize::new(0),
hits: AtomicUsize::new(0),
misses: AtomicUsize::new(0),
};
local.dump_into(&sink);
assert_eq!(sink.parts.load(Ordering::Relaxed), 1);
assert_eq!(sink.loads.load(Ordering::Relaxed), 1);
assert_eq!(sink.comparisons.load(Ordering::Relaxed), 5);
assert_eq!(sink.hits.load(Ordering::Relaxed), 2);
assert_eq!(sink.misses.load(Ordering::Relaxed), 3);
}
#[test]
fn no_op_metrics_collector_ignores_cache_counts() {
let collector = NoOpMetricsCollector;
collector.record_index_cache_hit();
collector.record_index_cache_miss();
collector.record_index_cache_hits(10);
collector.record_index_cache_misses(20);
}
}