makepad/libs/relax/base/examples/burst_probe.rs

59 lines
2.1 KiB
Rust

//! Synthetic malloc-fragmentation probe (separate processes for ON/OFF):
//! CARGO_INCREMENTAL=0 cargo run --release -p relax-base --example burst_probe
//! MAKEPAD_RUN_BURST_ARENA=0 CARGO_INCREMENTAL=0 cargo run --release -p relax-base --example burst_probe
//! Allocates 240 MB of transient blocks interleaved with same-sized long-lived heap blocks.
//! It intentionally leaves one arena block per class escaping until the last measurement.
//! This measures allocator behavior, not Amp or compiler throughput.
use relax_base::{memstat, zone_alloc};
use std::time::Instant;
#[global_allocator]
static ALLOC: memstat::Counting = memstat::Counting;
fn snapshot(label: &str) {
println!("{}", memstat::report(label).lines().next().unwrap());
}
fn main() {
let runtime = relax_base::options::RuntimeOptions::from_env();
let mut transient = Vec::with_capacity(2_100_000);
let mut anchors = Vec::with_capacity(17000);
let mut escaped = Vec::with_capacity(15);
snapshot("before");
let start = Instant::now();
for shift in 4..=18 {
let size = 1 << shift;
let count = (16 << 20) / size;
for batch in (0..count).step_by(128) {
// Persistent allocations on the ordinary heap, interleaved with compile bursts.
anchors.push(vec![0x1a_u8; size]);
let _phase = zone_alloc::compiler();
let _scope = zone_alloc::job("probe", runtime.burst_arena);
for _ in batch..(batch + 128).min(count) {
transient.push(vec![0x6b_u8; size]);
}
}
escaped.push(transient.pop().unwrap());
}
let allocated = start.elapsed();
snapshot("peak");
let start = Instant::now();
drop(transient);
let freed = start.elapsed();
snapshot("burst freed (15 escaped blocks)");
for block in &escaped {
assert!(block.iter().all(|&b| b == 0x6b));
}
drop(escaped);
snapshot("escapes freed");
println!(
"allocate_ms={}, free_ms={}",
allocated.as_millis(),
freed.as_millis()
);
std::hint::black_box(&anchors);
drop(anchors);
memstat::trim();
snapshot("anchors freed and malloc trimmed");
}