Simple benchmarking function.

const HAYSTACK = "abcdefghijklmnopqrstvuwxyz0123456789";

pub fn main() !void {
	(try zul.benchmark.run(indexOfScalar, .{})).print("indexOfScalar");
	(try zul.benchmark.run(lastIndexOfScalar, .{})).print("lastIndexOfScalar");
}

fn indexOfScalar(_: Allocator, _: *std.time.Timer) !void {
	const i = std.mem.indexOfScalar(u8, HAYSTACK, '9').?;
	if (i != 35) {
		@panic("fail");
	}
}

fn lastIndexOfScalar(_: Allocator, _: *std.time.Timer) !void {
	const i = std.mem.lastIndexOfScalar(u8, HAYSTACK, 'a').?;
	if (i != 0) {
		@panic("fail");
	}
}

// indexOfScalar
//   49882322 iterations   59.45ns per iterations
//   worst: 167ns  median: 42ns    stddev: 20.66ns
//
// lastIndexOfScalar
//   20993066 iterations   142.15ns per iterations
//   worst: 292ns  median: 125ns   stddev: 23.13ns

allocator: std.mem.Allocator

Provided for any allocation the function must make. When used, the Result will contain the requested_bytes.

timer: *std.time.Timer

In some cases, the function under benchmark might require setup that should not count towards the execution time. Use timer.reset() to reset the execution time to 0.

fn myfunc(_: Allocator, timer: *std.time.Timer) !void {
	// some expensive setup
	timer.reset();
	// code to benchmark
}

In most cases, it is better to use runC and provide a context.

opts: zul.benchmark.Opts

Options that control how the benchmark is run. Must be comptime-known.

  • samples: u32 - The maximum number of samples to take for calculating metrics. Defaults to 10_000
  • runtime: usize - The time, in milliseconds, to run the benchmark for. Defaults ot 3000 (3 seconds).

variant

A variant of run that passes arbitrary data to the benchmark function. For example, rather than relying on a global INPUT, our above example could leverage runC:

pub fn main() !void {
	const ctx = Context{
		.input = "abcdefghijklmnopqrstvuwxyz0123456789",
	};

	(try zul.benchmark.runC(ctx, indexOfScalar, .{})).print("indexOfScalar");
	(try zul.benchmark.runC(ctx, lastIndexOfScalar, .{})).print("lastIndexOfScalar");
}

const Context = struct{
	input: []const u8,
};

fn indexOfScalar(ctx: Context, _: Allocator, _: *std.time.Timer) !void {
	const i = std.mem.indexOfScalar(u8, ctx.input, '9').?;
	if (i != 35) {
		@panic("fail");
	}
}

fn lastIndexOfScalar(ctx: Context, _: Allocator, _: *std.time.Timer) !void {
	const i = std.mem.lastIndexOfScalar(u8, ctx.input, 'a').?;
	if (i != 0) {
		@panic("fail");
	}
}

A zul.benchmark.Result(sample_size) is returned by the call to run or runC.

total: u64

Total time, in nanosecond, that the benchmark ran for. This can be greater than the sum of values in samples().

iterations: u64

Number of times the benchmarked function was called. This can be greater than samples().len.

requested_bytes: usize

Total number of bytes allocated by the allocator.

Outputs a summary to stderr (using std.debug.print)

Returns a sorted list of sample. The value is the time each sample took in nanoseconds.

Returns the worst (slowest) sample time.

Returns the mean of samples().

Returns the median of samples().

Returns the stdDev of samples().