blob: 88cb170de87e0d541899a469e36781196e6e0938 [file] [edit]
// Copyright 2026 the Vello Authors
// SPDX-License-Identifier: Apache-2.0 OR MIT
mod counter;
mod report;
pub mod tiger;
pub use counter::{AllocationStats, CountingAllocator, measure};
use report::{BenchmarkReport, BenchmarkResult};
const DEFAULT_MEASURED_FRAMES: usize = 1;
#[derive(Clone, Copy, Debug)]
pub struct AllocationBenchmark {
name: &'static str,
measure: fn(usize) -> AllocationStats,
limits_per_frame: AllocationLimits,
}
impl AllocationBenchmark {
pub const fn new(
name: &'static str,
measure: fn(usize) -> AllocationStats,
limits_per_frame: AllocationLimits,
) -> Self {
Self {
name,
measure,
limits_per_frame,
}
}
fn run(self, measured_frames: usize) -> BenchmarkResult {
BenchmarkResult {
measured_frames,
name: self.name,
stats: (self.measure)(measured_frames),
limits: self.limits_per_frame.for_frames(measured_frames),
}
}
}
#[derive(Clone, Copy, Debug)]
pub struct AllocationLimits {
allocations: usize,
reallocations: usize,
allocated_bytes: usize,
additional_peak_bytes: usize,
retained_bytes: isize,
}
impl AllocationLimits {
pub const ZERO: Self = Self::new(0, 0, 0, 0, 0);
pub const fn new(
allocations: usize,
reallocations: usize,
allocated_bytes: usize,
additional_peak_bytes: usize,
retained_bytes: isize,
) -> Self {
Self {
allocations,
reallocations,
allocated_bytes,
additional_peak_bytes,
retained_bytes,
}
}
fn for_frames(self, frames: usize) -> Self {
Self {
allocations: self.allocations.checked_mul(frames).unwrap(),
reallocations: self.reallocations.checked_mul(frames).unwrap(),
allocated_bytes: self.allocated_bytes.checked_mul(frames).unwrap(),
additional_peak_bytes: self.additional_peak_bytes,
retained_bytes: self.retained_bytes,
}
}
}
pub fn run(benchmarks: &[AllocationBenchmark]) {
let measured_frame_counts = measured_frame_counts();
let mut results = Vec::with_capacity(measured_frame_counts.len() * benchmarks.len());
for measured_frames in measured_frame_counts {
results.extend(
benchmarks
.iter()
.map(|benchmark| benchmark.run(measured_frames)),
);
}
let report = BenchmarkReport::new(results);
report.print();
if report.has_failures() {
std::process::exit(1);
}
}
fn measured_frame_counts() -> Vec<usize> {
let mut measured_frame_counts = Vec::new();
let mut args = std::env::args().skip(1).peekable();
while let Some(arg) = args.next() {
if arg == "--frames" {
let initial_count = measured_frame_counts.len();
while args.peek().is_some_and(|arg| !arg.starts_with('-')) {
parse_frame_counts(&args.next().unwrap(), &mut measured_frame_counts);
}
assert!(
measured_frame_counts.len() > initial_count,
"expected at least one frame count after `--frames`"
);
} else if let Some(value) = arg.strip_prefix("--frames=") {
parse_frame_counts(value, &mut measured_frame_counts);
}
}
if measured_frame_counts.is_empty() {
measured_frame_counts.push(DEFAULT_MEASURED_FRAMES);
}
measured_frame_counts
}
fn parse_frame_counts(value: &str, measured_frame_counts: &mut Vec<usize>) {
for value in value.split(',').filter(|value| !value.is_empty()) {
let measured_frames = value
.parse()
.expect("frame count must be a positive integer");
assert!(measured_frames > 0, "frame count must be greater than zero");
if !measured_frame_counts.contains(&measured_frames) {
measured_frame_counts.push(measured_frames);
}
}
}