diff options
| author | Gabriel Schneider <[email protected]> | 2026-07-31 07:32:15 -0300 |
|---|---|---|
| committer | Gabriel Schneider <[email protected]> | 2026-08-01 15:02:08 -0300 |
| commit | 560ae0a63f7b5d354bad3ba13dd8ab26b5f0a870 (patch) | |
| tree | db2762cc2bc7bdeaae187e15cfc68a33ef2d1c48 /build.zig | |
| parent | 5bf8d6dd077517270377e5d8551108ecf252374f (diff) | |
| download | pardes-560ae0a63f7b5d354bad3ba13dd8ab26b5f0a870.tar.gz pardes-560ae0a63f7b5d354bad3ba13dd8ab26b5f0a870.zip | |
motion, scrolling and redraw are flat in file size now
zig build perf drives the core directly — event, effects, one frame, no pty —
over four generated fixtures: 1k lines, 50k, 300k, and 400 lines of 8000
columns, because a file that is long and a file that is wide fail differently.
Every sample seeks somewhere else in the file first, since measuring at line 3
of a 300k-line file hides exactly the bug.
perf record said half the run was scanning for newlines from byte 0. So File
carries a line index, built on demand and invalidated in exactly ONE place —
setContent, the funnel every content swap already goes through. That killed the
scrollbar's per-frame line count (12.6% of the whole run by itself), scrollBy,
ensureCursorVisible, lastNavRow, the syntax window bounds and two O(scroll)
walks. normalKey computed max_line as a const at the top: two full passes over
the buffer on every keystroke of every kind, for three g/G branches. It is lazy
now. The modal primitives each walked the text twice for the same line.
And the visible window was re-parsed on every scrolled row — a third of a
megabyte per keypress on the wide fixture. The highlighted range is remembered,
a scroll inside it is free, and only a re-parse that FOLLOWS a scroll takes
slack: doing it unconditionally made typing 2.1x slower, since every character
paid for a band it could never amortise.
One j on a 19 MB file: 37.8ms -> 266us. Render: 4.1ms -> 77us. Open costs 1.25x
more for the one extra pass, which buys 54x on every frame after, and 8 bytes
per line of memory.
Left standing, measured and named: edit-char is 14ms on 19MB because content is
immutable and every keystroke copies the buffer. A third of that is the index
rebuild, which could be a shift if setContent knew the edit offset; the rest
wants a rope. bodyText's double copy and Surface.print's per-cell decode never
rose above 2% of the profile afterwards, so they were left alone.
No golden moved.
Diffstat (limited to 'build.zig')
| -rw-r--r-- | build.zig | 20 |
1 files changed, 20 insertions, 0 deletions
@@ -670,6 +670,26 @@ pub fn build(b: *std.Build) void { run_lspbench.setCwd(b.path(".")); b.step("lspbench", "language-backend latency + feature matrix (-- [--json] [repo-root])").dependOn(&run_lspbench.step); + // the editing scoreboard: one gesture, one file size, one number. + // ReleaseFast for the same reason lspbench is — a Debug build measures + // safety checks, and the question here is what the algorithm costs. + // Same core module again, so the `render` this times is the editor's. + const perf = b.addExecutable(.{ + .name = "pardes-perf", + .root_module = b.createModule(.{ + .target = target, + .optimize = .ReleaseFast, + .root_source_file = b.path("test/perf.zig"), + .link_libc = true, + }), + }); + perf.root_module.addImport("pardes", hx_core_mod); + b.installArtifact(perf); + const run_perf = b.addRunArtifact(perf); + if (b.args) |args| run_perf.addArgs(args); + run_perf.setCwd(b.path(".")); + b.step("perf", "large-file / long-line latency table (-- [--json] [--reps N] [--base old.json])").dependOn(&run_perf.step); + // modal.zig is pure std — its inline unit tests run here const unit = b.addTest(.{ .root_module = b.createModule(.{ .target = target, |
