summaryrefslogtreecommitdiff
path: root/tests_syntax_perf.zig
diff options
context:
space:
mode:
Diffstat (limited to 'tests_syntax_perf.zig')
-rw-r--r--tests_syntax_perf.zig795
1 files changed, 0 insertions, 795 deletions
diff --git a/tests_syntax_perf.zig b/tests_syntax_perf.zig
deleted file mode 100644
index 4179cd38..00000000
--- a/tests_syntax_perf.zig
+++ /dev/null
@@ -1,795 +0,0 @@
-const std = @import("std");
-const h = @import("e2e_harness.zig");
-
-const Harness = h.Harness;
-const linux = std.os.linux;
-const posix = std.posix;
-
-const payload_len = 64;
-const wheel_events = 240;
-const j_events = 240;
-const ctrl_d_events = 60;
-const delete_line_input = "xxxxxxxxxxxxxxxxd";
-const delete_select_lines = delete_line_input.len - 1;
-const scroll_marker_line = wheel_events;
-const marker_wait_ms = 60_000;
-const scroll_quiet_ms = 1000;
-const large_stdlib_path = "/usr/lib/zig/std/Io/Threaded.zig";
-const target_ratio_x100 = 150;
-const movement_ratio_floor_us = 20_000;
-const startup_first_frame_target_us = 15_000;
-const startup_samples = 5;
-
-fn nowNs() u64 {
- var ts: linux.timespec = undefined;
- _ = linux.clock_gettime(linux.CLOCK.MONOTONIC, &ts);
- return @intCast(ts.sec * 1_000_000_000 + ts.nsec);
-}
-
-fn fillPayload(buf: *[payload_len]u8, marker: []const u8) []const u8 {
- const alphabet = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
- for (buf, 0..) |*ch, i| ch.* = alphabet[(i * 7 + marker.len * 11) % alphabet.len];
- @memcpy(buf[0..marker.len], marker);
- return buf[0..marker.len];
-}
-
-fn waitRawGrowth(hs: *Harness, start_len: usize, ms: i64) !bool {
- const deadline = nowNs() + @as(u64, @intCast(ms)) * 1_000_000;
- var buf: [4096]u8 = undefined;
- while (nowNs() < deadline) {
- var fds = [_]posix.pollfd{.{ .fd = hs.master, .events = posix.POLL.IN, .revents = 0 }};
- _ = posix.poll(&fds, 5) catch {};
- if ((fds[0].revents & posix.POLL.IN) != 0) {
- const n = posix.read(hs.master, &buf) catch break;
- if (n == 0) break;
- try hs.raw.appendSlice(hs.gpa, buf[0..n]);
- hs.stream.nextSlice(buf[0..n]);
- if (hs.raw.items.len > start_len) return true;
- }
- }
- return hs.raw.items.len > start_len;
-}
-
-fn waitForOutputQuiet(hs: *Harness, quiet_ms: i64, max_ms: i64) !bool {
- const deadline = nowNs() + @as(u64, @intCast(max_ms)) * 1_000_000;
- var quiet_deadline = nowNs() + @as(u64, @intCast(quiet_ms)) * 1_000_000;
- var buf: [4096]u8 = undefined;
- while (nowNs() < deadline) {
- if (nowNs() >= quiet_deadline) return true;
- var fds = [_]posix.pollfd{.{ .fd = hs.master, .events = posix.POLL.IN, .revents = 0 }};
- _ = posix.poll(&fds, 10) catch {};
- if ((fds[0].revents & posix.POLL.IN) != 0) {
- const n = posix.read(hs.master, &buf) catch break;
- if (n == 0) break;
- try hs.raw.appendSlice(hs.gpa, buf[0..n]);
- hs.stream.nextSlice(buf[0..n]);
- quiet_deadline = nowNs() + @as(u64, @intCast(quiet_ms)) * 1_000_000;
- }
- }
- return false;
-}
-
-fn leftPaneTopLine(hs: *Harness) !usize {
- const text = try hs.screenText();
- defer hs.gpa.free(text);
- var rows = std.mem.splitScalar(u8, text, '\n');
- var row_idx: usize = 0;
- while (rows.next()) |row| : (row_idx += 1) {
- if (row_idx < 2) continue;
- if (row_idx > 2) break;
- const trimmed = std.mem.trimStart(u8, row, " ");
- var end: usize = 0;
- while (end < trimmed.len and std.ascii.isDigit(trimmed[end])) : (end += 1) {}
- if (end > 0) return std.fmt.parseInt(usize, trimmed[0..end], 10) catch 0;
- }
- return 0;
-}
-
-fn waitForTopLineAbove(hs: *Harness, start_line: usize, ms: i64) !?usize {
- const deadline = nowNs() + @as(u64, @intCast(ms)) * 1_000_000;
- var buf: [4096]u8 = undefined;
- while (nowNs() < deadline) {
- var fds = [_]posix.pollfd{.{ .fd = hs.master, .events = posix.POLL.IN, .revents = 0 }};
- _ = posix.poll(&fds, 10) catch {};
- if ((fds[0].revents & posix.POLL.IN) != 0) {
- const n = posix.read(hs.master, &buf) catch break;
- if (n == 0) break;
- try hs.raw.appendSlice(hs.gpa, buf[0..n]);
- hs.stream.nextSlice(buf[0..n]);
- }
- const top_line = try leftPaneTopLine(hs);
- if (top_line > start_line) return top_line;
- }
- return null;
-}
-
-fn waitForScreenMissing(hs: *Harness, needle: []const u8, ms: i64) !bool {
- {
- const text = try hs.screenText();
- defer hs.gpa.free(text);
- if (std.mem.indexOf(u8, text, needle) == null) return true;
- }
- const deadline = nowNs() + @as(u64, @intCast(ms)) * 1_000_000;
- var buf: [4096]u8 = undefined;
- while (nowNs() < deadline) {
- var fds = [_]posix.pollfd{.{ .fd = hs.master, .events = posix.POLL.IN, .revents = 0 }};
- _ = posix.poll(&fds, 10) catch {};
- if ((fds[0].revents & posix.POLL.IN) != 0) {
- const n = posix.read(hs.master, &buf) catch break;
- if (n == 0) break;
- try hs.raw.appendSlice(hs.gpa, buf[0..n]);
- hs.stream.nextSlice(buf[0..n]);
- }
- const text = try hs.screenText();
- defer hs.gpa.free(text);
- if (std.mem.indexOf(u8, text, needle) == null) return true;
- }
- return false;
-}
-
-fn waitForScreenContainsPoll(hs: *Harness, needle: []const u8, ms: i64, poll_ms: i32) !bool {
- {
- const text = try hs.screenText();
- defer hs.gpa.free(text);
- if (std.mem.indexOf(u8, text, needle) != null) return true;
- }
- const deadline = nowNs() + @as(u64, @intCast(ms)) * 1_000_000;
- var buf: [4096]u8 = undefined;
- while (nowNs() < deadline) {
- var fds = [_]posix.pollfd{.{ .fd = hs.master, .events = posix.POLL.IN, .revents = 0 }};
- _ = posix.poll(&fds, poll_ms) catch {};
- if ((fds[0].revents & posix.POLL.IN) != 0) {
- const n = posix.read(hs.master, &buf) catch break;
- if (n == 0) break;
- try hs.raw.appendSlice(hs.gpa, buf[0..n]);
- hs.stream.nextSlice(buf[0..n]);
- }
- const text = try hs.screenText();
- defer hs.gpa.free(text);
- if (std.mem.indexOf(u8, text, needle) != null) return true;
- }
- return false;
-}
-
-fn waitForRawContainsPoll(hs: *Harness, needle: []const u8, ms: i64, poll_ms: i32) !bool {
- if (std.mem.indexOf(u8, hs.raw.items, needle) != null) return true;
- const deadline = nowNs() + @as(u64, @intCast(ms)) * 1_000_000;
- var buf: [4096]u8 = undefined;
- while (nowNs() < deadline) {
- var fds = [_]posix.pollfd{.{ .fd = hs.master, .events = posix.POLL.IN, .revents = 0 }};
- _ = posix.poll(&fds, poll_ms) catch {};
- if ((fds[0].revents & posix.POLL.IN) != 0) {
- const n = posix.read(hs.master, &buf) catch break;
- if (n == 0) break;
- try hs.raw.appendSlice(hs.gpa, buf[0..n]);
- hs.stream.nextSlice(buf[0..n]);
- if (std.mem.indexOf(u8, hs.raw.items, needle) != null) return true;
- }
- }
- return std.mem.indexOf(u8, hs.raw.items, needle) != null;
-}
-
-const startup_metric_prefix = "PARDES_STARTUP_US=";
-
-fn parseStartupMetric(raw: []const u8) ?u64 {
- const start = std.mem.indexOf(u8, raw, startup_metric_prefix) orelse return null;
- var i = start + startup_metric_prefix.len;
- const digit_start = i;
- while (i < raw.len and std.ascii.isDigit(raw[i])) : (i += 1) {}
- if (i == digit_start) return null;
- return std.fmt.parseInt(u64, raw[digit_start..i], 10) catch null;
-}
-
-fn waitForStartupMetric(hs: *Harness, ms: i64, poll_ms: i32) !?u64 {
- if (parseStartupMetric(hs.raw.items)) |value| return value;
- const deadline = nowNs() + @as(u64, @intCast(ms)) * 1_000_000;
- var buf: [4096]u8 = undefined;
- while (nowNs() < deadline) {
- var fds = [_]posix.pollfd{.{ .fd = hs.master, .events = posix.POLL.IN, .revents = 0 }};
- _ = posix.poll(&fds, poll_ms) catch {};
- if ((fds[0].revents & posix.POLL.IN) != 0) {
- const n = posix.read(hs.master, &buf) catch break;
- if (n == 0) break;
- try hs.raw.appendSlice(hs.gpa, buf[0..n]);
- if (parseStartupMetric(hs.raw.items)) |value| return value;
- }
- }
- return parseStartupMetric(hs.raw.items);
-}
-
-fn wheelDownBurst(gpa: std.mem.Allocator) ![]u8 {
- var out: std.ArrayList(u8) = .empty;
- errdefer out.deinit(gpa);
- var i: usize = 0;
- while (i < wheel_events) : (i += 1)
- try out.appendSlice(gpa, "\x1b[<65;10;6M");
- return out.toOwnedSlice(gpa);
-}
-
-const Movement = enum { wheel, j, ctrl_d };
-
-fn movementName(movement: Movement) []const u8 {
- return switch (movement) {
- .wheel => "wheel",
- .j => "j",
- .ctrl_d => "ctrl_d",
- };
-}
-
-fn movementEventCount(movement: Movement) usize {
- return switch (movement) {
- .wheel => wheel_events,
- .j => j_events,
- .ctrl_d => ctrl_d_events,
- };
-}
-
-fn movementInput(gpa: std.mem.Allocator, movement: Movement) ![]u8 {
- if (movement == .wheel) return wheelDownBurst(gpa);
- var out: std.ArrayList(u8) = .empty;
- errdefer out.deinit(gpa);
- var i: usize = 0;
- const bytes = switch (movement) {
- .wheel => unreachable,
- .j => "j",
- .ctrl_d => "\x04",
- };
- while (i < movementEventCount(movement)) : (i += 1)
- try out.appendSlice(gpa, bytes);
- return out.toOwnedSlice(gpa);
-}
-
-fn readFileAlloc(gpa: std.mem.Allocator, path: [*:0]const u8) ![]u8 {
- const rc = linux.open(path, .{ .ACCMODE = .RDONLY }, 0);
- const sfd: isize = @bitCast(rc);
- if (sfd < 0) return error.OpenFailed;
- const fd: c_int = @intCast(sfd);
- defer _ = linux.close(fd);
-
- var out: std.ArrayList(u8) = .empty;
- errdefer out.deinit(gpa);
- var buf: [16 * 1024]u8 = undefined;
- while (true) {
- const n = posix.read(fd, &buf) catch return error.ReadFailed;
- if (n == 0) break;
- try out.appendSlice(gpa, buf[0..n]);
- }
- return out.toOwnedSlice(gpa);
-}
-
-fn appendZigMarker(gpa: std.mem.Allocator, out: *std.ArrayList(u8), marker: []const u8) !void {
- const line = try std.fmt.allocPrint(gpa, "const {s} = \"visible\";\n", .{marker});
- defer gpa.free(line);
- try out.appendSlice(gpa, line);
-}
-
-fn appendTextMarker(gpa: std.mem.Allocator, out: *std.ArrayList(u8), marker: []const u8) !void {
- const line = try std.fmt.allocPrint(gpa, "{s} visible\n", .{marker});
- defer gpa.free(line);
- try out.appendSlice(gpa, line);
-}
-
-fn appendMarker(gpa: std.mem.Allocator, out: *std.ArrayList(u8), marker: []const u8, highlighted: bool) !void {
- if (highlighted) {
- try appendZigMarker(gpa, out, marker);
- } else {
- try appendTextMarker(gpa, out, marker);
- }
-}
-
-fn makeSmallFixture(gpa: std.mem.Allocator, open_marker: []const u8, scroll_marker: []const u8, highlighted: bool) ![]u8 {
- var out: std.ArrayList(u8) = .empty;
- errdefer out.deinit(gpa);
- try appendMarker(gpa, &out, open_marker, highlighted);
- if (highlighted) {
- try out.appendSlice(gpa, "const std = @import(\"std\");\n");
- try out.appendSlice(gpa, "pub fn perf_entry() usize {\n");
- try out.appendSlice(gpa, " return perf_func_0() + perf_func_199();\n");
- try out.appendSlice(gpa, "}\n");
- }
- var line_no: usize = if (highlighted) 5 else 1;
- var i: usize = 0;
- while (line_no < scroll_marker_line) : ({
- i += 1;
- line_no += 1;
- }) {
- const line = if (highlighted)
- try std.fmt.allocPrint(gpa, "fn perf_func_{d}() usize {{ return {d} + @as(usize, {d}); }}\n", .{ i, i, i % 17 })
- else
- try std.fmt.allocPrint(gpa, "plain perf line {d}: words numbers strings comments\n", .{i});
- defer gpa.free(line);
- try out.appendSlice(gpa, line);
- }
- try appendMarker(gpa, &out, scroll_marker, highlighted);
- line_no += 1;
- while (line_no < scroll_marker_line + 40) : ({
- i += 1;
- line_no += 1;
- }) {
- const line = if (highlighted)
- try std.fmt.allocPrint(gpa, "fn perf_tail_{d}() usize {{ return {d}; }}\n", .{ i, i })
- else
- try std.fmt.allocPrint(gpa, "plain perf tail {d}: words numbers strings comments\n", .{i});
- defer gpa.free(line);
- try out.appendSlice(gpa, line);
- }
- return out.toOwnedSlice(gpa);
-}
-
-fn makeStdlibFixture(gpa: std.mem.Allocator, source: []const u8, open_marker: []const u8, scroll_marker: []const u8, highlighted: bool) ![]u8 {
- var out: std.ArrayList(u8) = .empty;
- errdefer out.deinit(gpa);
- try appendMarker(gpa, &out, open_marker, highlighted);
- var line_no: usize = 1;
- var lines = std.mem.splitScalar(u8, source, '\n');
- while (lines.next()) |line| {
- if (line_no == scroll_marker_line) {
- try appendMarker(gpa, &out, scroll_marker, highlighted);
- line_no += 1;
- }
- try out.appendSlice(gpa, line);
- try out.append(gpa, '\n');
- line_no += 1;
- }
- while (line_no <= scroll_marker_line) : (line_no += 1) {
- if (line_no == scroll_marker_line) {
- try appendMarker(gpa, &out, scroll_marker, highlighted);
- } else if (highlighted) {
- const line = try std.fmt.allocPrint(gpa, "fn perf_padding_{d}() void {{}}\n", .{line_no});
- defer gpa.free(line);
- try out.appendSlice(gpa, line);
- } else {
- const line = try std.fmt.allocPrint(gpa, "plain stdlib padding {d}\n", .{line_no});
- defer gpa.free(line);
- try out.appendSlice(gpa, line);
- }
- }
- return out.toOwnedSlice(gpa);
-}
-
-fn lineCount(content: []const u8) usize {
- return std.mem.count(u8, content, "\n") + 1;
-}
-
-const Fixture = struct {
- name: []const u8,
- path: []const u8,
- open_marker: []const u8,
- edit_marker: []const u8,
- scroll_marker: []const u8,
- highlighted: bool,
- bytes: usize,
- lines: usize,
-};
-
-const BenchResult = struct {
- startup_app_first_frame_us: u64,
- startup_spawn_first_frame_us: u64,
- startup_shell_ready_us: u64,
- open_visible_us: u64,
- open_repaint_us: ?u64,
- edit_us: u64,
- delete_line_us: u64,
- undo_us: u64,
- move_first_us: u64,
- move_done_us: u64,
- move_active_us: u64,
- move_top_line: usize,
-};
-
-const StartupResult = struct {
- internal_first_frame_us: u64,
- harness_first_frame_us: u64,
- spawn_first_frame_us: u64,
-};
-
-const BestOps = struct {
- open_visible_us: u64 = std.math.maxInt(u64),
- edit_us: u64 = std.math.maxInt(u64),
- delete_line_us: u64 = std.math.maxInt(u64),
- undo_us: u64 = std.math.maxInt(u64),
-
- fn add(self: *BestOps, result: BenchResult) void {
- self.open_visible_us = @min(self.open_visible_us, result.open_visible_us);
- self.edit_us = @min(self.edit_us, result.edit_us);
- self.delete_line_us = @min(self.delete_line_us, result.delete_line_us);
- self.undo_us = @min(self.undo_us, result.undo_us);
- }
-};
-
-const StartupStats = struct {
- best_us: u64 = std.math.maxInt(u64),
- worst_us: u64 = 0,
- total_us: u64 = 0,
- count: usize = 0,
-
- fn add(self: *StartupStats, us: u64) void {
- self.best_us = @min(self.best_us, us);
- self.worst_us = @max(self.worst_us, us);
- self.total_us += us;
- self.count += 1;
- }
-
- fn avg(self: StartupStats) u64 {
- if (self.count == 0) return 0;
- return self.total_us / self.count;
- }
-};
-
-fn runFixture(gpa: std.mem.Allocator, exe: [*:0]const u8, fixture: Fixture, movement: Movement) !BenchResult {
- const startup_spawn_t0 = nowNs();
- var hs = try Harness.init(gpa, exe, 30, 100);
- defer hs.deinit();
- const startup_app_t0 = nowNs();
-
- if (!try waitForRawContainsPoll(&hs, "Kill Newcol", 8000, 1)) {
- hs.dump("app first frame did not render before syntax perf");
- return error.StartupFirstFrameTimeout;
- }
- const startup_app_first_frame_us = (nowNs() - startup_app_t0) / 1000;
- const startup_spawn_first_frame_us = (nowNs() - startup_spawn_t0) / 1000;
- try hs.expectWaitFor("tests.zig", 8000, "app never auto-ls'd before syntax perf");
- const startup_shell_ready_us = (nowNs() - startup_spawn_t0) / 1000;
- try hs.send("\x02"); // normal -> tty
- try hs.pump(500);
- {
- var cmd_buf: [256]u8 = undefined;
- const cmd = try std.fmt.bufPrint(&cmd_buf, "clear; echo {s}\r", .{fixture.path});
- try hs.send(cmd);
- }
- try hs.expectWaitFor(fixture.path, 8000, "perf path did not render in tty");
- try hs.send("\x02"); // tty -> normal
- try hs.expectWaitFor("nm /", 6000, "failed to leave tty before syntax perf open");
-
- const open_t0 = nowNs();
- try hs.send("\x1b[<2;8;3M\x1b[<2;8;3m"); // right-click body row 0 path
- try hs.expectWaitFor(fixture.open_marker, marker_wait_ms, "syntax perf file did not open");
- const open_visible_us = (nowNs() - open_t0) / 1000;
- var open_repaint_us: ?u64 = null;
- if (fixture.highlighted) {
- open_repaint_us = open_visible_us;
- }
- if (!try waitForOutputQuiet(&hs, 250, marker_wait_ms)) {
- hs.dump("syntax perf open did not go quiet");
- return error.OpenQuietTimeout;
- }
-
- try hs.send("i");
- try hs.expectWaitFor("in /tmp/pardes_perf_", 5000, "syntax perf did not enter file insert mode");
-
- var payload: [payload_len]u8 = undefined;
- const edit_marker = fillPayload(&payload, fixture.edit_marker);
- const edit_t0 = nowNs();
- try hs.send(&payload);
- try hs.expectWaitFor(edit_marker, marker_wait_ms, "syntax perf edit marker did not appear");
- const edit_us = (nowNs() - edit_t0) / 1000;
-
- try hs.send("\x1b"); // insert -> normal
- try hs.expectWaitFor("nm /tmp/pardes_perf_", 5000, "syntax perf did not leave insert mode");
- if (!try waitForOutputQuiet(&hs, 250, marker_wait_ms)) {
- hs.dump("syntax perf edit repaint did not go quiet");
- return error.EditRepaintQuietTimeout;
- }
-
- const delete_t0 = nowNs();
- try hs.send(delete_line_input);
- if (!try waitForScreenMissing(&hs, edit_marker, marker_wait_ms)) {
- hs.dump("syntax perf line delete marker did not disappear");
- return error.DeleteLineTimeout;
- }
- const delete_line_us = (nowNs() - delete_t0) / 1000;
- if (!try waitForOutputQuiet(&hs, 250, marker_wait_ms)) {
- hs.dump("syntax perf line delete repaint did not go quiet");
- return error.DeleteLineQuietTimeout;
- }
-
- const undo_t0 = nowNs();
- try hs.send("u");
- try hs.expectWaitFor(edit_marker, marker_wait_ms, "syntax perf undo did not restore edit marker");
- const undo_us = (nowNs() - undo_t0) / 1000;
- if (!try waitForOutputQuiet(&hs, 250, marker_wait_ms)) {
- hs.dump("syntax perf undo repaint did not go quiet");
- return error.UndoQuietTimeout;
- }
-
- const move_input = try movementInput(gpa, movement);
- defer gpa.free(move_input);
-
- const start_top_line = @max(try leftPaneTopLine(&hs), 1);
- const move_t0 = nowNs();
- try hs.send(move_input);
- if (try waitForTopLineAbove(&hs, start_top_line, marker_wait_ms)) |_| {
- // Progress observed; keep draining below until the movement settles.
- } else {
- hs.dump("syntax perf movement did not move the visible file window");
- return error.MovementDidNotMove;
- }
- const move_first_us = (nowNs() - move_t0) / 1000;
- if (!try waitForOutputQuiet(&hs, scroll_quiet_ms, marker_wait_ms)) {
- hs.dump("syntax perf movement did not go quiet");
- return error.MovementQuietTimeout;
- }
- const move_done_us = (nowNs() - move_t0) / 1000;
- const move_top_line = try leftPaneTopLine(&hs);
-
- return .{
- .startup_app_first_frame_us = startup_app_first_frame_us,
- .startup_spawn_first_frame_us = startup_spawn_first_frame_us,
- .startup_shell_ready_us = startup_shell_ready_us,
- .open_visible_us = open_visible_us,
- .open_repaint_us = open_repaint_us,
- .edit_us = edit_us,
- .delete_line_us = delete_line_us,
- .undo_us = undo_us,
- .move_first_us = move_first_us,
- .move_done_us = move_done_us,
- .move_active_us = move_done_us -| (@as(u64, scroll_quiet_ms) * 1000),
- .move_top_line = move_top_line,
- };
-}
-
-fn runStartup(gpa: std.mem.Allocator, exe: [*:0]const u8) !StartupResult {
- const startup_spawn_t0 = nowNs();
- var hs = try Harness.initArgs(gpa, exe, 30, 100, "--perf-startup");
- defer hs.deinit();
- const startup_app_t0 = nowNs();
-
- const internal_first_frame_us = (try waitForStartupMetric(&hs, 8000, 1)) orelse {
- hs.dump("app first frame did not render before startup perf");
- return error.StartupFirstFrameTimeout;
- };
- if (!try waitForRawContainsPoll(&hs, "Kill Newcol", 8000, 1)) {
- hs.dump("app first frame marker rendered without topbar text");
- return error.StartupFirstFrameTimeout;
- }
- return .{
- .internal_first_frame_us = internal_first_frame_us,
- .harness_first_frame_us = (nowNs() - startup_app_t0) / 1000,
- .spawn_first_frame_us = (nowNs() - startup_spawn_t0) / 1000,
- };
-}
-
-fn printResult(fixture: Fixture, movement: Movement, result: BenchResult) void {
- std.debug.print(
- "case={s} movement={s} highlight={d} bytes={d} lines={d} startup_app_first_frame_us={d} startup_spawn_first_frame_us={d} startup_shell_ready_us={d} open_visible_us={d} open_repaint_us={d} edit_us={d} edit_us_per_char={d} delete_line_us={d} undo_us={d} move_first_us={d} move_done_us={d} move_active_us={d} move_top_line={d}\n",
- .{
- fixture.name,
- movementName(movement),
- @intFromBool(fixture.highlighted),
- fixture.bytes,
- fixture.lines,
- result.startup_app_first_frame_us,
- result.startup_spawn_first_frame_us,
- result.startup_shell_ready_us,
- result.open_visible_us,
- result.open_repaint_us orelse 0,
- result.edit_us,
- result.edit_us / payload_len,
- result.delete_line_us,
- result.undo_us,
- result.move_first_us,
- result.move_done_us,
- result.move_active_us,
- result.move_top_line,
- },
- );
-}
-
-fn ratioX100(highlight_us: u64, plain_us: u64) u64 {
- if (plain_us == 0) return 0;
- return (highlight_us * 100 + plain_us / 2) / plain_us;
-}
-
-fn movementRatioX100(highlight_us: u64, plain_us: u64) u64 {
- return ratioX100(highlight_us, @max(plain_us, movement_ratio_floor_us));
-}
-
-fn printRatioSummary(movement: Movement, small_txt: BenchResult, small_zig: BenchResult, large_txt: BenchResult, large_zig: BenchResult) void {
- std.debug.print(
- "ratio movement={s} small_move_active_x100={d} large_move_active_x100={d} target_x100={d} small_open_visible_x100={d} large_open_visible_x100={d} small_edit_x100={d} large_edit_x100={d} small_delete_line_x100={d} large_delete_line_x100={d} small_undo_x100={d} large_undo_x100={d}\n",
- .{
- movementName(movement),
- movementRatioX100(small_zig.move_active_us, small_txt.move_active_us),
- movementRatioX100(large_zig.move_active_us, large_txt.move_active_us),
- target_ratio_x100,
- ratioX100(small_zig.open_visible_us, small_txt.open_visible_us),
- ratioX100(large_zig.open_visible_us, large_txt.open_visible_us),
- ratioX100(small_zig.edit_us, small_txt.edit_us),
- ratioX100(large_zig.edit_us, large_txt.edit_us),
- ratioX100(small_zig.delete_line_us, small_txt.delete_line_us),
- ratioX100(large_zig.delete_line_us, large_txt.delete_line_us),
- ratioX100(small_zig.undo_us, small_txt.undo_us),
- ratioX100(large_zig.undo_us, large_txt.undo_us),
- },
- );
-}
-
-fn checkMovementRatio(movement: Movement, size_name: []const u8, highlighted_us: u64, plain_us: u64) !void {
- const ratio = movementRatioX100(highlighted_us, plain_us);
- if (ratio > target_ratio_x100) {
- std.debug.print(
- "FAIL movement={s} size={s} move_active_x100={d} target_x100={d} highlighted_us={d} plain_us={d}\n",
- .{ movementName(movement), size_name, ratio, target_ratio_x100, highlighted_us, plain_us },
- );
- return error.HighlightMovementPerfRegression;
- }
-}
-
-fn checkMovementRatios(movement: Movement, small_txt: BenchResult, small_zig: BenchResult, large_txt: BenchResult, large_zig: BenchResult) !void {
- try checkMovementRatio(movement, "small", small_zig.move_active_us, small_txt.move_active_us);
- try checkMovementRatio(movement, "large", large_zig.move_active_us, large_txt.move_active_us);
-}
-
-fn printBestOpsRatioSummary(best: [4]BestOps) void {
- std.debug.print(
- "ratio aggregate=best_of_movements target_x100={d} small_open_visible_x100={d} large_open_visible_x100={d} small_edit_x100={d} large_edit_x100={d} small_delete_line_x100={d} large_delete_line_x100={d} small_undo_x100={d} large_undo_x100={d}\n",
- .{
- target_ratio_x100,
- ratioX100(best[1].open_visible_us, best[0].open_visible_us),
- ratioX100(best[3].open_visible_us, best[2].open_visible_us),
- ratioX100(best[1].edit_us, best[0].edit_us),
- ratioX100(best[3].edit_us, best[2].edit_us),
- ratioX100(best[1].delete_line_us, best[0].delete_line_us),
- ratioX100(best[3].delete_line_us, best[2].delete_line_us),
- ratioX100(best[1].undo_us, best[0].undo_us),
- ratioX100(best[3].undo_us, best[2].undo_us),
- },
- );
-}
-
-fn printStartupSummary(name: []const u8, target_us: ?u64, stats: StartupStats) void {
- if (target_us) |target| {
- std.debug.print(
- "startup summary metric={s} target_us={d} samples={d} best_us={d} avg_us={d} worst_us={d}\n",
- .{ name, target, stats.count, stats.best_us, stats.avg(), stats.worst_us },
- );
- return;
- }
- std.debug.print(
- "startup summary metric={s} samples={d} best_us={d} avg_us={d} worst_us={d}\n",
- .{ name, stats.count, stats.best_us, stats.avg(), stats.worst_us },
- );
-}
-
-fn checkStartupAverage(name: []const u8, target_us: u64, stats: StartupStats) !void {
- const avg_us = stats.avg();
- if (avg_us <= target_us) return;
- std.debug.print(
- "FAIL startup metric={s} avg_us={d} target_us={d} samples={d} best_us={d} worst_us={d}\n",
- .{ name, avg_us, target_us, stats.count, stats.best_us, stats.worst_us },
- );
- return error.StartupPerfRegression;
-}
-
-pub fn main(init: std.process.Init) !void {
- const gpa = init.gpa;
- const args = try init.minimal.args.toSlice(init.arena.allocator());
- if (args.len != 2) {
- std.debug.print("usage: {s} <pardes-exe>\n", .{args[0]});
- return error.BadArgs;
- }
-
- const small_txt_path = "/tmp/pardes_perf_small_plain.txt";
- const small_zig_path = "/tmp/pardes_perf_small_code.zig";
- const large_txt_path = "/tmp/pardes_perf_large_plain.txt";
- const large_zig_path = "/tmp/pardes_perf_large_code.zig";
-
- const stdlib_content = try readFileAlloc(gpa, large_stdlib_path);
- defer gpa.free(stdlib_content);
- const small_txt_content = try makeSmallFixture(gpa, "SMALLTXTOPENPERFEND", "SMALLTXTSCROLLPERFEND", false);
- defer gpa.free(small_txt_content);
- const small_zig_content = try makeSmallFixture(gpa, "SMALLZIGOPENPERFEND", "SMALLZIGSCROLLPERFEND", true);
- defer gpa.free(small_zig_content);
- const large_txt_content = try makeStdlibFixture(gpa, stdlib_content, "LARGETXTOPENPERFEND", "LARGETXTSCROLLPERFEND", false);
- defer gpa.free(large_txt_content);
- const large_zig_content = try makeStdlibFixture(gpa, stdlib_content, "LARGEZIGOPENPERFEND", "LARGEZIGSCROLLPERFEND", true);
- defer gpa.free(large_zig_content);
-
- try h.writeFile(small_txt_path, small_txt_content);
- try h.writeFile(small_zig_path, small_zig_content);
- try h.writeFile(large_txt_path, large_txt_content);
- try h.writeFile(large_zig_path, large_zig_content);
-
- const fixtures = [_]Fixture{
- .{
- .name = "small_txt",
- .path = small_txt_path,
- .open_marker = "SMALLTXTOPENPERFEND",
- .edit_marker = "SMALLTXTEDITPERFEND",
- .scroll_marker = "SMALLTXTSCROLLPERFEND",
- .highlighted = false,
- .bytes = small_txt_content.len,
- .lines = lineCount(small_txt_content),
- },
- .{
- .name = "small_zig",
- .path = small_zig_path,
- .open_marker = "SMALLZIGOPENPERFEND",
- .edit_marker = "SMALLZIGEDITPERFEND",
- .scroll_marker = "SMALLZIGSCROLLPERFEND",
- .highlighted = true,
- .bytes = small_zig_content.len,
- .lines = lineCount(small_zig_content),
- },
- .{
- .name = "large_txt",
- .path = large_txt_path,
- .open_marker = "LARGETXTOPENPERFEND",
- .edit_marker = "LARGETXTEDITPERFEND",
- .scroll_marker = "LARGETXTSCROLLPERFEND",
- .highlighted = false,
- .bytes = large_txt_content.len,
- .lines = lineCount(large_txt_content),
- },
- .{
- .name = "large_zig",
- .path = large_zig_path,
- .open_marker = "LARGEZIGOPENPERFEND",
- .edit_marker = "LARGEZIGEDITPERFEND",
- .scroll_marker = "LARGEZIGSCROLLPERFEND",
- .highlighted = true,
- .bytes = large_zig_content.len,
- .lines = lineCount(large_zig_content),
- },
- };
-
- std.debug.print(
- "syntax-perf backend=vaxis chars_per_edit={d} delete_select_lines={d} wheel_events={d} j_events={d} ctrl_d_events={d} scroll_quiet_ms={d} scroll_marker_line={d} large_stdlib={s}\n",
- .{ payload_len, delete_select_lines, wheel_events, j_events, ctrl_d_events, scroll_quiet_ms, scroll_marker_line, large_stdlib_path },
- );
- var startup_only_internal_stats: StartupStats = .{};
- var startup_only_harness_stats: StartupStats = .{};
- var startup_only_spawn_stats: StartupStats = .{};
- for (0..startup_samples) |sample| {
- const startup = try runStartup(gpa, args[1].ptr);
- startup_only_internal_stats.add(startup.internal_first_frame_us);
- startup_only_harness_stats.add(startup.harness_first_frame_us);
- startup_only_spawn_stats.add(startup.spawn_first_frame_us);
- std.debug.print(
- "startup_sample={d} internal_first_frame_us={d} harness_first_frame_us={d} spawn_first_frame_us={d}\n",
- .{ sample, startup.internal_first_frame_us, startup.harness_first_frame_us, startup.spawn_first_frame_us },
- );
- }
- printStartupSummary("startup_only_internal_first_frame", null, startup_only_internal_stats);
- printStartupSummary("startup_only_harness_first_frame", startup_first_frame_target_us, startup_only_harness_stats);
- printStartupSummary("startup_only_spawn_first_frame", null, startup_only_spawn_stats);
- try checkStartupAverage("startup_only_harness_first_frame", startup_first_frame_target_us, startup_only_harness_stats);
-
- const movements = [_]Movement{ .wheel, .j, .ctrl_d };
- var best_ops: [4]BestOps = .{ .{}, .{}, .{}, .{} };
- var startup_app_first_frame_stats: StartupStats = .{};
- var startup_spawn_first_frame_stats: StartupStats = .{};
- var startup_shell_ready_stats: StartupStats = .{};
- for (movements) |movement| {
- const small_txt = try runFixture(gpa, args[1].ptr, fixtures[0], movement);
- startup_app_first_frame_stats.add(small_txt.startup_app_first_frame_us);
- startup_spawn_first_frame_stats.add(small_txt.startup_spawn_first_frame_us);
- startup_shell_ready_stats.add(small_txt.startup_shell_ready_us);
- best_ops[0].add(small_txt);
- printResult(fixtures[0], movement, small_txt);
- const small_zig = try runFixture(gpa, args[1].ptr, fixtures[1], movement);
- startup_app_first_frame_stats.add(small_zig.startup_app_first_frame_us);
- startup_spawn_first_frame_stats.add(small_zig.startup_spawn_first_frame_us);
- startup_shell_ready_stats.add(small_zig.startup_shell_ready_us);
- best_ops[1].add(small_zig);
- printResult(fixtures[1], movement, small_zig);
- const large_txt = try runFixture(gpa, args[1].ptr, fixtures[2], movement);
- startup_app_first_frame_stats.add(large_txt.startup_app_first_frame_us);
- startup_spawn_first_frame_stats.add(large_txt.startup_spawn_first_frame_us);
- startup_shell_ready_stats.add(large_txt.startup_shell_ready_us);
- best_ops[2].add(large_txt);
- printResult(fixtures[2], movement, large_txt);
- const large_zig = try runFixture(gpa, args[1].ptr, fixtures[3], movement);
- startup_app_first_frame_stats.add(large_zig.startup_app_first_frame_us);
- startup_spawn_first_frame_stats.add(large_zig.startup_spawn_first_frame_us);
- startup_shell_ready_stats.add(large_zig.startup_shell_ready_us);
- best_ops[3].add(large_zig);
- printResult(fixtures[3], movement, large_zig);
- printRatioSummary(movement, small_txt, small_zig, large_txt, large_zig);
- try checkMovementRatios(movement, small_txt, small_zig, large_txt, large_zig);
- }
- printBestOpsRatioSummary(best_ops);
- printStartupSummary("app_first_frame", null, startup_app_first_frame_stats);
- printStartupSummary("spawn_first_frame", null, startup_spawn_first_frame_stats);
- printStartupSummary("shell_ready", null, startup_shell_ready_stats);
-}