diff options
| author | Nathan Reiner <nathan@nathanreiner.xyz> | 2025-05-20 10:33:45 +0200 |
|---|---|---|
| committer | Nathan Reiner <nathan@nathanreiner.xyz> | 2025-05-20 10:33:45 +0200 |
| commit | 8d45a866497816ade70a0068afd19035a1b769d2 (patch) | |
| tree | 311c26eacd03d66a0e979efc9cba6c305272be5e /src/command/benchmark.zig | |
| parent | d11b876df07ef2cbdf71f3b509aca19ea45170cf (diff) | |
rename recognize to benchmark
Diffstat (limited to 'src/command/benchmark.zig')
| -rw-r--r-- | src/command/benchmark.zig | 150 |
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) + ); +} |