pprof-alloc 0.2.0

Allocation profiling and Linux memory telemetry for Rust services.
Documentation
use std::hint::black_box;
use std::time::{Duration, Instant};

#[global_allocator]
static GLOBAL: pprof_alloc::PprofAlloc = pprof_alloc::PprofAlloc::new().with_pprof();

const WARMUP_ITERS: usize = 25_000;
const TINY_BOX_ITERS: usize = 250_000;
const MEDIUM_VEC_ITERS: usize = 100_000;
const REALLOC_ITERS: usize = 60_000;

fn main() {
	println!("capture_mode={:?}", pprof_alloc::capture_mode());
	println!("Build with RUSTFLAGS=-Cforce-frame-pointers=yes for correct unwinding.");
	println!();

	warmup();

	run_case("tiny_box_churn", TINY_BOX_ITERS, tiny_box_churn);
	run_case("medium_vec_churn", MEDIUM_VEC_ITERS, medium_vec_churn);
	run_case("realloc_churn", REALLOC_ITERS, realloc_churn);

	let stats = pprof_alloc::allocation_stats();
	println!();
	println!(
		"allocation_stats allocated={} freed={} in_use_bytes={}",
		stats.allocated,
		stats.freed,
		stats.in_use_bytes()
	);
}

fn warmup() {
	for i in 0..WARMUP_ITERS {
		tiny_box_churn(i);
	}
}

fn run_case(name: &str, iterations: usize, mut func: impl FnMut(usize)) {
	let start = Instant::now();
	for i in 0..iterations {
		func(i);
	}
	let elapsed = start.elapsed();
	let ns_per_op = elapsed.as_nanos() as f64 / iterations as f64;
	let ops_per_sec = iterations as f64 / elapsed.as_secs_f64();
	println!(
		"{name} iterations={iterations} elapsed={} ns_per_op={ns_per_op:.1} ops_per_sec={ops_per_sec:.0}",
		fmt_duration(elapsed),
	);
}

fn tiny_box_churn(i: usize) {
	let value = black_box(Box::new([i as u64; 8]));
	black_box(value[0]);
}

fn medium_vec_churn(i: usize) {
	let len = 256 + (i % 512);
	let mut data = Vec::with_capacity(len);
	for j in 0..len {
		data.push(((i + j) & 0xff) as u8);
	}
	black_box(data.len());
	black_box(data.as_ptr());
}

fn realloc_churn(i: usize) {
	let mut data = Vec::with_capacity(8);
	for j in 0..(64 + (i % 64)) {
		data.push(((i ^ j) & 0xff) as u8);
	}
	black_box(data.capacity());
}

fn fmt_duration(duration: Duration) -> String {
	if duration.as_secs() > 0 {
		format!("{:.3}s", duration.as_secs_f64())
	} else if duration.as_millis() > 0 {
		format!("{:.3}ms", duration.as_secs_f64() * 1_000.0)
	} else {
		format!("{}us", duration.as_micros())
	}
}