diff options
| author | Gabriel Schneider <[email protected]> | 2026-07-05 20:21:50 -0300 |
|---|---|---|
| committer | Gabriel Schneider <[email protected]> | 2026-08-01 15:02:07 -0300 |
| commit | de4def3548a6729b0dfd2120495a61beef8c8c2c (patch) | |
| tree | 12144f0f64dc96bb4741459c1f9db57f44349330 /tests_syntax_perf.zig | |
| parent | 7988d9bc6e31210ff13994f18d5622dd57ba9341 (diff) | |
| download | pardes-de4def3548a6729b0dfd2120495a61beef8c8c2c.tar.gz pardes-de4def3548a6729b0dfd2120495a61beef8c8c2c.zip | |
pardes v2: the rewrite, complete and organized. src/ (core + three shells), test/ (snapshot parity harness + 18 frozen goldens). One sans-IO core, vaxis tty + SDL3 GPU native + wasm web shells, 18/18 parity with the purged prototype, 7.6k lines vs 12.1k. Fix: gui shell pre-sized the core at init so the greet-releasing resize never fired (blank panes until first interaction); live sessions now init at defaults and get the real grid as a resize event (the shell contract, documented on Options).
Diffstat (limited to 'tests_syntax_perf.zig')
| -rw-r--r-- | tests_syntax_perf.zig | 795 |
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); -} |
