Skip to content
Merged
Show file tree
Hide file tree
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
2 changes: 1 addition & 1 deletion examples/bubble_sort_hooks.zig
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ const inc = @import("include");
const zbench = @import("zbench");

// Global variables modified/accessed by the hooks.
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
var gpa = std.heap.DebugAllocator(.{}){};
const array_size: usize = 100;
// BenchmarkData contains the data generation logic.
var benchmark_data: BenchmarkData = undefined;
Expand Down
2 changes: 1 addition & 1 deletion examples/json.zig
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
const std = @import("std");
const zbench = @import("zbench");
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
var gpa = std.heap.DebugAllocator(.{}){};

fn myBenchmark(alloc: std.mem.Allocator) void {
var result: usize = 0;
Expand Down
7 changes: 5 additions & 2 deletions examples/memory_tracking.zig
Original file line number Diff line number Diff line change
@@ -1,10 +1,13 @@
const std = @import("std");
const zbench = @import("zbench");
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
var gpa = std.heap.DebugAllocator(.{}){};

// amount of memory that should appear in the [MEMORY] section of the output
const NUM_BYTES = 1024;

fn myBenchmark(allocator: std.mem.Allocator) void {
for (0..2000) |_| {
const buf = allocator.alloc(u8, 512) catch @panic("OOM");
const buf = allocator.alloc(u8, NUM_BYTES) catch @panic("OOM");
defer allocator.free(buf);
}
}
Expand Down
4 changes: 2 additions & 2 deletions examples/progress.zig
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ pub fn main() !void {
try zbench.prettyPrintHeader(
io,
stdout,
"{s:<20} {s:<8} {s:<14} {s:<23} {s:<28} {s:<10} {s:<10} {s:<10}\n",
bench.max_name_len,
);

// Initialize the std.Progress api
Expand Down Expand Up @@ -77,7 +77,7 @@ pub fn main() !void {
completed_benchmarks += 1;
suite_node.setCompletedItems(completed_benchmarks);

try r.prettyPrint(io, stdout, "{s:<20} ");
try r.prettyPrint(io, stdout, bench.max_name_len);
},
};

Expand Down
74 changes: 47 additions & 27 deletions src/result.zig
Original file line number Diff line number Diff line change
@@ -1,11 +1,15 @@
const std = @import("std");
const Terminal = std.Io.Terminal;
const Color = Terminal.Color;
const Statistics = @import("statistics.zig").Statistics;
const Duration = std.Io.Duration;
const assert = std.debug.assert;

const fmt = @import("fmt.zig");
const statistics = @import("statistics.zig");
const Runner = @import("runner.zig");
const Readings = Runner.Readings;
const Statistics = statistics.Statistics;
const NAME_LEN_LIMIT = @import("zbench.zig").NAME_LEN_LIMIT;

/// Carries the results of a benchmark. The benchmark name and the recorded
/// durations are available, and some basic statistics are automatically
Expand All @@ -23,65 +27,80 @@ pub const Result = struct {
}

/// Formats and prints the benchmark result in a human readable format.
/// writer: Type that has the associated method print (for example std.Io.getStdOut.writer())
/// tty_config: TTY configuration for color output.
pub fn prettyPrint(
self: Result,
io: std.Io,
file: std.Io.File,
comptime name_fmt: []const u8,
name_len: usize,
) !void {
var w: std.Io.File.Writer = file.writerStreaming(io, &.{});
const writer: *std.Io.Writer = &w.interface;
const terminal_mode: Terminal.Mode = try .detect(io, file, false, false);
const terminal: Terminal = .{ .writer = writer, .mode = terminal_mode };

var buf: [128]u8 = undefined;
const buf_len: usize = 128;
const _name_len = if (name_len > NAME_LEN_LIMIT) NAME_LEN_LIMIT else name_len;
assert(_name_len + 3 <= buf_len);

var buf: [buf_len]u8 = undefined;

const timings_ns = self.readings.timings_ns;
const s = try Statistics(u64).init(timings_ns);
const truncated_name = self.name[0..@min(22, self.name.len)];
const truncated_name = self.name[0..@min(NAME_LEN_LIMIT, self.name.len)];

// Benchmark name, number of iterations, and total time
try writer.print(name_fmt, .{truncated_name});
_ = try std.Io.Writer.alignBuffer(writer, truncated_name, _name_len + 3, .left, ' ');
try terminal.setColor(Color.cyan);
try writer.print("{d:<8} {D:<15}", .{
var tmp = try std.fmt.bufPrint(&buf, "{d:<8} {f}", .{
self.readings.iterations,
s.total,
Duration.fromNanoseconds(s.total),
});
_ = try std.Io.Writer.alignBuffer(writer, tmp, 24, .left, ' ');

// Mean + standard deviation
try terminal.setColor(Color.green);
try writer.print("{s:<23}", .{
try std.fmt.bufPrint(&buf, "{D:.3} ± {D:.3}", .{
s.mean,
s.stddev,
}),
tmp = try std.fmt.bufPrint(&buf, "{f} ± {f}", .{
Duration.fromNanoseconds(s.mean),
Duration.fromNanoseconds(s.stddev),
});
_ = try std.Io.Writer.alignBuffer(writer, tmp, 23, .left, ' ');

// Minimum and maximum
try terminal.setColor(Color.green);
try writer.print("{s:<29}", .{
try std.fmt.bufPrint(&buf, "({D:.3} ... {D:.3})", .{
s.min,
s.max,
}),
tmp = try std.fmt.bufPrint(&buf, "({f} ... {f})", .{
Duration.fromNanoseconds(s.min),
Duration.fromNanoseconds(s.max),
});
_ = try std.Io.Writer.alignBuffer(writer, tmp, 29, .left, ' ');

// Percentiles
try terminal.setColor(Color.cyan);
try writer.print("{D:<10} {D:<10} {D:<10}", .{
s.percentiles.p75,
s.percentiles.p99,
s.percentiles.p995,
tmp = try std.fmt.bufPrint(&buf, "{f}", .{
Duration.fromNanoseconds(s.percentiles.p75),
});
_ = try std.Io.Writer.alignBuffer(writer, tmp, 11, .left, ' ');
tmp = try std.fmt.bufPrint(&buf, "{f}", .{
Duration.fromNanoseconds(s.percentiles.p99),
});
_ = try std.Io.Writer.alignBuffer(writer, tmp, 11, .left, ' ');
tmp = try std.fmt.bufPrint(&buf, "{f}", .{
Duration.fromNanoseconds(s.percentiles.p995),
});
_ = try std.Io.Writer.alignBuffer(writer, tmp, 11, .left, ' ');

// End of line
try terminal.setColor(Color.reset);
try writer.writeAll("\n");

if (self.readings.allocations) |allocs| {
const m = try Statistics(usize).init(allocs.maxes);
const trackmem_offset: usize = name_len - truncated_name.len + 18;

// Benchmark name
const name = try std.fmt.bufPrint(&buf, "{s} [MEMORY]", .{
truncated_name,
});
try writer.print("{s:<46} ", .{name});
tmp = try std.fmt.bufPrint(&buf, "{s} [MEMORY]", .{truncated_name});
_ = try std.Io.Writer.alignBuffer(writer, tmp, _name_len, .left, ' ');
try writer.splatByteAll(' ', trackmem_offset);

// Mean + standard deviation
try terminal.setColor(Color.green);
try writer.print("{s:<23}", .{
Expand Down Expand Up @@ -111,6 +130,7 @@ pub const Result = struct {
}
}

/// Formats and prints the benchmark result in JSON format.
pub fn writeJSON(
self: Result,
writer: *std.Io.Writer,
Expand Down
35 changes: 22 additions & 13 deletions src/zbench.zig
Original file line number Diff line number Diff line change
Expand Up @@ -23,11 +23,15 @@ const Partial = @import("partial.zig").Partial;
const partial = @import("partial.zig").partial;
const platform = @import("platform/platform.zig");

// Maximum number of characters to allow for the benchmark name (pretty-printing):
pub const NAME_LEN_LIMIT: usize = 96;

/// Benchmark manager, add your benchmark functions and run measure them.
pub const Benchmark = struct {
allocator: std.mem.Allocator,
common_config: Config,
benchmarks: std.ArrayListUnmanaged(Definition) = .{},
benchmarks: std.ArrayList(Definition) = .empty,
max_name_len: usize = 0, // for pretty-printing the results

pub fn init(allocator: std.mem.Allocator, config: Config) Benchmark {
return Benchmark{
Expand All @@ -52,9 +56,10 @@ pub const Benchmark = struct {
.defn = .{ .simple = func },
.config = partial(Config, config, self.common_config),
});
self.max_name_len = if (name.len > self.max_name_len) name.len else self.max_name_len;
}

/// Add a benchmark function to be timed with `run()`.
/// Add a parameterised benchmark function to be timed with `run()`.
pub fn addParam(
self: *Benchmark,
name: []const u8,
Expand Down Expand Up @@ -82,6 +87,7 @@ pub const Benchmark = struct {
} },
.config = partial(Config, config, self.common_config),
});
self.max_name_len = if (name.len > self.max_name_len) name.len else self.max_name_len;
}

/// An incremental API for getting progress updates on running benchmarks.
Expand Down Expand Up @@ -176,31 +182,34 @@ pub const Benchmark = struct {

/// Run all benchmarks and collect timing information.
pub fn run(self: Benchmark, io: std.Io, file: std.Io.File) !void {
// TODO : benchmark name length is comptime-known, so we should determine
// the format considering this (see #130)
const header_fmt = "{s:<22} {s:<8} {s:<14} {s:<23} {s:<28} {s:<10} {s:<10} {s:<10}\n";
const name_fmt = "{s:<22} ";

try prettyPrintHeader(io, file, header_fmt);
try prettyPrintHeader(io, file, self.max_name_len);
var iter = try self.iterator();
while (try iter.next(io)) |step| switch (step) {
.progress => {},
.result => |x| {
defer x.deinit();
try x.prettyPrint(io, file, name_fmt);
try x.prettyPrint(io, file, self.max_name_len);
},
};
}
};

/// Write the prettyPrint() header to a writer.
pub fn prettyPrintHeader(io: std.Io, file: std.Io.File, comptime header_fmt: []const u8) !void {
pub fn prettyPrintHeader(io: std.Io, file: std.Io.File, name_len: usize) !void {
const _name_len = if (name_len > NAME_LEN_LIMIT) NAME_LEN_LIMIT else name_len;
const header_fmt: []const u8 = "{s:<8} {s:<14} {s:<23} {s:<28} {s:<10} {s:<10} {s:<10}\n";
const dashes_repeat: usize = 111;

var w: std.Io.File.Writer = file.writerStreaming(io, &.{});
const writer: *std.Io.Writer = &w.interface;

// header starts with "benchmark", which needs to be padded depending on the
// longest benchmark name.
_ = try std.Io.Writer.alignBuffer(writer, "benchmark", _name_len + 3, .left, ' ');
try writer.print(
header_fmt,
.{
"benchmark",
// "benchmark" is written by alignBuffer
"runs",
"total time",
"time/run (avg ± σ)",
Expand All @@ -210,8 +219,8 @@ pub fn prettyPrintHeader(io: std.Io, file: std.Io.File, comptime header_fmt: []c
"p995",
},
);
const dashes = "-------------------------";
try writer.print(dashes ++ dashes ++ dashes ++ dashes ++ dashes ++ "\n", .{});
try writer.splatByteAll('-', _name_len + dashes_repeat);
try writer.print("\n", .{});
}

/// Get a copy of the system information, cpu type, cores, memory, etc.
Expand Down
Loading