grid: close the plain-text parse throughput gap #410
No reviewers
Labels
No labels
priority/P0
priority/P1
priority/P2
release/v0.1.0
status/blocked
status/planned
type/bug
type/design
type/test-gap
type/tracker
No milestone
No project
No assignees
1 participant
Due date
No due date set.
Dependencies
No dependencies set
Reference
natsukium/felis!410
Loading…
Reference in a new issue
No description provided.
Delete branch "grid/plain-text-throughput"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
The #401 diagnosis traced felis's plain-text gap to ghostty-tip to the grid's print paths, not the parser. This branch removes the costs the profile named, and a new equivalence proptest turned up three existing bugs in those paths, fixed here too.
What changed
print_utf8_runvalidates withsimdutf8::basic(runtime AVX2/SSE4.2/NEON) and uses std only on an invalid run, to getvalid_up_to. The std validator was 23.5% of the unicode floor's cycles. simdutf8 is MIT/Apache-2.0 and was already in the lock graph.bulk_width_or_defer). The table servesprint_wide_str's batch loop, its wrap check, andgrapheme_width, and the loop stores through a row slice. The opaqueunicode-widthcall inside the store loop was the alias poisontraps.mddescribes: it forced the loop to reload the cells pointer on every store.print_strand its gap fork now share one free function over the row slice. It probes for wide-partner eviction only below the row's occupancy watermark and plain-stores everything past it. kitten's bare-\nstaircase lands nearly every line past the watermark. The gap blank copies from a static blank array, becausefill(Cell::default())compiled to a loop that stores each cell separately instead of to a memset.dec_mode_stateshashes with foldhashFixedStateand updates in place with a singleget_mut.decode_onlynow decodes through the batched entry points, as the grid does. It had been measuring the old per-byte decoder, which made it slower than the full grid case. The newascii_throughputbench has two cases,bare_lf_staircaseandcrlf_lines.Bugs found by the new proptest (all present on main)
batched_print_matches_per_byte_printfeeds the same streams to two grids and compares the results: one grid gets the batchedprint_str/print_utf8_runpaths at random read-chunk sizes, the other gets per-byteSink::print. The streams mix ASCII, wide, fold-candidate and invalid UTF-8 runs with LF/CR/HT/BS, CUP, EL, SGR, REP, the alternate screen, DECAWM and IRM.Spacerpast the watermark and blanked the live cell to its left. The fix is the watermark split above.print_strleftzwj_pendingarmed across an ASCII run, so a later scalar could join a cluster the run had already ended.Each bug also has a deterministic test. CHANGELOG has a Fixed entry.
Measured
The parse-into-grid floor is
felis-daemon'smeasure_parse_into_grid_floor: kitten-shaped payloads at 35x106, release build, two passes. Instruction counts (instructions:u) are exact across runs. MiB/s is the median of 3 runs with main and this branch interleaved. Load average was 8.1–8.2, because this host is also the CI runner.Criterion, same host, load ~7–8, main → branch:
ascii_throughput/bare_lf_staircase: 326 → 487 MiB/sascii_throughput/crlf_lines: 361 → 586 MiB/sunicode_throughput/cjk_emoji_combining: 191 → 270 MiB/sEnd-to-end
kittenbenchneeds a window and a quiet host, so it was not run.Not in this PR
range_blank_innerresolves ISO protection for every cell it blanks (a further ~6% of csi instructions). Filed as #409.Checks
just checkpasses: 3552 tests, including the fullfelis-gridsuite and the new proptest at 120k cases locally.fuzz/checks clean, and its lock now carries simdutf8.decode_onlydoc comment overstated what the delta measures. That comment is fixed.Closes #401
c76161011ca4e583a757