Skip to main content

max / shop

Partial-region ring buffer: partial scrolls also O(1) Profile-driven followup. samply/perf showed 16% of vtebench CPU going into __memmove_avx512 + 7% into scroll_up_in_region, entirely from the partial-region memcpy path (fullscreen scrolls were already ring- optimized). Fix: extend the ring to scroll regions. New Grid.region_origin tracks rotation within the current DECSTBM scroll region, independent of main_origin. Invariant: * fullscreen (top=0, bot=rows-1) -> main/alt_origin rotates * partial region -> region_origin rotates, main/alt_origin held at 0 (unrolled via slice::rotate_left on transition) DECSTBM handler now unroll_region() (drain the old region rotation back to logical positions) then, if the new region is partial, unroll_active_ring() (drain fullscreen rotation so outside-region rows sit at logical=physical). Unroll preserves logical contents so the renderer's per-row cache stays valid — no cache invalidation needed. Also drives partial-region scrolls to O(1) origin bump + O(cols) blank, identical to the fullscreen fast path. Results (3-run medians, ms/sample lower is better): workload ring-buffer region-ring foot vs foot scrolling_bottom_region 392 120 133 0.90x (SHOP WINS) scrolling_top_region 310 118 134 0.88x (SHOP WINS) scrolling_bottom_small_region 187 120 134 0.90x (SHOP WINS) scrolling_top_small_region 217 120 135 0.89x (SHOP WINS) scrolling_fullscreen 7 5 5 1.00x (parity) scrolling 119 120 134 0.90x (SHOP WINS) unicode 4 4 8 0.50x (SHOP WINS 2x) Six workloads shop-wins. Full-screen and cell-content workloads unchanged or within noise (1ms drift on 4-5ms benchmarks). Removed the now-unused memcpy_scroll_*_partial helpers. All 31 shop-grid tests still pass. Full workspace: 67 tests, 0 failures.
Co-Authored-By
Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Author: Max Johnson <me@maxj.phd> · 2026-07-24 04:19 UTC
Signed with PGP, not checked
Commit: b5d7133c13b487e10415ec325f77187abc6355dc
Parent: ab8e84c
2 files changed, +106 insertions, -43 deletions
M Cargo.toml +8
@@ -49,5 +49,13 @@
49 49 codegen-units = 1
50 50 strip = "symbols"
51 51
52 + # Same optimizations as release but keep symbols + line-tables so samply /
53 + # perf can attribute samples to function names and source lines. Use with
54 + # `cargo build --profile profiling`.
55 + [profile.profiling]
56 + inherits = "release"
57 + strip = "none"
58 + debug = "line-tables-only"
59 +
52 60 [profile.dev]
53 61 opt-level = 1
@@ -109,6 +109,10 @@
109 109 on_alt: bool,
110 110 main_origin: u16,
111 111 alt_origin: u16,
112 + /// Rotation within the current scroll region. Independent from
113 + /// main/alt_origin. Non-zero only when the active screen is in a
114 + /// non-fullscreen region (DECSTBM); enforced by unroll on transition.
115 + region_origin: u16,
112 116 cursor: Cursor,
113 117 cursor_shape: CursorShape,
114 118 saved_main_cursor: Cursor,
@@ -139,6 +143,7 @@
139 143 on_alt: false,
140 144 main_origin: 0,
141 145 alt_origin: 0,
146 + region_origin: 0,
142 147 cursor: Cursor {
143 148 visible: true,
144 149 ..Cursor::default()
@@ -246,10 +251,60 @@
246 251
247 252 /// Physical byte offset for the start of logical row `r`.
248 253 fn row_start(&self, r: u16) -> usize {
249 - let phys = (self.active_origin() as u32 + r as u32) % self.rows as u32;
254 + let phys = if self.is_partial_region()
255 + && r >= self.scroll_top
256 + && r <= self.scroll_bottom
257 + {
258 + // In partial region: active_origin is guaranteed 0 by unroll on
259 + // transition, so we rotate only within the region.
260 + let region_size = (self.scroll_bottom - self.scroll_top + 1) as u32;
261 + let region_r = (r - self.scroll_top) as u32;
262 + let phys_in_region = (self.region_origin as u32 + region_r) % region_size;
263 + self.scroll_top as u32 + phys_in_region
264 + } else {
265 + (self.active_origin() as u32 + r as u32) % self.rows as u32
266 + };
250 267 phys as usize * self.cols as usize
251 268 }
252 269
270 + fn is_partial_region(&self) -> bool {
271 + self.scroll_top != 0 || self.scroll_bottom != self.rows - 1
272 + }
273 +
274 + /// Rotate the active screen's cells so `active_origin` becomes 0.
275 + /// Cheap: one `slice::rotate_left`. Called before entering partial-region
276 + /// mode so rows outside the region are at logical=physical positions.
277 + fn unroll_active_ring(&mut self) {
278 + let origin = self.active_origin();
279 + if origin == 0 {
280 + return;
281 + }
282 + let cols = self.cols as usize;
283 + let cells = self.active_cells_mut();
284 + cells.rotate_left(origin as usize * cols);
285 + if self.on_alt {
286 + self.alt_origin = 0;
287 + } else {
288 + self.main_origin = 0;
289 + }
290 + }
291 +
292 + /// Rotate the current partial region so `region_origin` becomes 0.
293 + /// Called before exiting partial-region mode (or entering a different
294 + /// region) so region contents are back at logical positions.
295 + fn unroll_region(&mut self) {
296 + if self.region_origin == 0 {
297 + return;
298 + }
299 + let cols = self.cols as usize;
300 + let top = self.scroll_top as usize;
301 + let region_len = (self.scroll_bottom - self.scroll_top + 1) as usize * cols;
302 + let shift = self.region_origin as usize * cols;
303 + let cells = self.active_cells_mut();
304 + cells[top * cols..top * cols + region_len].rotate_left(shift);
305 + self.region_origin = 0;
306 + }
307 +
253 308 /// Advance the active screen's ring origin. Positive `n` = scroll up
254 309 /// (logical row 0 shows what was logical row `n`); negative = scroll
255 310 /// down. Blanking of newly-exposed rows is the caller's job.
@@ -310,6 +365,7 @@
310 365 );
311 366 self.main_origin = 0;
312 367 self.alt_origin = 0;
368 + self.region_origin = 0;
313 369 self.cols = cols;
314 370 self.rows = rows;
315 371 self.scroll_top = 0;
@@ -505,13 +561,13 @@
505 561 }
506 562
507 563 fn scroll_up_in_region(&mut self, n: u16) {
508 - let n = n.min(self.scroll_bottom - self.scroll_top + 1);
564 + let region_size = self.scroll_bottom - self.scroll_top + 1;
565 + let n = n.min(region_size);
509 566 if n == 0 {
510 567 return;
511 568 }
512 - if self.scroll_top == 0 && self.scroll_bottom == self.rows - 1 {
513 - // Fullscreen fast path: O(1) origin shift + blank the newly-
514 - // exposed rows (which are physically the old top-of-ring).
569 + if !self.is_partial_region() {
570 + // Fullscreen fast path: O(1) origin shift + blank exposed rows.
515 571 let old_origin = self.active_origin();
516 572 self.advance_origin(n as i32);
517 573 for k in 0..n {
@@ -523,20 +579,30 @@
523 579 self.row_dirty[r as usize] = true;
524 580 }
525 581 } else {
526 - // Partial region: memcpy via logical indices (ring-mapped).
527 - self.memcpy_scroll_up_partial(n);
528 - for r in self.scroll_top..=self.scroll_bottom {
582 + // Partial region: ring-rotate within the region. active_origin is
583 + // guaranteed 0 in partial mode (unrolled on DECSTBM entry).
584 + let rs = region_size as u32;
585 + let old_region_origin = self.region_origin as u32;
586 + self.region_origin = ((old_region_origin + n as u32) % rs) as u16;
587 + for k in 0..n {
588 + let phys_in_region = (old_region_origin + k as u32) % rs;
589 + self.blank_physical_row(self.scroll_top + phys_in_region as u16);
590 + }
591 + // Partial-region scrolls don't propagate to the renderer's cache
592 + // rotation; mark exposed rows dirty for rebuild.
593 + for r in (self.scroll_bottom + 1 - n)..=self.scroll_bottom {
529 594 self.mark_row_dirty(r);
530 595 }
531 596 }
532 597 }
533 598
534 599 fn scroll_down_in_region(&mut self, n: u16) {
535 - let n = n.min(self.scroll_bottom - self.scroll_top + 1);
600 + let region_size = self.scroll_bottom - self.scroll_top + 1;
601 + let n = n.min(region_size);
536 602 if n == 0 {
537 603 return;
538 604 }
539 - if self.scroll_top == 0 && self.scroll_bottom == self.rows - 1 {
605 + if !self.is_partial_region() {
540 606 self.advance_origin(-(n as i32));
541 607 for k in 0..n {
542 608 self.blank_physical_row((self.active_origin() as u32 + k as u32) as u16 % self.rows);
@@ -546,44 +612,19 @@
546 612 self.row_dirty[r as usize] = true;
547 613 }
548 614 } else {
549 - self.memcpy_scroll_down_partial(n);
550 - for r in self.scroll_top..=self.scroll_bottom {
615 + let rs = region_size as u32;
616 + let new_region_origin = ((self.region_origin as u32 + rs - n as u32) % rs) as u16;
617 + self.region_origin = new_region_origin;
618 + for k in 0..n {
619 + let phys_in_region = (new_region_origin as u32 + k as u32) % rs;
620 + self.blank_physical_row(self.scroll_top + phys_in_region as u16);
621 + }
622 + for r in self.scroll_top..(self.scroll_top + n) {
551 623 self.mark_row_dirty(r);
552 624 }
553 625 }
554 626 }
555 627
556 - fn memcpy_scroll_up_partial(&mut self, n: u16) {
557 - let cols = self.cols as usize;
558 - let top = self.scroll_top;
559 - let bot = self.scroll_bottom;
560 - // Collect physical row ranges first so we can borrow cells mutably.
561 - for r in top..=(bot - n) {
562 - let src = self.row_start(r + n);
563 - let dst = self.row_start(r);
564 - let cells = self.active_cells_mut();
565 - cells.copy_within(src..src + cols, dst);
566 - }
567 - for r in (bot - n + 1)..=bot {
568 - self.blank_logical_row(r);
569 - }
570 - }
571 -
572 - fn memcpy_scroll_down_partial(&mut self, n: u16) {
573 - let cols = self.cols as usize;
574 - let top = self.scroll_top;
575 - let bot = self.scroll_bottom;
576 - for r in (top + n..=bot).rev() {
577 - let src = self.row_start(r - n);
578 - let dst = self.row_start(r);
579 - let cells = self.active_cells_mut();
580 - cells.copy_within(src..src + cols, dst);
581 - }
582 - for r in top..(top + n) {
583 - self.blank_logical_row(r);
584 - }
585 - }
586 -
587 628 fn erase_line(&mut self, mode: u16) {
588 629 let cols = self.cols as usize;
589 630 let row = self.cursor.row;
@@ -644,6 +685,9 @@
644 685 if self.on_alt == to_alt {
645 686 return;
646 687 }
688 + // region_origin belongs to the currently-active screen; unroll before
689 + // switching so the other screen starts with region_origin = 0.
690 + self.unroll_region();
647 691 if to_alt {
648 692 self.saved_main_cursor = self.cursor;
649 693 self.on_alt = true;
@@ -866,9 +910,20 @@
866 910 self.scroll_down_in_region(param1(params, 1));
867 911 }
868 912 ('r', false) => {
913 + // DECSTBM: transition-safe. If we're currently in a partial
914 + // region with a rotated region_origin, unroll it back to
915 + // logical order first. If the new region is partial, unroll
916 + // the fullscreen ring so outside-region rows sit at their
917 + // logical physical positions. Unroll preserves logical
918 + // contents so the renderer's per-row cache stays valid — no
919 + // need to mark rows dirty here.
920 + self.unroll_region();
869 921 let (top, bot) = param2(params, (1, self.rows));
870 922 self.scroll_top = top.saturating_sub(1).min(self.rows - 1);
871 923 self.scroll_bottom = bot.saturating_sub(1).min(self.rows - 1);
924 + if self.is_partial_region() {
925 + self.unroll_active_ring();
926 + }
872 927 self.cursor.row = 0;
873 928 self.cursor.col = 0;
874 929 }