diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/argument.zig | 14 | ||||
| -rw-r--r-- | src/command/benchmark.zig | 150 | ||||
| -rw-r--r-- | src/command/generate.zig | 54 | ||||
| -rw-r--r-- | src/command/root.zig | 2 | ||||
| -rw-r--r-- | src/main.zig | 147 |
5 files changed, 217 insertions, 150 deletions
diff --git a/src/argument.zig b/src/argument.zig index 75bb7bf..7912397 100644 --- a/src/argument.zig +++ b/src/argument.zig @@ -26,7 +26,7 @@ fn help(err: ?anyerror) noreturn { \\ \\ -m, --min-length n Minimum length of sentential string. \\ - \\ recognize [grammar] [options] + \\ benchmark [grammar] [options] \\ Options: \\ -e, --entry label Name of the entry point. (default: main) \\ @@ -70,7 +70,7 @@ fn check_flags(arg: []const u8, comptime flags: []const []const u8) bool { } pub const Mode = enum { - recognize, + benchmark, generate, }; @@ -115,7 +115,7 @@ pub const Entry = struct { } }; -pub const RecognizeArgs = struct { +pub const BenchmarkArgs = struct { input: Entry, grammar: Grammar, entry: []const u8, @@ -132,7 +132,7 @@ pub const GenerateArgs = struct { pub const Args = union(Mode) { const Self = @This(); - recognize: RecognizeArgs, + benchmark: BenchmarkArgs, generate: GenerateArgs, pub fn parse(allocator: std.mem.Allocator) Self { @@ -145,7 +145,7 @@ pub const Args = union(Mode) { defer allocator.free(text); switch (mode) { - .recognize => { + .benchmark => { var input: ?Entry = null; var entry: []const u8 = "main"; @@ -164,7 +164,7 @@ pub const Args = union(Mode) { ) catch |e| help(e); return Self { - .recognize = .{ + .benchmark = .{ .input = input orelse Entry.stdin(), .grammar = grammar, .entry = entry, @@ -213,7 +213,7 @@ pub const Args = union(Mode) { pub fn deinit(self: *Self) void { switch (self.*) { - .recognize => |*rec| { + .benchmark => |*rec| { rec.grammar.deinit(); }, .generate => |*gen| { 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) + ); +} diff --git a/src/command/generate.zig b/src/command/generate.zig new file mode 100644 index 0000000..1d9eb3e --- /dev/null +++ b/src/command/generate.zig @@ -0,0 +1,54 @@ +const std = @import("std"); +const root = @import("root"); +const argument = root.argument; +const Generator = root.Generator; +const Scheduler = root.Scheduler; + +const GenerateArgs = argument.GenerateArgs; + +const RandomGenerator = Generator(struct { + const Self = @This(); + + pub fn next(_: *Self, n: usize) usize { + return std.crypto.random.uintLessThan(usize, n); + } +}); + +fn generate_word( + args: *GenerateArgs, + generator: *RandomGenerator, + allocator: std.mem.Allocator +) []const u8 { + while (true) { + const text = generator.sentential_from_grammar( + &args.grammar, + 10000, + allocator + ) catch continue; + if (text.len >= args.min_length) { + return text; + } + + allocator.free(text); + } +} + +pub fn generate(args: *GenerateArgs, allocator: std.mem.Allocator) !void { + var writer = args.output.file.writer(); + + var scheduler: Scheduler(generate_word) = undefined; + try scheduler.init(allocator); + var generator = RandomGenerator {}; + + for (0..args.count) |_| { + try scheduler.push_task(.{args, &generator, allocator}); + } + + const results = try scheduler.deinit(); + defer allocator.free(results); + + for (results) |result| { + try writer.print("{s}\n", .{result}); + allocator.free(result); + } +} diff --git a/src/command/root.zig b/src/command/root.zig new file mode 100644 index 0000000..be15865 --- /dev/null +++ b/src/command/root.zig @@ -0,0 +1,2 @@ +pub const generate = @import("generate.zig").generate; +pub const benchmark = @import("benchmark.zig").benchmark; diff --git a/src/main.zig b/src/main.zig index 8797b31..d9aee77 100644 --- a/src/main.zig +++ b/src/main.zig @@ -3,153 +3,14 @@ pub const Grammar = @import("grammar.zig"); pub const gss = @import("gss.zig"); pub const recognizer = @import("recognizer.zig"); pub const argument = @import("argument.zig"); +pub const command = @import("command/root.zig"); pub const Generator = @import("generator.zig").Generator; pub const Scheduler = @import("scheduler.zig").Scheduler; const Args = argument.Args; -const RecognizeArgs = argument.RecognizeArgs; +const BenchmarkArgs = argument.BenchmarkArgs; const GenerateArgs = argument.GenerateArgs; -fn write_result( - writer: anytype, - is_tty: bool, - name: []const u8, - index: usize, - input: []const u8, - accepted: bool, -) !void { - if (is_tty) { - try writer.print("{s}[{}] {s}\x1b[0m: \"\x1b[3m{s}\x1b[0m\"\n", .{ - name, - index, - if (accepted) "\x1b[32maccept" - else "\x1b[31mreject", - input, - }); - } else { - try writer.print("{s}[{}] {s}: \"{s}\"\n", .{ - name, - index, - if (accepted) "accept" - else "reject", - input, - }); - } -} - -fn write_summary( - writer: anytype, - is_tty: bool, - total: usize, - accepted: usize, -) !void { - if (is_tty) { - try writer.print("finished {} ({} \x1b[32maccepted\x1b[0m {} \x1b[31mrejected\x1b[0m)\n", .{ total, accepted, total - accepted }); - } else { - try writer.print("finished {} ({} accepted {} rejected)\n", .{ total, accepted, total - accepted }); - } -} - -fn recognize(args: *RecognizeArgs, allocator: std.mem.Allocator) !void { - const stdout = std.io.getStdOut(); - var bufwriter = std.io.bufferedWriter(stdout.writer()); - const writer = bufwriter.writer(); - - var bufreader = std.io.bufferedReader(args.input.file.reader()); - var reader = bufreader.reader(); - var index: usize = 0; - const stderr = std.io.getStdErr(); - - 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; - - while (try reader.readUntilDelimiterOrEofAlloc( - read_arena.allocator(), - '\n', - std.math.maxInt(usize) - )) |buffer| { - - const trimmed = std.mem.trim(u8, buffer, &std.ascii.whitespace); - - const accepted = try recognizer.check( - &args.grammar, - trimmed, - allocator - ); - - try write_result( - writer, - stdout.isTty(), - args.input.name, - index, - trimmed, - accepted, - ); - - index += 1; - number_of_accepted += if (accepted) 1 else 0; - - if (args.input.file.isTty()) { - try bufwriter.flush(); - try stderr.writeAll("> "); - } - } - - try write_summary(writer, stdout.isTty(), index, number_of_accepted); - try bufwriter.flush(); -} - -const RandomGenerator = Generator(struct { - const Self = @This(); - - pub fn next(_: *Self, n: usize) usize { - return std.crypto.random.uintLessThan(usize, n); - } -}); - -pub fn generate_word( - args: *GenerateArgs, - generator: *RandomGenerator, - allocator: std.mem.Allocator -) []const u8 { - while (true) { - const text = generator.sentential_from_grammar( - &args.grammar, - 10000, - allocator - ) catch continue; - if (text.len >= args.min_length) { - return text; - } - - allocator.free(text); - } -} - -fn generate(args: *GenerateArgs, allocator: std.mem.Allocator) !void { - var writer = args.output.file.writer(); - - var scheduler: Scheduler(generate_word) = undefined; - try scheduler.init(allocator); - var generator = RandomGenerator {}; - - for (0..args.count) |_| { - try scheduler.push_task(.{args, &generator, allocator}); - } - - const results = try scheduler.deinit(); - defer allocator.free(results); - - for (results) |result| { - try writer.print("{s}\n", .{result}); - allocator.free(result); - } -} pub fn main() !void { var gpa = std.heap.GeneralPurposeAllocator(.{}){}; @@ -164,8 +25,8 @@ pub fn main() !void { defer arguments.deinit(); try switch(arguments) { - .recognize => |*args| recognize(args, allocator), - .generate => |*args| generate(args, allocator), + .benchmark => |*args| command.benchmark(args, allocator), + .generate => |*args| command.generate(args, allocator), }; } |