#![allow(clippy::expect_used)]
use criterion::{BenchmarkId, Criterion, black_box, criterion_group, criterion_main};
use tempfile::TempDir;
#[cfg(unix)]
use pprof::ProfilerGuardBuilder;
fn cpu_workload(iterations: u64) -> u64 {
let mut sum: u64 = 0;
for i in 0..iterations {
sum = sum.wrapping_add(i.wrapping_mul(i));
if sum.is_multiple_of(1000) {
sum = sum.wrapping_add(1);
}
}
sum
}
fn fib(n: u64) -> u64 {
if n <= 1 { n } else { fib(n - 1) + fib(n - 2) }
}
#[cfg(unix)]
fn bench_profiler_start(c: &mut Criterion) {
c.bench_function("profiler_start", |b| {
b.iter(|| {
let guard = ProfilerGuardBuilder::default()
.frequency(100)
.blocklist(&["libc", "libgcc", "pthread", "vdso"])
.build()
.expect("Failed to start profiler");
black_box(guard)
});
});
}
#[cfg(unix)]
fn bench_profiler_stop_and_flamegraph(c: &mut Criterion) {
let temp_dir = TempDir::new().expect("Failed to create temp dir");
c.bench_function("profiler_stop_flamegraph", |b| {
b.iter_custom(|iters| {
let mut total = std::time::Duration::ZERO;
for i in 0..iters {
let guard = ProfilerGuardBuilder::default()
.frequency(100)
.blocklist(&["libc", "libgcc", "pthread", "vdso"])
.build()
.expect("Failed to start profiler");
black_box(cpu_workload(1000));
let start = std::time::Instant::now();
let report = guard.report().build().expect("Failed to build report");
let output_path = temp_dir.path().join(format!("bench_{}.svg", i));
let file = std::fs::File::create(&output_path).expect("Failed to create file");
let mut writer = std::io::BufWriter::new(file);
report
.flamegraph(&mut writer)
.expect("Failed to write flamegraph");
total += start.elapsed();
let _ = std::fs::remove_file(&output_path);
}
total
});
});
}
#[cfg(unix)]
fn bench_profiling_overhead(c: &mut Criterion) {
let mut group = c.benchmark_group("profiling_overhead");
for size in [10_000u64, 100_000, 1_000_000].iter() {
group.bench_with_input(
BenchmarkId::new("without_profiler", size),
size,
|b, &size| {
b.iter(|| black_box(cpu_workload(size)));
},
);
group.bench_with_input(BenchmarkId::new("with_profiler", size), size, |b, &size| {
b.iter(|| {
let guard = ProfilerGuardBuilder::default()
.frequency(100)
.blocklist(&["libc", "libgcc", "pthread", "vdso"])
.build()
.expect("Failed to start profiler");
let result = black_box(cpu_workload(size));
drop(guard);
result
});
});
}
group.finish();
}
#[cfg(unix)]
fn bench_flamegraph_sizes(c: &mut Criterion) {
let temp_dir = TempDir::new().expect("Failed to create temp dir");
let mut group = c.benchmark_group("flamegraph_generation");
for fib_n in [20u64, 25, 30].iter() {
group.bench_with_input(BenchmarkId::new("fib_depth", fib_n), fib_n, |b, &fib_n| {
b.iter_custom(|iters| {
let mut total = std::time::Duration::ZERO;
for i in 0..iters {
let guard = ProfilerGuardBuilder::default()
.frequency(1000) .blocklist(&["libc", "libgcc", "pthread", "vdso"])
.build()
.expect("Failed to start profiler");
black_box(fib(fib_n));
let start = std::time::Instant::now();
let report = guard.report().build().expect("Failed to build report");
let output_path = temp_dir.path().join(format!("fib_{}_{}.svg", fib_n, i));
let file = std::fs::File::create(&output_path).expect("Failed to create file");
let mut writer = std::io::BufWriter::new(file);
report
.flamegraph(&mut writer)
.expect("Failed to write flamegraph");
total += start.elapsed();
let _ = std::fs::remove_file(&output_path);
}
total
});
});
}
group.finish();
}
#[cfg(unix)]
criterion_group!(
benches,
bench_profiler_start,
bench_profiler_stop_and_flamegraph,
bench_profiling_overhead,
bench_flamegraph_sizes,
);
#[cfg(unix)]
criterion_main!(benches);
#[cfg(not(unix))]
fn main() {
eprintln!("Benchmarks only supported on Unix platforms (macOS, Linux)");
std::process::exit(1);
}