Skip to content

Commit

Permalink
Fix formatting for fractions with leading zeroes
Browse files Browse the repository at this point in the history
Use the stdlib's duration formatting function rather than hand rolling something
and dealing with possible bugs. Refactored the pretty printing function a bit to
allow for monochrome output more easily as well.
  • Loading branch information
bens committed Mar 13, 2024
1 parent cdbbd93 commit b06e3a5
Show file tree
Hide file tree
Showing 4 changed files with 56 additions and 137 deletions.
1 change: 0 additions & 1 deletion build.zig
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,6 @@ fn setupLibrary(b: *std.Build, target: std.zig.CrossTarget, optimize: std.builti
fn setupTesting(b: *std.Build, target: std.zig.CrossTarget, optimize: std.builtin.OptimizeMode) void {
const test_files = [_]struct { name: []const u8, path: []const u8 }{
.{ .name = "tests", .path = "tests.zig" },
.{ .name = "format_test", .path = "util/format_test.zig" },
.{ .name = "quicksort", .path = "util/quicksort.zig" },
};

Expand Down
95 changes: 36 additions & 59 deletions util/format.zig
Original file line number Diff line number Diff line change
@@ -1,26 +1,6 @@
const std = @import("std");
const Color = @import("./color.zig").Color;
const log = std.log.scoped(.zbench_format);

pub fn duration(buffer: []u8, d: u64) ![]u8 {
const units = [_][]const u8{ "ns", "µs", "ms", "s" };

var scaledDuration: u64 = d;
var unitIndex: usize = 0;

var fractionalPart: u64 = 0;

while (scaledDuration >= 1_000 and unitIndex < units.len - 1) {
fractionalPart = scaledDuration % 1_000;
scaledDuration /= 1_000;
unitIndex += 1;
}

const formatted = try std.fmt.bufPrint(buffer, "{d}.{d}{s}", .{ scaledDuration, fractionalPart, units[unitIndex] });

return formatted;
}

pub fn memorySize(bytes: u64, allocator: std.mem.Allocator) ![]const u8 {
const units = .{ "B", "KB", "MB", "GB", "TB" };
var size: f64 = @floatFromInt(bytes);
Expand Down Expand Up @@ -52,70 +32,67 @@ pub fn memorySize(bytes: u64, allocator: std.mem.Allocator) ![]const u8 {
pub fn prettyPrintHeader(writer: anytype) !void {
try writer.print(
"\n{s:<22} {s:<8} {s:<14} {s:<22} {s:<28} {s:<10} {s:<10} {s:<10}\n",
.{ "benchmark", "runs", "total time", "time/run (avg ± σ)", "(min ... max)", "p75", "p99", "p995" },
.{
"benchmark",
"runs",
"total time",
"time/run (avg ± σ)",
"(min ... max)",
"p75",
"p99",
"p995",
},
);
try writer.print("-----------------------------------------------------------------------------------------------------------------------------\n", .{});
const dashes = "-------------------------";
try writer.print(dashes ++ dashes ++ dashes ++ dashes ++ dashes ++ "\n", .{});
}

/// Pretty-prints the name of the benchmark
/// writer: Type that has the associated method print (for example std.io.getStdOut.writer())
pub fn prettyPrintName(name: []const u8, writer: anytype, color: Color) !void {
try writer.print("{s}{s:<22}{s} ", .{ color.code(), name, Color.reset.code() });
pub fn prettyPrintName(name: []const u8, writer: anytype) !void {
try writer.print("{s:<22} ", .{name});
}

/// Pretty-prints the number of total operations (or runs) of the benchmark performed
/// writer: Type that has the associated method print (for example std.io.getStdOut.writer())
pub fn prettyPrintTotalOperations(total_operations: u64, writer: anytype, color: Color) !void {
try writer.print("{s}{d:<8}{s} ", .{ color.code(), total_operations, Color.reset.code() });
pub fn prettyPrintTotalOperations(total_operations: u64, writer: anytype) !void {
try writer.print("{d:<8} ", .{total_operations});
}

/// Pretty-prints the total time it took to perform all the runs
/// writer: Type that has the associated method print (for example std.io.getStdOut.writer())
pub fn prettyPrintTotalTime(total_time: u64, writer: anytype, color: Color) !void {
var buffer: [128]u8 = undefined;
const str = try duration(buffer[0..], total_time);

try writer.print("{s}{s:<14}{s} ", .{ color.code(), str, Color.reset.code() });
pub fn prettyPrintTotalTime(total_time: u64, writer: anytype) !void {
try writer.print("{s:<14} ", .{std.fmt.fmtDuration(total_time)});
}

/// Pretty-prints the average (arithmetic mean) and the standard deviation of the durations
/// writer: Type that has the associated method print (for example std.io.getStdOut.writer())
pub fn prettyPrintAvgStd(avg: u64, stdd: u64, writer: anytype, color: Color) !void {
pub fn prettyPrintAvgStd(avg: u64, stddev: u64, writer: anytype) !void {
var buffer: [128]u8 = undefined;
var avg_stdd_offset = (try duration(buffer[0..], avg)).len;
avg_stdd_offset += (try std.fmt.bufPrint(buffer[avg_stdd_offset..], " ± ", .{})).len;
avg_stdd_offset += (try duration(buffer[avg_stdd_offset..], stdd)).len;
const str = buffer[0..avg_stdd_offset];

try writer.print("{s}{s:<22}{s} ", .{ color.code(), str, Color.reset.code() });
const str = try std.fmt.bufPrint(&buffer, "{} ± {}", .{
std.fmt.fmtDuration(avg),
std.fmt.fmtDuration(stddev),
});
try writer.print("{s:<22} ", .{str});
}

/// Pretty-prints the minumim and maximum duration
/// writer: Type that has the associated method print (for example std.io.getStdOut.writer())
pub fn prettyPrintMinMax(min: u64, max: u64, writer: anytype, color: Color) !void {
var min_buffer: [128]u8 = undefined;
const min_str = try duration(min_buffer[0..], min);

var max_buffer: [128]u8 = undefined;
const max_str = try duration(max_buffer[0..], max);

pub fn prettyPrintMinMax(min: u64, max: u64, writer: anytype) !void {
var buffer: [128]u8 = undefined;
const str = try std.fmt.bufPrint(buffer[0..], "({s} ... {s})", .{ min_str, max_str });

try writer.print("{s}{s:<28}{s} ", .{ color.code(), str, Color.reset.code() });
const str = try std.fmt.bufPrint(&buffer, "({} ... {})", .{
std.fmt.fmtDuration(min),
std.fmt.fmtDuration(max),
});
try writer.print("{s:<28} ", .{str});
}

/// Pretty-prints the 75th, 99th and 99.5th percentile of the durations
/// writer: Type that has the associated method print (for example std.io.getStdOut.writer())
pub fn prettyPrintPercentiles(p75: u64, p99: u64, p995: u64, writer: anytype, color: Color) !void {
var p75_buffer: [128]u8 = undefined;
const p75_str = try duration(p75_buffer[0..], p75);

var p99_buffer: [128]u8 = undefined;
const p99_str = try duration(p99_buffer[0..], p99);

var p995_buffer: [128]u8 = undefined;
const p995_str = try duration(p995_buffer[0..], p995);

try writer.print("{s}{s:<10} {s:<10} {s:<10}{s} ", .{ color.code(), p75_str, p99_str, p995_str, Color.reset.code() });
pub fn prettyPrintPercentiles(p75: u64, p99: u64, p995: u64, writer: anytype) !void {
try writer.print("{s:<10} {s:<10} {s:<10}", .{
std.fmt.fmtDuration(p75),
std.fmt.fmtDuration(p99),
std.fmt.fmtDuration(p995),
});
}
38 changes: 0 additions & 38 deletions util/format_test.zig

This file was deleted.

59 changes: 20 additions & 39 deletions zbench.zig
Original file line number Diff line number Diff line change
Expand Up @@ -219,17 +219,25 @@ pub const BenchmarkResult = struct {
/// Formats and prints the benchmark-result in a readable format.
/// writer: Type that has the associated method print (for example std.io.getStdOut.writer())
/// header: Whether to pretty-print the header or not
pub fn prettyPrint(self: Self, writer: anytype, header: bool) !void {
if (header) try format.prettyPrintHeader(writer);

try format.prettyPrintName(self.name, writer, Color.none);
try format.prettyPrintTotalOperations(self.total_operations, writer, Color.cyan);
try format.prettyPrintTotalTime(self.total_time, writer, Color.cyan);
try format.prettyPrintAvgStd(self.avg_durations, self.std_durations, writer, Color.green);
try format.prettyPrintMinMax(self.min_durations, self.max_durations, writer, Color.blue);
try format.prettyPrintPercentiles(self.percentiles.p75, self.percentiles.p99, self.percentiles.p995, writer, Color.cyan);
pub fn prettyPrint(self: Self, writer: anytype) !void {
const colors = true;
const pc = self.percentiles;
try format.prettyPrintName(self.name, writer);
try setColor(colors, writer, Color.cyan);
try format.prettyPrintTotalOperations(self.total_operations, writer);
try format.prettyPrintTotalTime(self.total_time, writer);
try setColor(colors, writer, Color.green);
try format.prettyPrintAvgStd(self.avg_duration, self.std_duration, writer);
try setColor(colors, writer, Color.blue);
try format.prettyPrintMinMax(self.min_duration, self.max_duration, writer);
try setColor(colors, writer, Color.cyan);
try format.prettyPrintPercentiles(pc.p75, pc.p99, pc.p995, writer);
try setColor(colors, writer, Color.reset);
try writer.writeAll("\n");
}

_ = try writer.write("\n");
fn setColor(colors: bool, writer: anytype, color: Color) !void {
if (colors) try writer.writeAll(color.code());
}
};

Expand Down Expand Up @@ -258,38 +266,11 @@ pub const BenchmarkResults = struct {
self.results.deinit();
}

/// Determines the color representation based on the total-time of the benchmark.
/// total_time: The total-time to evaluate.
pub fn getColor(self: *const BenchmarkResults, total_time: u64) Color {
const max_total_time = @max(self.results.items[0].total_time, self.results.items[self.results.items.len - 1].total_time);
const min_total_time = @min(self.results.items[0].total_time, self.results.items[self.results.items.len - 1].total_time);

if (total_time <= min_total_time) return Color.green;
if (total_time >= max_total_time) return Color.red;

const prop = (total_time - min_total_time) * 100 / (max_total_time - min_total_time + 1);

if (prop < 50) return Color.green;
if (prop < 75) return Color.yellow;

return Color.red;
}

/// Formats and prints the benchmark results in a readable format.
pub fn prettyPrint(self: *BenchmarkResults) !void {
var writer = self.out_stream.writer();
const writer = self.out_stream.writer();
try format.prettyPrintHeader(writer);
for (self.results.items) |result| {
try format.prettyPrintName(result.name, writer, Color.none);
try format.prettyPrintTotalOperations(result.total_operations, writer, Color.cyan);
try format.prettyPrintTotalTime(result.total_time, writer, self.getColor(result.total_time));
try format.prettyPrintAvgStd(result.avg_duration, result.std_duration, writer, Color.green);
try format.prettyPrintMinMax(result.min_duration, result.max_duration, writer, Color.blue);
try format.prettyPrintPercentiles(result.percentiles.p75, result.percentiles.p99, result.percentiles.p995, writer, Color.cyan);

_ = try writer.write("\n");
}

for (self.results.items) |r| try r.prettyPrint(writer);
try self.out_stream.flush();
}
};
Expand Down

0 comments on commit b06e3a5

Please sign in to comment.