aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/argument.zig14
-rw-r--r--src/command/benchmark.zig150
-rw-r--r--src/command/generate.zig54
-rw-r--r--src/command/root.zig2
-rw-r--r--src/main.zig147
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),
};
}