use pprof::ProfilerGuardBuilder;
use rustis::resp::{BenchTape, bench_parse_only};
use std::{collections::HashMap, fs::File, hint::black_box};
fn env_usize(key: &str, default: usize) -> usize {
std::env::var(key)
.ok()
.and_then(|v| v.parse().ok())
.unwrap_or(default)
}
fn build_array(n: usize, elem_len: usize) -> Vec<u8> {
let elem = "x".repeat(elem_len);
let mut buf = Vec::new();
buf.extend_from_slice(format!("*{n}\r\n").as_bytes());
for _ in 0..n {
buf.extend_from_slice(format!("${elem_len}\r\n").as_bytes());
buf.extend_from_slice(elem.as_bytes());
buf.extend_from_slice(b"\r\n");
}
buf
}
fn build_nested(rows: usize, cols: usize, elem_len: usize) -> Vec<u8> {
let elem = "x".repeat(elem_len);
let mut buf = Vec::new();
buf.extend_from_slice(format!("*{rows}\r\n").as_bytes());
for _ in 0..rows {
buf.extend_from_slice(format!("*{cols}\r\n").as_bytes());
for _ in 0..cols {
buf.extend_from_slice(format!("${elem_len}\r\n").as_bytes());
buf.extend_from_slice(elem.as_bytes());
buf.extend_from_slice(b"\r\n");
}
}
buf
}
fn main() {
let iters = env_usize("PPROF_ITERS", 150_000);
let hz = env_usize("PPROF_HZ", 4_000) as i32;
let flat = build_array(5_000, 50);
let nested = build_nested(500, 10, 20);
let mut tape = BenchTape::new();
let guard = ProfilerGuardBuilder::default()
.frequency(hz)
.blocklist(&["libc", "libgcc", "pthread", "vdso"])
.build()
.expect("failed to start pprof profiler");
println!("Profiling {iters} parse iterations per shape at {hz} Hz...");
for _ in 0..iters {
bench_parse_only(black_box(&flat), &mut tape);
bench_parse_only(black_box(&nested), &mut tape);
}
let report = guard
.report()
.build()
.expect("failed to build pprof report");
let out_path = "target/resp_parse_flamegraph.svg";
let file = File::create(out_path).expect("failed to create flamegraph file");
report
.flamegraph(file)
.expect("failed to write flamegraph SVG");
println!("Flamegraph written to {out_path}\n");
let mut leaf: HashMap<String, isize> = HashMap::new();
let mut total = 0isize;
for (frames, count) in report.data.iter() {
total += *count;
if let Some(sym) = frames.frames.first().and_then(|f| f.first()) {
*leaf.entry(format!("{sym}")).or_default() += *count;
}
}
let mut rows: Vec<(String, isize)> = leaf.into_iter().collect();
rows.sort_by_key(|(_, c)| std::cmp::Reverse(*c));
println!("=== hottest leaf functions — self time ({total} samples) ===");
for (name, count) in rows.iter().take(30) {
let pct = 100.0 * *count as f64 / total.max(1) as f64;
println!("{pct:6.2}% {count:>7} {name}");
}
}