Wado

core:benchmark

Benchmark utilities: lazy headers, timed phases, throughput reports.

Every phase reports a throughput figure (work done per second, higher is better) with the elapsed time retained alongside it. The throughput unit is chosen so the number is meaningful for the workload:

A benchmark program is an export fn run() with (Stdout, MonotonicClock) called from a test block, so one file both runs under wado run and is checked by wado test; benchmark/ holds the real ones.

Synopsis

let mut bench = Benchmark { name: "sum" };
// A real benchmark leaves `target_ms` alone and repeats each phase until
// it has timed enough of them; one iteration keeps this example quick.
bench.target_ms = 0;
// A natural unit of work reports a work rate. Without one a phase reports
// ops/s, or MB/s when `input_size` is set instead.
bench.work_per_iter = Option::Some(100.0);
bench.unit = "numbers";

let total = bench.run("1..=100", || {
    let mut n = 0;
    for let i of 1..=100 {
        n += i;
    }
    return n;
});
assert total == 5050;

// One `run` per phase; each reports its own line.
let doubled = bench.run("double", || total * 2);
assert doubled == 10100;

Functions

pub fn format_rate(total_work: f64, elapsed_ns: u64, unit: String) -> String

Format a throughput figure: total_work units done over elapsed_ns nanoseconds, as a per-second rate. The byte unit ("B") renders with KB/MB/GB scaling; any other unit uses SI k/M/G prefixes. A zero elapsed time yields a 0.00 rate instead of dividing by zero.

Structs

pub struct Benchmark

A benchmark run. The driver prints a header on the first measurement, then a labeled line per run() phase reporting throughput (work per second) with the per-iteration time retained alongside it.

name: String

iterations: i32

Number of timed iterations. 0 (the default) auto-calibrates the iteration count so the timed loop runs for about target_ms milliseconds; the total elapsed time is then meaningless (~target_ms) so only the per-iteration figure and throughput are reported. Set a positive value to run a fixed iteration count instead.

target_ms: i32

Wall-clock budget in milliseconds for auto-calibration (used when iterations == 0). The loop grows the iteration count until the timed region reaches this duration. 0 runs exactly one iteration.

warmup: i32

Number of unmeasured warmup runs for fixed mode. -1 (the default) means "auto": 1 warmup if iterations >= 2, otherwise 0. Auto mode always performs one warmup before calibration.

input_size: Option<i32>

Optional input size in bytes, shown in the header (e.g. "631044 bytes"). When set and work_per_iter is unset, throughput is reported as a byte rate (MB/s).

work_per_iter: Option<f64>

Units of work performed per iteration, for the throughput figure. null (the default) means "auto": byte rate from input_size if it is set, otherwise iterations per second. Set this to report a custom work-rate (e.g. 5_000_000.0 conversions per iteration).

unit: String

Unit label for the throughput figure when work_per_iter is set (e.g. "conversions", "px", "numbers"). Ignored when throughput falls back to a byte rate or to ops/s.

pub fn run<T>(&mut self, label: String, mut f: fn mut() -> T) -> T with (Stdout, MonotonicClock)

Run a single phase and report its throughput.

In auto mode (iterations == 0) the iteration count is calibrated so the timed loop runs for about target_ms milliseconds. In fixed mode (iterations > 0) it performs warmup unmeasured runs (the -1 default auto-derives to 1 when iterations >= 2, otherwise 0) then exactly iterations timed runs. Prints the header on first call. Returns the final timed iteration's value.

label may be empty for an unlabeled single-phase benchmark.

pub fn println(&mut self, msg: String) with Stdout

Print an indented note line. Useful for accumulator values or final counts alongside the timing output.