diff options
| author | Mitchell Hashimoto <m@mitchellh.com> | 2025-07-08 09:12:03 -0700 |
|---|---|---|
| committer | Mitchell Hashimoto <m@mitchellh.com> | 2025-07-09 15:06:24 -0700 |
| commit | b8f5cf9d52506add7d30cacc2142a79949f76ea3 (patch) | |
| tree | 93699d38f3a0c16cca4d23ee391904df307deef0 /src/benchmark/TerminalStream.zig | |
| parent | 1739418f6f6fab3bb7df9c2c84eba91ddabe91b2 (diff) | |
initial `ghostty-bench` program
Diffstat (limited to 'src/benchmark/TerminalStream.zig')
| -rw-r--r-- | src/benchmark/TerminalStream.zig | 56 |
1 files changed, 52 insertions, 4 deletions
diff --git a/src/benchmark/TerminalStream.zig b/src/benchmark/TerminalStream.zig index 8580695f2..6300ba04c 100644 --- a/src/benchmark/TerminalStream.zig +++ b/src/benchmark/TerminalStream.zig @@ -14,36 +14,49 @@ const TerminalStream = @This(); const std = @import("std"); +const assert = std.debug.assert; const Allocator = std.mem.Allocator; const terminalpkg = @import("../terminal/main.zig"); const Benchmark = @import("Benchmark.zig"); const Terminal = terminalpkg.Terminal; const Stream = terminalpkg.Stream(*Handler); +opts: Options, terminal: Terminal, handler: Handler, stream: Stream, +/// The file, opened in the setup function. +data_f: ?std.fs.File = null, + pub const Options = struct { /// The size of the terminal. This affects benchmarking when /// dealing with soft line wrapping and the memory impact /// of page sizes. @"terminal-rows": u16 = 80, @"terminal-cols": u16 = 120, + + /// The data to read as a filepath. If this is "-" then + /// we will read stdin. If this is unset, then we will + /// do nothing (benchmark is a noop). It'd be more unixy to + /// use stdin by default but I find that a hanging CLI command + /// with no interaction is a bit annoying. + data: ?[]const u8 = null, }; /// Create a new terminal stream handler for the given arguments. pub fn create( alloc: Allocator, - args: Options, + opts: Options, ) !*TerminalStream { const ptr = try alloc.create(TerminalStream); errdefer alloc.destroy(ptr); ptr.* = .{ + .opts = opts, .terminal = try .init(alloc, .{ - .rows = args.@"terminal-rows", - .cols = args.@"terminal-cols", + .rows = opts.@"terminal-rows", + .cols = opts.@"terminal-cols", }), .handler = .{ .t = &ptr.terminal }, .stream = .{ .handler = &ptr.handler }, @@ -61,17 +74,52 @@ pub fn benchmark(self: *TerminalStream) Benchmark { return .init(self, .{ .stepFn = step, .setupFn = setup, + .teardownFn = teardown, }); } fn setup(ptr: *anyopaque) Benchmark.Error!void { const self: *TerminalStream = @ptrCast(@alignCast(ptr)); + + // Always reset our terminal state self.terminal.fullReset(); + + // Open our data file to prepare for reading. We can do more + // validation here eventually. + assert(self.data_f == null); + if (self.opts.data) |path| { + self.data_f = std.fs.cwd().openFile(path, .{}) catch + return error.BenchmarkFailed; + } +} + +fn teardown(ptr: *anyopaque) void { + const self: *TerminalStream = @ptrCast(@alignCast(ptr)); + if (self.data_f) |f| { + f.close(); + self.data_f = null; + } } fn step(ptr: *anyopaque) Benchmark.Error!void { const self: *TerminalStream = @ptrCast(@alignCast(ptr)); - _ = self; + + // Get our buffered reader so we're not predominantly + // waiting on file IO. It'd be better to move this fully into + // memory. If we're IO bound though that should show up on + // the benchmark results and... I know writing this that we + // aren't currently IO bound. + const f = self.data_f orelse return; + var r = std.io.bufferedReader(f.reader()); + + var buf: [4096]u8 = undefined; + while (true) { + const n = r.read(&buf) catch return error.BenchmarkFailed; + if (n == 0) break; // EOF reached + const chunk = buf[0..n]; + self.stream.nextSlice(chunk) catch + return error.BenchmarkFailed; + } } /// Implements the handler interface for the terminal.Stream. |
