membase/utils/
metrics.rs

1//! Performance metrics for memory mapping operations.
2//!
3//! This module provides functionality for tracking and reporting performance
4//! metrics for memory mapping operations.
5
6use std::sync::atomic::{AtomicU64, Ordering};
7use std::time::{Duration, Instant};
8
9/// Operation types for metrics tracking.
10#[derive(Debug, Clone, Copy, PartialEq, Eq)]
11pub enum Operation {
12    /// Map a file.
13    MapFile,
14    
15    /// Map anonymous memory.
16    MapAnon,
17    
18    /// Unmap memory.
19    Unmap,
20    
21    /// Flush memory to disk.
22    Flush,
23    
24    /// Advise the kernel about memory usage.
25    Advise,
26}
27
28/// Memory mapping statistics.
29#[derive(Debug, Default)]
30pub struct MemoryStats {
31    /// Total number of map operations.
32    pub map_count: u64,
33    
34    /// Total number of unmap operations.
35    pub unmap_count: u64,
36    
37    /// Total number of flush operations.
38    pub flush_count: u64,
39    
40    /// Total number of advise operations.
41    pub advise_count: u64,
42    
43    /// Total bytes mapped.
44    pub bytes_mapped: u64,
45    
46    /// Total bytes unmapped.
47    pub bytes_unmapped: u64,
48    
49    /// Total bytes flushed.
50    pub bytes_flushed: u64,
51    
52    /// Average map operation time in microseconds.
53    pub avg_map_time_us: u64,
54    
55    /// Average unmap operation time in microseconds.
56    pub avg_unmap_time_us: u64,
57    
58    /// Average flush operation time in microseconds.
59    pub avg_flush_time_us: u64,
60}
61
62// Atomic counters for tracking metrics
63static MAP_COUNT: AtomicU64 = AtomicU64::new(0);
64static UNMAP_COUNT: AtomicU64 = AtomicU64::new(0);
65static FLUSH_COUNT: AtomicU64 = AtomicU64::new(0);
66static ADVISE_COUNT: AtomicU64 = AtomicU64::new(0);
67static BYTES_MAPPED: AtomicU64 = AtomicU64::new(0);
68static BYTES_UNMAPPED: AtomicU64 = AtomicU64::new(0);
69static BYTES_FLUSHED: AtomicU64 = AtomicU64::new(0);
70static TOTAL_MAP_TIME_US: AtomicU64 = AtomicU64::new(0);
71static TOTAL_UNMAP_TIME_US: AtomicU64 = AtomicU64::new(0);
72static TOTAL_FLUSH_TIME_US: AtomicU64 = AtomicU64::new(0);
73
74/// Record a memory mapping operation for metrics tracking.
75///
76/// # Arguments
77///
78/// * `op` - The operation type.
79/// * `size` - The size of the memory region in bytes.
80/// * `duration` - The duration of the operation.
81#[inline]
82pub fn record_operation(op: Operation, size: usize, duration: Duration) {
83    let duration_us = duration.as_micros() as u64;
84    
85    match op {
86        Operation::MapFile | Operation::MapAnon => {
87            MAP_COUNT.fetch_add(1, Ordering::Relaxed);
88            BYTES_MAPPED.fetch_add(size as u64, Ordering::Relaxed);
89            TOTAL_MAP_TIME_US.fetch_add(duration_us, Ordering::Relaxed);
90        },
91        Operation::Unmap => {
92            UNMAP_COUNT.fetch_add(1, Ordering::Relaxed);
93            BYTES_UNMAPPED.fetch_add(size as u64, Ordering::Relaxed);
94            TOTAL_UNMAP_TIME_US.fetch_add(duration_us, Ordering::Relaxed);
95        },
96        Operation::Flush => {
97            FLUSH_COUNT.fetch_add(1, Ordering::Relaxed);
98            BYTES_FLUSHED.fetch_add(size as u64, Ordering::Relaxed);
99            TOTAL_FLUSH_TIME_US.fetch_add(duration_us, Ordering::Relaxed);
100        },
101        Operation::Advise => {
102            ADVISE_COUNT.fetch_add(1, Ordering::Relaxed);
103        },
104    }
105}
106
107/// Get the current memory mapping statistics.
108///
109/// # Returns
110///
111/// The current memory mapping statistics.
112#[inline]
113pub fn get_stats() -> MemoryStats {
114    let map_count = MAP_COUNT.load(Ordering::Relaxed);
115    let unmap_count = UNMAP_COUNT.load(Ordering::Relaxed);
116    let flush_count = FLUSH_COUNT.load(Ordering::Relaxed);
117    
118    let avg_map_time_us = if map_count > 0 {
119        TOTAL_MAP_TIME_US.load(Ordering::Relaxed) / map_count
120    } else {
121        0
122    };
123    
124    let avg_unmap_time_us = if unmap_count > 0 {
125        TOTAL_UNMAP_TIME_US.load(Ordering::Relaxed) / unmap_count
126    } else {
127        0
128    };
129    
130    let avg_flush_time_us = if flush_count > 0 {
131        TOTAL_FLUSH_TIME_US.load(Ordering::Relaxed) / flush_count
132    } else {
133        0
134    };
135    
136    MemoryStats {
137        map_count,
138        unmap_count,
139        flush_count,
140        advise_count: ADVISE_COUNT.load(Ordering::Relaxed),
141        bytes_mapped: BYTES_MAPPED.load(Ordering::Relaxed),
142        bytes_unmapped: BYTES_UNMAPPED.load(Ordering::Relaxed),
143        bytes_flushed: BYTES_FLUSHED.load(Ordering::Relaxed),
144        avg_map_time_us,
145        avg_unmap_time_us,
146        avg_flush_time_us,
147    }
148}
149
150/// Reset all memory mapping statistics.
151#[inline]
152pub fn reset_stats() {
153    MAP_COUNT.store(0, Ordering::Relaxed);
154    UNMAP_COUNT.store(0, Ordering::Relaxed);
155    FLUSH_COUNT.store(0, Ordering::Relaxed);
156    ADVISE_COUNT.store(0, Ordering::Relaxed);
157    BYTES_MAPPED.store(0, Ordering::Relaxed);
158    BYTES_UNMAPPED.store(0, Ordering::Relaxed);
159    BYTES_FLUSHED.store(0, Ordering::Relaxed);
160    TOTAL_MAP_TIME_US.store(0, Ordering::Relaxed);
161    TOTAL_UNMAP_TIME_US.store(0, Ordering::Relaxed);
162    TOTAL_FLUSH_TIME_US.store(0, Ordering::Relaxed);
163}
164
165/// Measure the duration of an operation and record it.
166///
167/// # Arguments
168///
169/// * `op` - The operation type.
170/// * `size` - The size of the memory region in bytes.
171/// * `f` - The function to measure.
172///
173/// # Returns
174///
175/// The result of the function.
176#[inline]
177pub fn measure<F, T>(op: Operation, size: usize, f: F) -> T
178where
179    F: FnOnce() -> T,
180{
181    let start = Instant::now();
182    let result = f();
183    let duration = start.elapsed();
184    
185    record_operation(op, size, duration);
186    
187    result
188}