metrale_gpu_runtime/cuda_backend/
alloc_ledger.rs1use super::MetraleCudaBackend;
12
13#[derive(Clone, Copy)]
17pub(super) struct AllocRecord {
18 pub(super) bytes: usize,
19 pub(super) site: &'static std::panic::Location<'static>,
20}
21
22#[derive(Clone)]
26pub struct LedgerProbe(
27 std::sync::Arc<parking_lot::Mutex<std::collections::HashMap<u64, AllocRecord>>>,
28);
29
30impl LedgerProbe {
31 pub fn live_bytes(&self) -> usize {
33 ledger_bytes(&self.0.lock())
34 }
35}
36
37fn ledger_bytes(ledger: &std::collections::HashMap<u64, AllocRecord>) -> usize {
38 ledger.values().map(|r| r.bytes).sum()
39}
40
41pub const SMALL_ALLOC_CHUNK: u64 = 2 << 20;
46
47pub fn chunk_slack(allocs: impl Iterator<Item = (u64, usize)>, chunk: u64) -> usize {
54 let mut chunks = std::collections::BTreeSet::new();
55 let mut small = 0usize;
56 for (addr, bytes) in allocs.filter(|&(_, b)| (b as u64) < chunk && b > 0) {
57 small += bytes;
58 let last = addr + bytes as u64 - 1;
59 chunks.extend(addr / chunk..=last / chunk);
60 }
61 (chunks.len() as u64 * chunk).saturating_sub(small as u64) as usize
62}
63
64impl MetraleCudaBackend {
65 pub(crate) fn record_alloc(
68 &self,
69 ptr: crate::gpu::DevicePtr,
70 bytes: usize,
71 site: &'static std::panic::Location<'static>,
72 ) {
73 self.live_allocs
74 .lock()
75 .insert(ptr.0, AllocRecord { bytes, site });
76 }
77
78 pub fn live_allocations(&self) -> Vec<(crate::gpu::DevicePtr, usize, String)> {
80 self.live_allocs
81 .lock()
82 .iter()
83 .map(|(&a, r)| {
84 let site = format!("{}:{}", r.site.file(), r.site.line());
85 (crate::gpu::DevicePtr(a), r.bytes, site)
86 })
87 .collect()
88 }
89
90 pub(crate) fn forget_alloc(&self, ptr: crate::gpu::DevicePtr) {
91 self.live_allocs.lock().remove(&ptr.0);
92 }
93
94 pub fn live_bytes(&self) -> usize {
96 ledger_bytes(&self.live_allocs.lock())
97 }
98
99 pub fn chunk_slack_bytes(&self) -> usize {
101 let ledger = self.live_allocs.lock();
102 chunk_slack(ledger.iter().map(|(&a, r)| (a, r.bytes)), SMALL_ALLOC_CHUNK)
103 }
104
105 pub fn ledger_probe(&self) -> LedgerProbe {
107 LedgerProbe(std::sync::Arc::clone(&self.live_allocs))
108 }
109
110 pub fn alloc_report(&self, top_n: usize, min_mb: usize) -> String {
115 use std::collections::HashMap;
116 let mut by_site: HashMap<String, (usize, usize)> = HashMap::new();
117 let mut total = 0usize;
118 for rec in self.live_allocs.lock().values() {
119 total += rec.bytes;
120 let key = format!("{}:{}", rec.site.file(), rec.site.line());
121 let e = by_site.entry(key).or_insert((0, 0));
122 e.0 += rec.bytes;
123 e.1 += 1;
124 }
125 let mut rows: Vec<(String, usize, usize)> =
126 by_site.into_iter().map(|(k, v)| (k, v.0, v.1)).collect();
127 rows.sort_by(|a, b| b.1.cmp(&a.1));
128
129 let mut out = format!(
130 "GPU allocation ledger: {:.2} GB live across {} sites\n",
131 total as f64 / 1e9,
132 rows.len()
133 );
134 let mut shown = 0usize;
135 let mut folded_bytes = 0usize;
136 let mut folded_sites = 0usize;
137 for (site, bytes, count) in rows {
138 if shown < top_n && bytes >= min_mb * 1024 * 1024 {
139 out.push_str(&format!(
140 " {:>9.1} MB x{:<5} {}\n",
141 bytes as f64 / (1024.0 * 1024.0),
142 count,
143 site
144 ));
145 shown += 1;
146 } else {
147 folded_bytes += bytes;
148 folded_sites += 1;
149 }
150 }
151 if folded_sites > 0 {
152 out.push_str(&format!(
153 " {:>9.1} MB across {} smaller sites\n",
154 folded_bytes as f64 / (1024.0 * 1024.0),
155 folded_sites
156 ));
157 }
158
159 let mut by_file: HashMap<&str, (usize, usize)> = HashMap::new();
162 for rec in self.live_allocs.lock().values() {
163 let e = by_file.entry(rec.site.file()).or_insert((0, 0));
164 e.0 += rec.bytes;
165 e.1 += 1;
166 }
167 let mut frows: Vec<(&str, usize, usize)> =
168 by_file.into_iter().map(|(k, v)| (k, v.0, v.1)).collect();
169 frows.sort_by(|a, b| b.1.cmp(&a.1));
170 out.push_str(" ── by file ──\n");
171 for (file, bytes, count) in frows.into_iter().take(top_n) {
172 out.push_str(&format!(
173 " {:>9.1} MB x{:<5} {}\n",
174 bytes as f64 / (1024.0 * 1024.0),
175 count,
176 file
177 ));
178 }
179 out
180 }
181}
182
183#[cfg(test)]
184#[path = "alloc_ledger_tests.rs"]
185mod tests;