1#[cfg(feature = "fmt")]
2use std::fmt;
3use std::{
4 alloc::{GlobalAlloc, Layout, System},
5 cmp, ptr,
6 sync::atomic::{AtomicUsize, Ordering},
7};
8
9#[cfg(feature = "fmt")]
10use humansize::{format_size, BINARY};
11#[cfg(feature = "fmt")]
12use num_format::{Locale, ToFormattedString};
13
14#[derive(Debug, Default, Clone, Copy)]
16pub struct SystemWithStats;
17
18#[derive(Debug, Default, Clone)]
20pub struct SystemStats {
21 pub alloc_count: usize,
23 pub alloc_avg: Option<usize>,
25 pub dealloc_count: usize,
27 pub dealloc_avg: Option<usize>,
29 pub realloc_growth_count: usize,
31 pub realloc_growth_avg: Option<usize>,
33 pub realloc_shrink_count: usize,
35 pub realloc_shrink_avg: Option<usize>,
37 pub use_curr: usize,
39 pub use_max: usize,
41}
42
43#[cfg(feature = "fmt")]
44impl fmt::Display for SystemStats {
45 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
46 writeln!(f, "SystemStats {{")?;
47 writeln!(
48 f,
49 "\talloc_count: {}",
50 self.alloc_count.to_formatted_string(&Locale::en)
51 )?;
52 if let Some(alloc_avg) = self.alloc_avg {
53 writeln!(f, "\talloc_avg: {}", format_size(alloc_avg, BINARY))?;
54 }
55 writeln!(
56 f,
57 "\tdealloc_count: {}",
58 self.dealloc_count.to_formatted_string(&Locale::en)
59 )?;
60 if let Some(dealloc_avg) = self.dealloc_avg {
61 writeln!(f, "\tdealloc_avg: {}", format_size(dealloc_avg, BINARY))?;
62 }
63 writeln!(
64 f,
65 "\trealloc_growth_count: {}",
66 self.realloc_growth_count.to_formatted_string(&Locale::en)
67 )?;
68 if let Some(realloc_growth_avg) = self.realloc_growth_avg {
69 writeln!(
70 f,
71 "\trealloc_growth_avg: {}",
72 format_size(realloc_growth_avg, BINARY)
73 )?;
74 }
75 writeln!(
76 f,
77 "\trealloc_shrink_count: {}",
78 self.realloc_shrink_count.to_formatted_string(&Locale::en)
79 )?;
80 if let Some(realloc_shrink_avg) = self.realloc_shrink_avg {
81 writeln!(
82 f,
83 "\trealloc_shrink_avg: {}",
84 format_size(realloc_shrink_avg, BINARY)
85 )?;
86 }
87 writeln!(f, "\tuse_curr: {}", format_size(self.use_curr, BINARY))?;
88 writeln!(f, "\tuse_max: {}", format_size(self.use_max, BINARY))?;
89 writeln!(f, "}}")
90 }
91}
92
93static ALLOC_COUNT: AtomicUsize = AtomicUsize::new(0);
94static ALLOC_SUM: AtomicUsize = AtomicUsize::new(0);
95static DEALLOC_COUNT: AtomicUsize = AtomicUsize::new(0);
96static DEALLOC_SUM: AtomicUsize = AtomicUsize::new(0);
97static REALLOC_GROWTH_COUNT: AtomicUsize = AtomicUsize::new(0);
98static REALLOC_GROWTH_SUM: AtomicUsize = AtomicUsize::new(0);
99static REALLOC_SHRINK_COUNT: AtomicUsize = AtomicUsize::new(0);
100static REALLOC_SHRINK_SUM: AtomicUsize = AtomicUsize::new(0);
101static USE_CURR: AtomicUsize = AtomicUsize::new(0);
102static USE_MAX: AtomicUsize = AtomicUsize::new(0);
103
104impl SystemWithStats {
105 pub fn alloc_count(&self) -> usize {
107 ALLOC_COUNT.load(Ordering::Relaxed)
108 }
109
110 pub fn alloc_sum(&self) -> usize {
112 ALLOC_SUM.load(Ordering::Relaxed)
113 }
114
115 pub fn dealloc_count(&self) -> usize {
117 DEALLOC_COUNT.load(Ordering::Relaxed)
118 }
119
120 pub fn dealloc_sum(&self) -> usize {
122 DEALLOC_SUM.load(Ordering::Relaxed)
123 }
124
125 pub fn realloc_growth_count(&self) -> usize {
127 REALLOC_GROWTH_COUNT.load(Ordering::Relaxed)
128 }
129
130 pub fn realloc_growth_sum(&self) -> usize {
132 REALLOC_GROWTH_SUM.load(Ordering::Relaxed)
133 }
134
135 pub fn realloc_shrink_count(&self) -> usize {
137 REALLOC_SHRINK_COUNT.load(Ordering::Relaxed)
138 }
139
140 pub fn realloc_shrink_sum(&self) -> usize {
142 REALLOC_SHRINK_SUM.load(Ordering::Relaxed)
143 }
144
145 pub fn alloc_avg(&self) -> Option<usize> {
147 let sum = ALLOC_SUM.load(Ordering::Relaxed);
148 let count = ALLOC_COUNT.load(Ordering::Relaxed);
149 sum.checked_div(count)
150 }
151
152 pub fn dealloc_avg(&self) -> Option<usize> {
154 let sum = DEALLOC_SUM.load(Ordering::Relaxed);
155 let count = DEALLOC_COUNT.load(Ordering::Relaxed);
156 sum.checked_div(count)
157 }
158
159 pub fn realloc_growth_avg(&self) -> Option<usize> {
161 let sum = REALLOC_GROWTH_SUM.load(Ordering::Relaxed);
162 let count = REALLOC_GROWTH_COUNT.load(Ordering::Relaxed);
163 sum.checked_div(count)
164 }
165
166 pub fn realloc_shrink_avg(&self) -> Option<usize> {
168 let sum = REALLOC_SHRINK_SUM.load(Ordering::Relaxed);
169 let count = REALLOC_SHRINK_COUNT.load(Ordering::Relaxed);
170 sum.checked_div(count)
171 }
172
173 pub fn use_curr(&self) -> usize {
175 USE_CURR.load(Ordering::Relaxed)
176 }
177
178 pub fn use_max(&self) -> usize {
180 USE_MAX.load(Ordering::Relaxed)
181 }
182
183 pub fn reset(&self) {
186 ALLOC_SUM.store(0, Ordering::Relaxed);
187 ALLOC_COUNT.store(0, Ordering::Relaxed);
188 DEALLOC_SUM.store(0, Ordering::Relaxed);
189 DEALLOC_COUNT.store(0, Ordering::Relaxed);
190 REALLOC_GROWTH_COUNT.store(0, Ordering::Relaxed);
191 REALLOC_GROWTH_SUM.store(0, Ordering::Relaxed);
192 REALLOC_SHRINK_COUNT.store(0, Ordering::Relaxed);
193 REALLOC_SHRINK_SUM.store(0, Ordering::Relaxed);
194 USE_MAX.store(self.use_curr(), Ordering::Relaxed);
195 }
196
197 pub fn stats(&self) -> SystemStats {
199 let alloc_count = self.alloc_count();
200 let alloc_sum = self.alloc_sum();
201 let alloc_avg = alloc_sum.checked_div(alloc_count);
202
203 let dealloc_count = self.dealloc_count();
204 let dealloc_sum = self.dealloc_sum();
205 let dealloc_avg = dealloc_sum.checked_div(dealloc_count);
206
207 let realloc_growth_count = self.realloc_growth_count();
208 let realloc_growth_sum = self.realloc_growth_sum();
209 let realloc_growth_avg = realloc_growth_sum.checked_div(realloc_growth_count);
210
211 let realloc_shrink_count = self.realloc_shrink_count();
212 let realloc_shrink_sum = self.realloc_shrink_sum();
213 let realloc_shrink_avg = realloc_shrink_sum.checked_div(realloc_shrink_count);
214
215 let use_curr = self.use_curr();
216 let use_max = self.use_max();
217
218 SystemStats {
219 alloc_count,
220 alloc_avg,
221 dealloc_count,
222 dealloc_avg,
223 realloc_growth_count,
224 realloc_growth_avg,
225 realloc_shrink_count,
226 realloc_shrink_avg,
227 use_curr,
228 use_max,
229 }
230 }
231}
232
233unsafe impl GlobalAlloc for SystemWithStats {
234 unsafe fn alloc(&self, layout: Layout) -> *mut u8 {
235 let ret = System.alloc(layout);
236 if !ret.is_null() {
237 let size = layout.size();
238 ALLOC_SUM.fetch_add(size, Ordering::Relaxed);
239 ALLOC_COUNT.fetch_add(1, Ordering::Relaxed);
240 let curr = USE_CURR.fetch_add(size, Ordering::Relaxed) + size;
241 USE_MAX.fetch_max(curr, Ordering::Relaxed);
242 }
243 ret
244 }
245
246 unsafe fn dealloc(&self, ptr: *mut u8, layout: Layout) {
247 System.dealloc(ptr, layout);
248 let size = layout.size();
249 USE_CURR.fetch_sub(size, Ordering::Relaxed);
250 DEALLOC_SUM.fetch_add(size, Ordering::Relaxed);
251 DEALLOC_COUNT.fetch_add(1, Ordering::Relaxed);
252 }
253
254 unsafe fn realloc(&self, ptr: *mut u8, layout: Layout, new_size: usize) -> *mut u8 {
255 let new_layout = unsafe { Layout::from_size_align_unchecked(new_size, layout.align()) };
256 let new_ptr = unsafe { self.alloc(new_layout) };
257 if !new_ptr.is_null() {
258 if new_size > layout.size() {
259 let diff = new_size - layout.size();
260 REALLOC_GROWTH_COUNT.fetch_add(1, Ordering::Relaxed);
261 REALLOC_GROWTH_SUM.fetch_add(diff, Ordering::Relaxed);
262 } else {
263 let diff = layout.size() - new_size;
264 REALLOC_SHRINK_COUNT.fetch_add(1, Ordering::Relaxed);
265 REALLOC_SHRINK_SUM.fetch_add(diff, Ordering::Relaxed);
266 }
267
268 unsafe {
269 ptr::copy_nonoverlapping(ptr, new_ptr, cmp::min(layout.size(), new_size));
270 self.dealloc(ptr, layout);
271 }
272 }
273 new_ptr
274 }
275}