Skip to content

Commit

Permalink
Add an incremental API for running benchmarks
Browse files Browse the repository at this point in the history
Allows to get results as they become available and show a progress bar type
display, and allows to limit memory use by freeing results when they're
received.

Changed run() to return `!void` and got rid of the `Results` struct, it's not
really needed. If people want the collection of `Result` values they can easily
get them with `iterator()`.

Split hooks and system info into their own sample programs
  • Loading branch information
bens committed Mar 19, 2024
1 parent c0d82a0 commit 87b5402
Show file tree
Hide file tree
Showing 10 changed files with 262 additions and 102 deletions.
3 changes: 3 additions & 0 deletions build.zig
Original file line number Diff line number Diff line change
Expand Up @@ -60,8 +60,11 @@ fn setupExamples(b: *std.Build, target: std.zig.CrossTarget, optimize: std.built
const example_names = [_][]const u8{
"basic",
"bubble_sort",
"hooks",
"parameterised",
"progress",
"sleep",
"systeminfo",
};

for (example_names) |example_name| {
Expand Down
42 changes: 7 additions & 35 deletions examples/basic.zig
Original file line number Diff line number Diff line change
@@ -1,47 +1,19 @@
const std = @import("std");
const zbench = @import("zbench");
const test_allocator = std.testing.allocator;

fn helloWorld() []const u8 {
fn myBenchmark(_: std.mem.Allocator) void {
var result: usize = 0;
var i: usize = 0;
while (i < 1000) : (i += 1) {
const square = i * i;
result += square;
for (0..1_000_000) |i| {
std.mem.doNotOptimizeAway(i);
result += i * i;
}

return "Hello, world!";
}

fn beforeAllHook() void {
std.debug.print("Starting benchmarking...\n", .{});
}

fn afterAllHook() void {
std.debug.print("Finished benchmarking.\n", .{});
}

fn myBenchmark(_: std.mem.Allocator) void {
_ = helloWorld();
}

test "bench test basic" {
const stdout = std.io.getStdOut().writer();
var bench = zbench.Benchmark.init(test_allocator, .{});
var bench = zbench.Benchmark.init(std.testing.allocator, .{});
defer bench.deinit();

try bench.add("My Benchmark", myBenchmark, .{
.iterations = 10,
.hooks = .{
.before_all = beforeAllHook,
.after_all = afterAllHook,
},
});

const sysinfo = try bench.getSystemInfo();
try std.fmt.format(stdout, "\n{}\n", .{sysinfo});

const results = try bench.run();
defer results.deinit();
try results.prettyPrint(stdout, true);
try bench.add("My Benchmark", myBenchmark, .{});
try bench.run(stdout);
}
4 changes: 1 addition & 3 deletions examples/bubble_sort.zig
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,5 @@ test "bench test bubbleSort" {

try bench.add("Bubble Sort Benchmark", myBenchmark, .{});

const results = try bench.run();
defer results.deinit();
try results.prettyPrint(stdout, true);
try bench.run(stdout);
}
33 changes: 33 additions & 0 deletions examples/hooks.zig
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
const std = @import("std");
const zbench = @import("zbench");

fn beforeAllHook() void {
std.debug.print("Starting benchmarking...\n", .{});
}

fn afterAllHook() void {
std.debug.print("Finished benchmarking.\n", .{});
}

fn myBenchmark(_: std.mem.Allocator) void {
var result: usize = 0;
for (0..1_000_000) |i| {
std.mem.doNotOptimizeAway(i);
result += i * i;
}
}

test "bench test hooks" {
const stdout = std.io.getStdOut().writer();
var bench = zbench.Benchmark.init(std.testing.allocator, .{});
defer bench.deinit();

try bench.add("My Benchmark", myBenchmark, .{
.iterations = 100,
.hooks = .{
.before_all = beforeAllHook,
.after_all = afterAllHook,
},
});
try bench.run(stdout);
}
9 changes: 5 additions & 4 deletions examples/parameterised.zig
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,10 @@ const MyBenchmark = struct {

pub fn run(self: MyBenchmark, _: std.mem.Allocator) void {
var result: usize = 0;
for (0..self.loops) |i| result += i * i;
for (0..self.loops) |i| {
std.mem.doNotOptimizeAway(i);
result += i * i;
}
}
};

Expand All @@ -23,8 +26,6 @@ test "bench test parameterised" {
try bench.addParam("My Benchmark 1", &MyBenchmark.init(100_000), .{});
try bench.addParam("My Benchmark 2", &MyBenchmark.init(200_000), .{});

const results = try bench.run();
defer results.deinit();
try stdout.writeAll("\n");
try results.prettyPrint(stdout, true);
try bench.run(stdout);
}
52 changes: 52 additions & 0 deletions examples/progress.zig
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
const std = @import("std");
const zbench = @import("zbench");
const test_allocator = std.testing.allocator;

fn myBenchmark1(_: std.mem.Allocator) void {
var result: usize = 0;
for (0..100_000) |i| {
std.mem.doNotOptimizeAway(i);
result += i * i;
}
}

fn myBenchmark2(_: std.mem.Allocator) void {
var result: usize = 0;
for (0..200_000) |i| {
std.mem.doNotOptimizeAway(i);
result += i * i;
}
}

test "bench test progress" {
const stdout = std.io.getStdOut().writer();
var bench = zbench.Benchmark.init(test_allocator, .{});
defer bench.deinit();

try bench.add("My Benchmark 1", myBenchmark1, .{});
try bench.add("My Benchmark 2", myBenchmark2, .{});

var progress = std.Progress{};
const progress_node = progress.start("", 0);
defer progress_node.end();

try stdout.writeAll("\n");
try bench.prettyPrintHeader(stdout);
var iter = try bench.iterator();
while (try iter.next()) |step| switch (step) {
.progress => |p| {
progress_node.setEstimatedTotalItems(p.total_runs);
progress_node.setCompletedItems(p.completed_runs);
progress_node.setName(p.current_name);
progress.maybeRefresh();
},
.result => |x| {
defer x.deinit();
progress_node.setName("");
progress_node.setEstimatedTotalItems(0);
progress_node.setCompletedItems(0);
progress.refresh();
try x.prettyPrint(stdout, true);
},
};
}
12 changes: 2 additions & 10 deletions examples/sleep.zig
Original file line number Diff line number Diff line change
Expand Up @@ -2,22 +2,14 @@ const std = @import("std");
const zbench = @import("zbench");
const test_allocator = std.testing.allocator;

fn sooSleepy() void {
std.time.sleep(100_000_000);
}

fn sleepBenchmark(_: std.mem.Allocator) void {
_ = sooSleepy();
std.time.sleep(100_000_000);
}

test "bench test sleepy" {
const stdout = std.io.getStdOut().writer();
var bench = zbench.Benchmark.init(test_allocator, .{});
defer bench.deinit();

try bench.add("Sleep Benchmark", sleepBenchmark, .{});

const results = try bench.run();
defer results.deinit();
try results.prettyPrint(stdout, true);
try bench.run(stdout);
}
22 changes: 22 additions & 0 deletions examples/systeminfo.zig
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
const std = @import("std");
const zbench = @import("zbench");

fn myBenchmark(_: std.mem.Allocator) void {
var result: usize = 0;
for (0..1_000_000) |i| {
std.mem.doNotOptimizeAway(i);
result += i * i;
}
}

test "bench test system info" {
const stdout = std.io.getStdOut().writer();
var bench = zbench.Benchmark.init(std.testing.allocator, .{});
defer bench.deinit();

const sysinfo = try bench.getSystemInfo();
try std.fmt.format(stdout, "\n{}\n", .{sysinfo});

try bench.add("My Benchmark", myBenchmark, .{});
try bench.run(stdout);
}
23 changes: 23 additions & 0 deletions util/runner.zig
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,9 @@ const State = union(enum) {
};

const Running = struct {
/// Total number of iterations done.
iterations_count: usize,

/// Number of timings still to be performed in the benchmark.
iterations_remaining: usize,

Expand All @@ -62,6 +65,7 @@ pub fn init(
} else .{
.allocator = allocator,
.state = .{ .running = .{
.iterations_count = iterations,
.iterations_remaining = iterations,
.timings_ns = try allocator.alloc(u64, iterations),
} },
Expand Down Expand Up @@ -92,6 +96,7 @@ pub fn next(self: *Runner, ns: u64) Runner.Error!?Runner.Step {
if (N > MAX_N) N = MAX_N;
// Now run the benchmark with the adjusted N value
self.state = .{ .running = .{
.iterations_count = N,
.iterations_remaining = N,
.timings_ns = try self.allocator.alloc(u64, N),
} };
Expand Down Expand Up @@ -125,6 +130,24 @@ pub fn abort(self: *Runner) void {
};
}

pub const Status = struct {
total_runs: usize,
completed_runs: usize,
};

pub fn status(self: Runner) Status {
return switch (self.state) {
.preparing => |_| Status{
.total_runs = 0,
.completed_runs = 0,
},
.running => |st| Status{
.total_runs = st.iterations_count,
.completed_runs = st.iterations_count - st.iterations_remaining,
},
};
}

test "Runner" {
var r = try Runner.init(std.testing.allocator, 0, 16384, 2e9);
{
Expand Down
Loading

0 comments on commit 87b5402

Please sign in to comment.