const std = @import("std"); const root = @import("../main.zig"); 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) ); }