Add a benchmark exe for testing parser throughput

This commit is contained in:
Mitchell Hashimoto
2022-11-13 11:23:23 -08:00
parent 445ca8998f
commit 81fbc94b3c
2 changed files with 112 additions and 0 deletions

View File

@ -72,6 +72,9 @@ pub fn build(b: *std.build.Builder) !void {
"Build and install test executables with 'build'",
) orelse false;
// Add our benchmarks
try benchSteps(b, target, mode);
const exe = b.addExecutable("ghostty", "src/main.zig");
const exe_options = b.addOptions();
exe_options.addOption(bool, "tracy_enabled", tracy);
@ -337,6 +340,44 @@ fn addDeps(
try glfw.link(b, imgui_step, glfw_opts);
}
fn benchSteps(
b: *std.build.Builder,
target: std.zig.CrossTarget,
mode: std.builtin.Mode,
) !void {
// Open the directory ./src/bench
const c_dir_path = (comptime root()) ++ "/src/bench";
var c_dir = try fs.openIterableDirAbsolute(c_dir_path, .{});
defer c_dir.close();
// Go through and add each as a step
var c_dir_it = c_dir.iterate();
while (try c_dir_it.next()) |entry| {
// Get the index of the last '.' so we can strip the extension.
const index = std.mem.lastIndexOfScalar(u8, entry.name, '.') orelse continue;
if (index == 0) continue;
// If it doesn't end in 'zig' then ignore
if (!std.mem.eql(u8, entry.name[index + 1 ..], "zig")) continue;
// Name of the conformance app and full path to the entrypoint.
const name = entry.name[0..index];
const path = try fs.path.join(b.allocator, &[_][]const u8{
c_dir_path,
entry.name,
});
// Executable builder.
const bin_name = try std.fmt.allocPrint(b.allocator, "bench-{s}", .{name});
const c_exe = b.addExecutable(bin_name, path);
c_exe.setTarget(target);
c_exe.setBuildMode(mode);
c_exe.setMainPkgPath("./src");
c_exe.install();
try addDeps(b, c_exe, true);
}
}
fn conformanceSteps(
b: *std.build.Builder,
target: std.zig.CrossTarget,

71
src/bench/parser.zig Normal file
View File

@ -0,0 +1,71 @@
//! This benchmark tests the throughput of the terminal escape code parser.
//!
//! To benchmark, this takes an input stream (which is expected to come in
//! as fast as possible), runs it through the parser, and does nothing
//! with the parse result. This bottlenecks and tests the throughput of the
//! parser.
//!
//! Usage:
//!
//! "--f=<path>" - A file to read to parse. If path is "-" then stdin
//! is read. Required.
//!
const std = @import("std");
const ArenaAllocator = std.heap.ArenaAllocator;
const cli_args = @import("../cli_args.zig");
const terminal = @import("../terminal/main.zig");
pub fn main() !void {
// Just use a GPA
const GPA = std.heap.GeneralPurposeAllocator(.{});
var gpa = GPA{};
defer _ = gpa.deinit();
const alloc = gpa.allocator();
// Parse our args
var args: Args = args: {
var args: Args = .{};
errdefer args.deinit();
var iter = try std.process.argsWithAllocator(alloc);
defer iter.deinit();
try cli_args.parse(Args, alloc, &args, &iter);
break :args args;
};
defer args.deinit();
// Read the file for our input
const file = file: {
if (std.mem.eql(u8, args.f, "-"))
break :file std.io.getStdIn();
@panic("file reading not implemented yet");
};
// Read all into memory (TODO: support buffers one day)
const input = try file.reader().readAllAlloc(
alloc,
1024 * 1024 * 1024 * 1024 * 16, // 16 GB
);
defer alloc.free(input);
// Run our parser
var p: terminal.Parser = .{};
for (input) |c| {
const actions = p.next(c);
//std.log.warn("actions={any}", .{actions});
_ = actions;
}
}
const Args = struct {
f: []const u8 = "-",
/// This is set by the CLI parser for deinit.
_arena: ?ArenaAllocator = null,
pub fn deinit(self: *Args) void {
if (self._arena) |arena| arena.deinit();
self.* = undefined;
}
};