aboutsummaryrefslogtreecommitdiff
path: root/vendor/libghostty-vt/src/benchmark/Benchmark.zig
diff options
context:
space:
mode:
authorCan Celik <ogulcancelik@gmail.com>2026-04-04 19:22:04 +0300
committerCan Celik <ogulcancelik@gmail.com>2026-04-05 01:23:06 +0300
commit41dd733ca7123c9ac4bfc6c781697a660e95b052 (patch)
tree3fc91815ca139ef2e0b281bba0abda4ef15175f4 /vendor/libghostty-vt/src/benchmark/Benchmark.zig
parent55c138263e190a1e79d4b997bfb9397306c0ec1f (diff)
feat: finish ghostty pane backend integration
Diffstat (limited to 'vendor/libghostty-vt/src/benchmark/Benchmark.zig')
-rw-r--r--vendor/libghostty-vt/src/benchmark/Benchmark.zig178
1 files changed, 178 insertions, 0 deletions
diff --git a/vendor/libghostty-vt/src/benchmark/Benchmark.zig b/vendor/libghostty-vt/src/benchmark/Benchmark.zig
new file mode 100644
index 00000000..41c3695d
--- /dev/null
+++ b/vendor/libghostty-vt/src/benchmark/Benchmark.zig
@@ -0,0 +1,178 @@
+//! A single benchmark case.
+const Benchmark = @This();
+
+const std = @import("std");
+const builtin = @import("builtin");
+const assert = std.debug.assert;
+const macos = @import("macos");
+const build_config = @import("../build_config.zig");
+
+ptr: *anyopaque,
+vtable: VTable,
+
+/// Create a new benchmark from a pointer and a vtable.
+///
+/// This usually is only called by benchmark implementations, not
+/// benchmark users.
+pub fn init(
+ pointer: anytype,
+ vtable: VTable,
+) Benchmark {
+ const Ptr = @TypeOf(pointer);
+ assert(@typeInfo(Ptr) == .pointer); // Must be a pointer
+ assert(@typeInfo(Ptr).pointer.size == .one); // Must be a single-item pointer
+ assert(@typeInfo(@typeInfo(Ptr).pointer.child) == .@"struct"); // Must point to a struct
+ return .{ .ptr = pointer, .vtable = vtable };
+}
+
+/// Run the benchmark.
+pub fn run(
+ self: Benchmark,
+ mode: RunMode,
+) Error!RunResult {
+ // Run our setup function if it exists. We do this first because
+ // we don't want this part of our benchmark and we want to fail fast.
+ if (self.vtable.setupFn) |func| try func(self.ptr);
+ defer if (self.vtable.teardownFn) |func| func(self.ptr);
+
+ // Our result accumulator. This will be returned at the end of the run.
+ var result: RunResult = .{};
+
+ // If we're on macOS, we setup signposts so its easier to find
+ // the results in Instruments. There's a lot of nasty comptime stuff
+ // here but its just to ensure this does nothing on other platforms.
+ const signpost_name = "ghostty";
+ const signpost: if (builtin.target.os.tag.isDarwin()) struct {
+ log: *macos.os.Log,
+ id: macos.os.signpost.Id,
+ } else void = if (builtin.target.os.tag.isDarwin()) darwin: {
+ macos.os.signpost.init();
+ const log = macos.os.Log.create(
+ build_config.bundle_id,
+ macos.os.signpost.Category.points_of_interest,
+ );
+ const id = macos.os.signpost.Id.forPointer(log, self.ptr);
+ macos.os.signpost.intervalBegin(log, id, signpost_name);
+ break :darwin .{ .log = log, .id = id };
+ } else {};
+ defer if (comptime builtin.target.os.tag.isDarwin()) {
+ macos.os.signpost.intervalEnd(
+ signpost.log,
+ signpost.id,
+ signpost_name,
+ );
+ signpost.log.release();
+ };
+
+ const start = std.time.Instant.now() catch return error.BenchmarkFailed;
+ while (true) {
+ // Run our step function. If it fails, we return the error.
+ try self.vtable.stepFn(self.ptr);
+ result.iterations += 1;
+
+ // Get our current monotonic time and check our exit conditions.
+ const now = std.time.Instant.now() catch return error.BenchmarkFailed;
+ const exit = switch (mode) {
+ .once => true,
+ .duration => |ns| now.since(start) >= ns,
+ };
+
+ if (exit) {
+ result.duration = now.since(start);
+ return result;
+ }
+ }
+
+ // We exit within the loop body.
+ unreachable;
+}
+
+/// The type of benchmark run. This is used to determine how the benchmark
+/// is executed.
+pub const RunMode = union(enum) {
+ /// Run the benchmark exactly once.
+ once,
+
+ /// Run the benchmark for a fixed duration in nanoseconds. This
+ /// will not interrupt a running step so if the granularity of the
+ /// duration is too low, benchmark results may be inaccurate.
+ duration: u64,
+};
+
+/// The result of a benchmark run.
+pub const RunResult = struct {
+ /// The total iterations that step was executed. For "once" run
+ /// modes this will always be 1.
+ iterations: u32 = 0,
+
+ /// The total time taken for the run. For "duration" run modes
+ /// this will be relatively close to the requested duration.
+ /// The units are nanoseconds.
+ duration: u64 = 0,
+};
+
+/// The possible errors that can occur during various stages of the
+/// benchmark. Right now its just "failure" which ends the benchmark.
+pub const Error = error{BenchmarkFailed};
+
+/// The vtable that must be provided to invoke the real implementation.
+pub const VTable = struct {
+ /// A single step to execute the benchmark. This should do the work
+ /// that is under test. This may be called multiple times if we're
+ /// testing throughput.
+ stepFn: *const fn (ptr: *anyopaque) Error!void,
+
+ /// Setup and teardown functions. These are called once before
+ /// the first step and once after the last step. They are not part
+ /// of the benchmark results (unless you're benchmarking the full
+ /// binary).
+ setupFn: ?*const fn (ptr: *anyopaque) Error!void = null,
+ teardownFn: ?*const fn (ptr: *anyopaque) void = null,
+};
+
+test Benchmark {
+ // This test fails on FreeBSD and Windows so skip:
+ //
+ // /home/runner/work/ghostty/ghostty/src/benchmark/Benchmark.zig:165:5: 0x3cd2de1 in decltest.Benchmark (ghostty-test)
+ // try testing.expect(result.duration > 0);
+ // ^
+ switch (builtin.os.tag) {
+ .freebsd,
+ .windows,
+ => return error.SkipZigTest,
+ else => {},
+ }
+
+ const testing = std.testing;
+ const Simple = struct {
+ const Self = @This();
+
+ setup_i: usize = 0,
+ step_i: usize = 0,
+
+ pub fn benchmark(self: *Self) Benchmark {
+ return .init(self, .{
+ .stepFn = step,
+ .setupFn = setup,
+ });
+ }
+
+ fn setup(ptr: *anyopaque) Error!void {
+ const self: *Self = @ptrCast(@alignCast(ptr));
+ self.setup_i += 1;
+ }
+
+ fn step(ptr: *anyopaque) Error!void {
+ const self: *Self = @ptrCast(@alignCast(ptr));
+ self.step_i += 1;
+ }
+ };
+
+ var s: Simple = .{};
+ const b = s.benchmark();
+ const result = try b.run(.once);
+ try testing.expectEqual(1, s.setup_i);
+ try testing.expectEqual(1, s.step_i);
+ try testing.expectEqual(1, result.iterations);
+ try testing.expect(result.duration > 0);
+}