1use std::sync::atomic::{AtomicU64, Ordering};
7use std::time::{Duration, Instant};
8
9#[derive(Debug, Clone, Copy, PartialEq, Eq)]
11pub enum Operation {
12 MapFile,
14
15 MapAnon,
17
18 Unmap,
20
21 Flush,
23
24 Advise,
26}
27
28#[derive(Debug, Default)]
30pub struct MemoryStats {
31 pub map_count: u64,
33
34 pub unmap_count: u64,
36
37 pub flush_count: u64,
39
40 pub advise_count: u64,
42
43 pub bytes_mapped: u64,
45
46 pub bytes_unmapped: u64,
48
49 pub bytes_flushed: u64,
51
52 pub avg_map_time_us: u64,
54
55 pub avg_unmap_time_us: u64,
57
58 pub avg_flush_time_us: u64,
60}
61
62static 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#[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#[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#[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#[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}