Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
93 changes: 85 additions & 8 deletions bench.zig
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,32 @@ const time = std.time;

const Decl = std.builtin.TypeInfo.Declaration;

pub fn benchmark(comptime B: type) !void {
pub const Opts = struct {
min_iterations: usize = 10000,
max_iterations: usize = 100000,
max_time: usize = 500 * time.ns_per_ms,
};

pub fn benchmark(type_or_instance: anytype) !void {
switch (@typeInfo(@TypeOf(type_or_instance))) {
.Type => try run(type_or_instance, type_or_instance, null),
.Pointer => |p| try run(p.child, type_or_instance, null),
else => @compileError("Pass a type or a pointer/instance to a struct."),
}
}

pub fn run(comptime B: type, context: anytype, run_opts: ?Opts) !void {
const args = if (@hasDecl(B, "args")) B.args else [_]void{{}};
const arg_names = if (@hasDecl(B, "arg_names")) B.arg_names else [_]u8{};
const min_iterations = if (@hasDecl(B, "min_iterations")) B.min_iterations else 10000;
const max_iterations = if (@hasDecl(B, "max_iterations")) B.max_iterations else 100000;
const max_time = 500 * time.ns_per_ms;

var opts = Opts{};
if (run_opts) |ro| {
opts = ro;
} else {
if (@hasDecl(B, "min_iterations")) opts.min_iterations = B.min_iterations;
if (@hasDecl(B, "max_iterations")) opts.max_iterations = B.max_iterations;
}
opts.max_iterations = math.max(opts.min_iterations, opts.max_iterations);

const functions = comptime blk: {
var res: []const Decl = &[_]Decl{};
Expand Down Expand Up @@ -78,14 +98,14 @@ pub fn benchmark(comptime B: type) !void {
var runtime_sum: u128 = 0;

var i: usize = 0;
while (i < min_iterations or
(i < max_iterations and runtime_sum < max_time)) : (i += 1)
while (i < opts.min_iterations or
(i < opts.max_iterations and runtime_sum < opts.max_time)) : (i += 1)
{
timer.reset();

const res = switch (@TypeOf(arg)) {
void => @field(B, def.name)(),
else => @field(B, def.name)(arg),
void => @field(context, def.name)(),
else => @field(context, def.name)(arg),
};
const runtime = timer.read();
runtime_sum += runtime;
Expand Down Expand Up @@ -244,3 +264,60 @@ test "benchmark generics" {
}
});
}

test "benchmark instance" {
const iterations: usize = 100000;
const args = [_][]const u8{ "01" };
const arg_names = [_][]const u8{ "01 x n" };

const capacity = args[0].len * iterations;
const BoundedArray = std.BoundedArray(u8, capacity);
const ArrayList = std.ArrayList(u8);
const allocator = std.testing.allocator;

var instance = (struct {
const args = args;
const arg_names = arg_names;
const min_iterations = iterations;
const max_iterations = iterations;

const Self = @This();
bounded_array: BoundedArray,
non_resizing_list: ArrayList,
resizing_list: ArrayList,

fn bounded_array_append_slice(self: *Self, data: []const u8) !void {
try self.bounded_array.appendSlice(data);
}
fn non_resizing_list_append_slice(self: *Self, data: []const u8) !void {
try self.non_resizing_list.appendSlice(data);
}
fn resizing_list_append_slice(self: *Self, data: []const u8) !void {
try self.resizing_list.appendSlice(data);
}
} {
.bounded_array = .{ .buffer = undefined, .len = 0 },
.non_resizing_list = ArrayList.init(allocator),
.resizing_list = ArrayList.init(allocator),
});
defer {
instance.non_resizing_list.deinit();
instance.resizing_list.deinit();
}

try instance.non_resizing_list.ensureTotalCapacity(capacity);

try benchmark(&instance);

// reset
instance.bounded_array.len = 0;
instance.non_resizing_list.clearRetainingCapacity();
instance.resizing_list.clearAndFree();

// run with fewer iterations
const fewer_iterations = iterations / 2;
try run(@TypeOf(instance), &instance, .{
.min_iterations = fewer_iterations,
.max_iterations = fewer_iterations,
});
}