aboutsummaryrefslogtreecommitdiff
path: root/src/command/benchmark.zig
diff options
context:
space:
mode:
Diffstat (limited to 'src/command/benchmark.zig')
-rw-r--r--src/command/benchmark.zig150
1 files changed, 150 insertions, 0 deletions
diff --git a/src/command/benchmark.zig b/src/command/benchmark.zig
new file mode 100644
index 0000000..7881db5
--- /dev/null
+++ b/src/command/benchmark.zig
@@ -0,0 +1,150 @@
+const std = @import("std");
+const root = @import("root");
+const BenchmarkArgs = root.argument.BenchmarkArgs;
+const recognizer = root.recognizer;
+
+fn write_result(
+ writer: anytype,
+ is_tty: bool,
+ name: []const u8,
+ index: usize,
+ accepted: bool,
+ elapsed: u64,
+ throughput: f64,
+) !void {
+ if (is_tty) {
+ try writer.print("{s}[{}] {s}\x1b[0m ({}, {d:.2} tps)\n", .{
+ name,
+ index,
+ if (accepted) "\x1b[32maccept"
+ else "\x1b[31mreject",
+ std.fmt.fmtDuration(elapsed),
+ throughput,
+ });
+ } else {
+ try writer.print("{s}[{}] {s} ({}, {d:.2} tps)\n", .{
+ name,
+ index,
+ if (accepted) "accept"
+ else "reject",
+ std.fmt.fmtDuration(elapsed),
+ throughput,
+ });
+ }
+}
+
+fn write_summary(
+ writer: anytype,
+ is_tty: bool,
+ total: usize,
+ accepted: usize,
+ total_time: u64,
+ throughput: struct { f64, f64, f64 },
+) !void {
+ const min, const max, const avg = throughput;
+
+ if (is_tty) {
+ try writer.print("finished {} ({} \x1b[32maccepted\x1b[0m {d:.2} \x1b[31mrejected\x1b[0m)", .{
+ total,
+ accepted,
+ total - accepted,
+ });
+ } else {
+ try writer.print("finished {} ({} accepted {} rejected)", .{
+ total,
+ accepted,
+ total - accepted,
+ });
+ }
+
+ try writer.print(" in {} (min: {d:.5} tps, max: {d:.2} tps, average: {d:.2} tps)\n", .{
+ std.fmt.fmtDuration(total_time),
+ min,
+ max,
+ avg,
+ });
+}
+
+fn min_max_avg(values: []f64) struct { f64, f64, f64 } {
+ var min: f64 = -1;
+ var max: f64 = 0;
+ var sum: f64 = 0;
+
+ for (values) |value| {
+ min = if (min < 0) value else @min(min, value);
+ max = @max(max, value);
+ sum += value;
+ }
+
+ return .{ min, max, sum / @as(f64, @floatFromInt(values.len)) };
+}
+
+pub fn benchmark(args: *BenchmarkArgs, allocator: std.mem.Allocator) !void {
+ const stdout = std.io.getStdOut();
+ const writer = stdout.writer();
+
+ var bufreader = std.io.bufferedReader(args.input.file.reader());
+ var reader = bufreader.reader();
+ var index: usize = 0;
+ const stderr = std.io.getStdErr();
+
+ var throughputs = std.ArrayList(f64).init(allocator);
+ defer throughputs.deinit();
+
+ if (args.input.file.isTty()) {
+ try stderr.writeAll("> ");
+ }
+
+ var read_arena = std.heap.ArenaAllocator.init(allocator);
+ defer read_arena.deinit();
+ var number_of_accepted: usize = 0;
+ var total_time: u64 = 0;
+ var timer = try std.time.Timer.start();
+
+ while (try reader.readUntilDelimiterOrEofAlloc(
+ read_arena.allocator(),
+ '\n',
+ std.math.maxInt(usize)
+ )) |buffer| {
+
+ const trimmed = std.mem.trim(u8, buffer, &std.ascii.whitespace);
+
+ timer.reset();
+ const accepted = try recognizer.check(
+ &args.grammar,
+ trimmed,
+ allocator
+ );
+
+ const elapsed = timer.read();
+ const throughput = @as(f64, @floatFromInt(trimmed.len)) / (@as(f64, @floatFromInt(elapsed)) / std.time.ns_per_s);
+ try throughputs.append(throughput);
+
+ try write_result(
+ writer,
+ stdout.isTty(),
+ args.input.name,
+ index,
+ accepted,
+ elapsed,
+ throughput,
+ );
+
+ index += 1;
+ number_of_accepted += @intFromBool(accepted);
+ total_time += elapsed;
+
+ if (args.input.file.isTty()) {
+ try stderr.writeAll("> ");
+ }
+ }
+
+ try write_summary(
+ writer,
+ stdout.isTty(),
+ index,
+ number_of_accepted,
+ total_time,
+ min_max_avg(throughputs.items)
+ );
+}