terminal: bound page map probe lengths

#13292

Page maps previously allowed a 100% load factor. Once live entries
and tombstones filled every slot, a missing-key lookup or insertion
could scan the entire map.

Reserve 20% of each offset hash map as insertion headroom and track
that budget separately from the live count. Port the allocation-free
in-place rehash from Zig HashMapUnmanaged so canonical insertion can
rebuild fragmented probes and restore tombstone-exhausted headroom
without growing the page. Assumed-capacity insertion now applies the
same guard, preventing the headroom counter from wrapping.

Pass the exact requested hyperlink count into map layout before
load-factor scaling, avoiding a redundant power-of-two rounding step.
Screen-level recovery now grows only when live hyperlinks actually fill
usable capacity.

ReleaseFast terminal benchmarks compare main with the combined map
changes:

| workload | before | after | speedup |
|---|---:|---:|---:|
| map churn | 1.253 s | 12.5 ms | 100x |
| full OSC 8 stream | 3.576 s | 75.6 ms | 47x |
| clear and redraw | 299.1 ms | 118.3 ms | 2.5x |

Co-authored-by: Tim Culverhouse <tim@timculverhouse.com>
This commit is contained in:
Mitchell Hashimoto
2026-07-11 09:21:27 -07:00
parent 53bd14fecf
commit 7e14347c13
7 changed files with 631 additions and 41 deletions

View File

@@ -9,6 +9,7 @@ pub const Action = enum {
@"apc-parser",
@"codepoint-width",
@"grapheme-break",
@"hyperlink-map",
@"page-compression",
@"scrollback-compression",
@"screen-clone",
@@ -28,6 +29,7 @@ pub const Action = enum {
pub fn Struct(comptime action: Action) type {
return switch (action) {
.@"apc-parser" => @import("ApcParser.zig"),
.@"hyperlink-map" => @import("HyperlinkMap.zig"),
.@"screen-clone" => @import("ScreenClone.zig"),
.@"page-compression" => @import("PageCompression.zig"),
.@"scrollback-compression" => @import("ScrollbackCompression.zig"),