From 817d818d4c807cd1848872f762f315b1dd0d6edd Mon Sep 17 00:00:00 2001 From: Yves Biener Date: Sat, 9 Nov 2024 01:17:09 +0100 Subject: [PATCH] feat(benchmark): add benchmark build (with build option) to test frame rate --- README.md | 8 ++++---- build.zig | 9 +++++++++ src/main.zig | 42 ++++++++++++++++++++++++++++++++++++++++-- 3 files changed, 53 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index a0ae101..3e13cdf 100644 --- a/README.md +++ b/README.md @@ -16,11 +16,11 @@ It contains information about me and my projects as well as blog entries about s - [ ] How can I support to run a sub-process inside of a given pane / layout? - [ ] Create demo gifs using [vhs](https://github.com/charmbracelet/vhs) -- [ ] How would I measure my FPS? -- [ ] Could I simulate a corresponding event loop? - - empty user event - - emmit as many as possible through another thread (until the event queue is full?) + +- [x] Could I simulate a corresponding event loop? + - emmit as many as possible through another thread (until the event queue is full?) [1023] - see how fast the application can render each frame and measure the necessary time for each _frame_? -> determine statistics like, min, max, median, mean, etc. -> Or buffered writer to the `std.posix.STDOUT_FILENO`? -> I could use this to see if it makes sense to implement a buffered version using a screen buffer (to only render the differences?) + - seems pretty good (with some exceptions) diff --git a/build.zig b/build.zig index 6fac23a..f05cf3a 100644 --- a/build.zig +++ b/build.zig @@ -15,6 +15,14 @@ pub fn build(b: *std.Build) void { // set a preferred release mode, allowing the user to decide how to optimize. const optimize = b.standardOptimizeOption(.{}); + // build options to customize the log message formatting + const benchmark = b.option(bool, "benchmark", "Create a benchmark build (default: false)") orelse false; + + const options = b.addOptions(); + options.addOption(bool, "benchmark", benchmark); + + const options_module = options.createModule(); + // Dependencies const vaxis_dep = b.dependency("vaxis", .{ .optimize = optimize, @@ -37,6 +45,7 @@ pub fn build(b: *std.Build) void { .target = target, .optimize = optimize, }); + exe.root_module.addImport("build_options", options_module); exe.root_module.addImport("vaxis", vaxis_dep.module("vaxis")); exe.root_module.addImport("zlog", zlog_dep.module("zlog")); exe.root_module.addImport("zmd", zmd_dep.module("zmd")); diff --git a/src/main.zig b/src/main.zig index f950a39..6a5f95d 100644 --- a/src/main.zig +++ b/src/main.zig @@ -1,3 +1,4 @@ +const build_options = @import("build_options"); const std = @import("std"); const terminal = @import("terminal.zig"); const zlog = @import("zlog"); @@ -38,6 +39,14 @@ pub fn main() !void { try app.start(); defer app.stop() catch unreachable; + // Benchmarking + var instants: std.ArrayList(u64) = undefined; + var benchmark_thread: std.Thread = undefined; + if (comptime build_options.benchmark) { + instants = try std.ArrayList(u64).initCapacity(allocator, 512); + benchmark_thread = try std.Thread.spawn(.{}, benchmark, .{&app}); + } + // App.Event loop while (true) { const event = app.nextEvent(); @@ -68,15 +77,44 @@ pub fn main() !void { }, else => {}, } + + var start_instant: std.time.Instant = undefined; + if (comptime build_options.benchmark) { + start_instant = try std.time.Instant.now(); + } + // NOTE: this currently re-renders the screen for every key-press -> which might be a bit of an overkill const events = try layout.handle(event); for (events.items) |e| { app.postEvent(e); } try renderer.render(try layout.content()); + + if (comptime build_options.benchmark) { + const end_instant = try std.time.Instant.now(); + try instants.append(end_instant.since(start_instant)); + } } - // TODO: I could use the ascii codes in vaxis - // - see https://gist.github.com/ConnerWill/d4b6c776b509add763e17f9f113fd25b + + if (comptime build_options.benchmark) { + benchmark_thread.join(); + // print benchmark results + for (instants.items, 1..) |epoch, i| { + _ = i; + const epoch_float: f64 = @floatFromInt(epoch); + std.log.info("{d:.3}", .{epoch_float / std.time.ns_per_ms}); + } + instants.deinit(); + } +} + +fn benchmark(app: *App) void { + std.time.sleep(1 * std.time.ns_per_s); + for (0..511) |_| { + app.postEvent(.{ .key = .{ .cp = 'j' } }); + app.postEvent(.{ .key = .{ .cp = 'k' } }); + } + app.quit(); } test {