pub struct ModAlloc { /* private fields */ }Expand description
Global allocator wrapper that tracks allocations.
Install as #[global_allocator] to enable tracking. The wrapper
forwards every allocation, deallocation, reallocation, and
zero-initialised allocation to std::alloc::System and records
the event in four lock-free AtomicU64 counters.
§Example
use mod_alloc::ModAlloc;
#[global_allocator]
static GLOBAL: ModAlloc = ModAlloc::new();
fn main() {
let v: Vec<u8> = vec![0; 1024];
let stats = GLOBAL.snapshot();
assert!(stats.alloc_count >= 1);
drop(v);
}Implementations§
Source§impl ModAlloc
impl ModAlloc
Sourcepub const fn new() -> Self
pub const fn new() -> Self
Construct a new ModAlloc allocator wrapper.
All counters start at zero. This function is const, which
allows construction in a static for use as
#[global_allocator].
§Example
use mod_alloc::ModAlloc;
static GLOBAL: ModAlloc = ModAlloc::new();
let stats = GLOBAL.snapshot();
assert_eq!(stats.alloc_count, 0);Examples found in repository?
More examples
Sourcepub fn snapshot(&self) -> AllocStats
pub fn snapshot(&self) -> AllocStats
Snapshot the current counter values.
Each counter is read independently with Relaxed ordering;
the resulting AllocStats is a coherent best-effort view
but does not represent a single atomic moment in time. For
scoped measurement, prefer Profiler.
§Example
use mod_alloc::ModAlloc;
let alloc = ModAlloc::new();
let stats = alloc.snapshot();
assert_eq!(stats.alloc_count, 0);Examples found in repository?
26fn main() {
27 for _ in 0..WARMUP {
28 let v: Vec<u8> = Vec::with_capacity(SIZE);
29 std::hint::black_box(&v);
30 }
31
32 GLOBAL.reset();
33 let start = Instant::now();
34 for _ in 0..N {
35 let v: Vec<u8> = Vec::with_capacity(SIZE);
36 std::hint::black_box(&v);
37 }
38 let elapsed = start.elapsed();
39
40 let snap = GLOBAL.snapshot();
41 let per_cycle_ns = elapsed.as_nanos() as f64 / N as f64;
42
43 println!("bench_overhead:");
44 println!(" iterations: {N}");
45 println!(" allocation size: {SIZE} bytes");
46 println!(" elapsed: {elapsed:?}");
47 println!(" per alloc+dealloc: {per_cycle_ns:.1} ns");
48 println!();
49 println!("counter snapshot after run:");
50 println!(" alloc_count: {}", snap.alloc_count);
51 println!(" total_bytes: {}", snap.total_bytes);
52 println!(" current_bytes: {}", snap.current_bytes);
53 println!(" peak_bytes: {}", snap.peak_bytes);
54}More examples
10fn main() {
11 let p = Profiler::start();
12
13 let v: Vec<u64> = (0..1_000).collect();
14 let sum: u64 = v.iter().sum();
15 drop(v);
16
17 let mut owned: Vec<String> = Vec::with_capacity(100);
18 for i in 0..100 {
19 owned.push(format!("item-{i}"));
20 }
21 drop(owned);
22
23 let delta = p.stop();
24 println!("Profiler delta (alloc/total/current = delta; peak = absolute):");
25 println!(" alloc_count: {}", delta.alloc_count);
26 println!(" total_bytes: {}", delta.total_bytes);
27 println!(" current_bytes: {}", delta.current_bytes);
28 println!(" peak_bytes: {}", delta.peak_bytes);
29
30 let snap = GLOBAL.snapshot();
31 println!();
32 println!("Process-wide snapshot:");
33 println!(" alloc_count: {}", snap.alloc_count);
34 println!(" total_bytes: {}", snap.total_bytes);
35 println!(" current_bytes: {}", snap.current_bytes);
36 println!(" peak_bytes: {}", snap.peak_bytes);
37
38 println!();
39 println!("(workload checksum: {sum})");
40}37fn main() {
38 for _ in 0..1_000 {
39 alloc_small();
40 }
41 for _ in 0..100 {
42 alloc_medium();
43 }
44 for _ in 0..10 {
45 alloc_large();
46 }
47
48 let snap = GLOBAL.snapshot();
49 println!("Process-wide snapshot:");
50 println!(" alloc_count: {}", snap.alloc_count);
51 println!(" total_bytes: {}", snap.total_bytes);
52 println!(" current_bytes: {}", snap.current_bytes);
53 println!(" peak_bytes: {}", snap.peak_bytes);
54 println!();
55
56 let mut sites = GLOBAL.call_sites();
57 sites.sort_by_key(|s| std::cmp::Reverse(s.total_bytes));
58
59 println!("Top 10 call sites by total bytes:");
60 println!(
61 "{:>10} {:>14} {:>4} {:>18}",
62 "count", "total_bytes", "frm", "top frame"
63 );
64 for (rank, site) in sites.iter().take(10).enumerate() {
65 println!(
66 "{rank:>2}: {count:>6} {bytes:>14} {frm:>4} {top:#018x}",
67 rank = rank,
68 count = site.count,
69 bytes = site.total_bytes,
70 frm = site.frame_count,
71 top = site.frames[0],
72 );
73 }
74}38fn main() {
39 for _ in 0..500 {
40 alloc_small();
41 }
42 for _ in 0..100 {
43 alloc_medium();
44 }
45 for _ in 0..10 {
46 alloc_large();
47 }
48
49 let mut report = GLOBAL.symbolicated_report();
50 report.sort_by_key(|s| std::cmp::Reverse(s.total_bytes));
51
52 let snap = GLOBAL.snapshot();
53 println!("Process-wide:");
54 println!(
55 " {} allocations, {} total bytes, peak {} bytes",
56 snap.alloc_count, snap.total_bytes, snap.peak_bytes
57 );
58 println!();
59
60 println!("Top call sites by total bytes:");
61 println!(
62 "{:>2} {:>10} {:>14} top frame",
63 "#", "count", "total_bytes"
64 );
65 for (rank, site) in report.iter().take(10).enumerate() {
66 let top = &site.frames[0];
67 let name = top.function.as_deref().unwrap_or("<unresolved>");
68 let loc = match (top.file.as_ref(), top.line) {
69 (Some(f), Some(l)) => format!(" {}:{}", f.display(), l),
70 _ => String::new(),
71 };
72 println!(
73 "{rank:>2} {count:>10} {bytes:>14} {name}{loc}",
74 rank = rank,
75 count = site.count,
76 bytes = site.total_bytes,
77 name = name,
78 loc = loc,
79 );
80 // Show inlined expansions if any.
81 for inlined in site.frames.iter().skip(1).take_while(|f| f.inlined) {
82 let n = inlined.function.as_deref().unwrap_or("<unresolved>");
83 println!(" inlined: {n}");
84 }
85 }
86}Sourcepub fn reset(&self)
pub fn reset(&self)
Reset all counters to zero.
Intended for use at the start of a profile run, before any
outstanding allocations exist. Calling reset while
allocations are live can cause current_bytes to wrap on
subsequent deallocations; the other counters are unaffected.
§Example
use mod_alloc::ModAlloc;
let alloc = ModAlloc::new();
alloc.reset();
let stats = alloc.snapshot();
assert_eq!(stats.alloc_count, 0);Examples found in repository?
26fn main() {
27 for _ in 0..WARMUP {
28 let v: Vec<u8> = Vec::with_capacity(SIZE);
29 std::hint::black_box(&v);
30 }
31
32 GLOBAL.reset();
33 let start = Instant::now();
34 for _ in 0..N {
35 let v: Vec<u8> = Vec::with_capacity(SIZE);
36 std::hint::black_box(&v);
37 }
38 let elapsed = start.elapsed();
39
40 let snap = GLOBAL.snapshot();
41 let per_cycle_ns = elapsed.as_nanos() as f64 / N as f64;
42
43 println!("bench_overhead:");
44 println!(" iterations: {N}");
45 println!(" allocation size: {SIZE} bytes");
46 println!(" elapsed: {elapsed:?}");
47 println!(" per alloc+dealloc: {per_cycle_ns:.1} ns");
48 println!();
49 println!("counter snapshot after run:");
50 println!(" alloc_count: {}", snap.alloc_count);
51 println!(" total_bytes: {}", snap.total_bytes);
52 println!(" current_bytes: {}", snap.current_bytes);
53 println!(" peak_bytes: {}", snap.peak_bytes);
54}Sourcepub fn call_sites(&self) -> Vec<CallSiteStats>
Available on crate feature backtraces only.
pub fn call_sites(&self) -> Vec<CallSiteStats>
backtraces only.Drain the per-call-site aggregation table into a Vec.
Available only with the backtraces cargo feature. The
returned vector contains one CallSiteStats per unique
call site observed since the table was first written. Each
row carries up to 8 raw return addresses (top of stack
first), the number of allocations attributed to that site,
and the total bytes.
Symbolication (resolving addresses to function names)
lands in v0.9.2. This method exposes raw addresses only.
§Example
use mod_alloc::ModAlloc;
#[global_allocator]
static GLOBAL: ModAlloc = ModAlloc::new();
let _v: Vec<u8> = vec![0; 1024];
for site in GLOBAL.call_sites() {
println!("{} allocs, {} bytes at {:#x}",
site.count, site.total_bytes, site.frames[0]);
}Examples found in repository?
37fn main() {
38 for _ in 0..1_000 {
39 alloc_small();
40 }
41 for _ in 0..100 {
42 alloc_medium();
43 }
44 for _ in 0..10 {
45 alloc_large();
46 }
47
48 let snap = GLOBAL.snapshot();
49 println!("Process-wide snapshot:");
50 println!(" alloc_count: {}", snap.alloc_count);
51 println!(" total_bytes: {}", snap.total_bytes);
52 println!(" current_bytes: {}", snap.current_bytes);
53 println!(" peak_bytes: {}", snap.peak_bytes);
54 println!();
55
56 let mut sites = GLOBAL.call_sites();
57 sites.sort_by_key(|s| std::cmp::Reverse(s.total_bytes));
58
59 println!("Top 10 call sites by total bytes:");
60 println!(
61 "{:>10} {:>14} {:>4} {:>18}",
62 "count", "total_bytes", "frm", "top frame"
63 );
64 for (rank, site) in sites.iter().take(10).enumerate() {
65 println!(
66 "{rank:>2}: {count:>6} {bytes:>14} {frm:>4} {top:#018x}",
67 rank = rank,
68 count = site.count,
69 bytes = site.total_bytes,
70 frm = site.frame_count,
71 top = site.frames[0],
72 );
73 }
74}Sourcepub fn symbolicated_report(&self) -> Vec<SymbolicatedCallSite>
Available on crate feature symbolicate only.
pub fn symbolicated_report(&self) -> Vec<SymbolicatedCallSite>
symbolicate only.Drain the per-call-site table and symbolicate each frame against the running binary’s own debug info.
Available only with the symbolicate cargo feature, which
also implies backtraces. Returns one
SymbolicatedCallSite per unique call site, each
carrying resolved function names plus (where available)
source file and line.
Allocates. Safe to call from non-allocator contexts only (ordinary user code outside the global-allocator hook).
Results are cached per-address across calls.
§Example
use mod_alloc::ModAlloc;
#[global_allocator]
static GLOBAL: ModAlloc = ModAlloc::new();
let _v: Vec<u8> = vec![0; 1024];
for site in GLOBAL.symbolicated_report() {
let top = &site.frames[0];
println!("{} allocs / {} bytes at {}",
site.count,
site.total_bytes,
top.function.as_deref().unwrap_or("<unresolved>"));
}Examples found in repository?
38fn main() {
39 for _ in 0..500 {
40 alloc_small();
41 }
42 for _ in 0..100 {
43 alloc_medium();
44 }
45 for _ in 0..10 {
46 alloc_large();
47 }
48
49 let mut report = GLOBAL.symbolicated_report();
50 report.sort_by_key(|s| std::cmp::Reverse(s.total_bytes));
51
52 let snap = GLOBAL.snapshot();
53 println!("Process-wide:");
54 println!(
55 " {} allocations, {} total bytes, peak {} bytes",
56 snap.alloc_count, snap.total_bytes, snap.peak_bytes
57 );
58 println!();
59
60 println!("Top call sites by total bytes:");
61 println!(
62 "{:>2} {:>10} {:>14} top frame",
63 "#", "count", "total_bytes"
64 );
65 for (rank, site) in report.iter().take(10).enumerate() {
66 let top = &site.frames[0];
67 let name = top.function.as_deref().unwrap_or("<unresolved>");
68 let loc = match (top.file.as_ref(), top.line) {
69 (Some(f), Some(l)) => format!(" {}:{}", f.display(), l),
70 _ => String::new(),
71 };
72 println!(
73 "{rank:>2} {count:>10} {bytes:>14} {name}{loc}",
74 rank = rank,
75 count = site.count,
76 bytes = site.total_bytes,
77 name = name,
78 loc = loc,
79 );
80 // Show inlined expansions if any.
81 for inlined in site.frames.iter().skip(1).take_while(|f| f.inlined) {
82 let n = inlined.function.as_deref().unwrap_or("<unresolved>");
83 println!(" inlined: {n}");
84 }
85 }
86}Trait Implementations§
Source§impl GlobalAlloc for ModAlloc
impl GlobalAlloc for ModAlloc
Source§unsafe fn alloc(&self, layout: Layout) -> *mut u8
unsafe fn alloc(&self, layout: Layout) -> *mut u8
layout. Read moreSource§unsafe fn alloc_zeroed(&self, layout: Layout) -> *mut u8
unsafe fn alloc_zeroed(&self, layout: Layout) -> *mut u8
alloc, but also ensures that the contents
are set to zero before being returned. Read more