Skip to content

perf(screen): collapse non-clear ResetPosition cursor walk-up into one CSI#1285

Merged
ArthurSonzogni merged 3 commits into
ArthurSonzogni:mainfrom
zozowell:ralph/resetposition-walk-perf
Jun 4, 2026
Merged

perf(screen): collapse non-clear ResetPosition cursor walk-up into one CSI#1285
ArthurSonzogni merged 3 commits into
ArthurSonzogni:mainfrom
zozowell:ralph/resetposition-walk-perf

Conversation

@zozowell

@zozowell zozowell commented Jun 3, 2026

Copy link
Copy Markdown
Contributor

Problem

In the steady-state redraw path, Screen::ResetPosition(std::string&, bool) (the non-clear branch) walks the cursor back to the top-left by emitting one \x1B[1A (cursor-up) escape per row. On a tall terminal this writes a large, redundant burst of escape bytes every single frame. The cursor only needs to be repositioned once — the per-row walk encodes the same movement far more verbosely than necessary.

The change

Collapse the per-row walk-up into a single parameterized CSI cursor-up, guarded for the single-row case:

ss += '\r';  // MOVE_LEFT;
if (dimy_ > 1) {
  ss += "\x1B[" + std::to_string(dimy_ - 1) + "A";  // MOVE_UP;
}

The leading \r and the final cursor position are unchanged. On-screen output is identical.

The clear branch is intentionally left per-row: each row needs its own \x1B[2K (CLEAR_LINE) erase, so it cannot be collapsed. A code comment notes this.

Before / after (bytes per frame, non-clear path)

Rows Before After Reduction
10 37 5 7.4x
24 93 6 15.5x
50 197 6 32.8x
100 397 6 66.2x

Tests

Adds src/ftxui/screen/screen_test.cpp (registered in cmake/ftxui_test.cmake):

  • Non-clear output for dimy_ > 1 equals "\r\x1B[" + (dimy_-1) + "A"; equals "\r" for dimy_ == 1.
  • Terminal-effect equivalence to the old per-row walk (same total rows moved up) across several heights — the byte sequences differ by design, so this compares movement, not bytes.
  • The 50-row non-clear output is >= 10x smaller than the old per-row form (<= 8 bytes vs 197).
  • The clear branch remains per-row.

ctest passes in full: 339/339 tests pass, including the new ones.

zozowell and others added 3 commits June 3, 2026 17:01
…ed CSI

In Screen::ResetPosition(std::string&, bool), the non-clear (else) branch
previously emitted one \x1B[1A per row to walk the cursor to the top. Replace
that per-row loop with a single parameterized CSI cursor-up
(\x1B[<dimy_-1>A), guarded by if (dimy_ > 1). The leading \r (MOVE_LEFT) is
preserved and the final cursor position is identical.

For a 50-row screen this reduces the per-frame output from 197 bytes to 6
bytes (~32x). The clear branch is left unchanged because each row needs its
own \x1B[2K erase; a comment documents the rationale.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Add src/ftxui/screen/screen_test.cpp covering the collapsed non-clear
cursor walk-up in Screen::ResetPosition:
- dimy_ > 1 emits exactly "\r\x1B[<dimy-1>A" (single CSI cursor-up)
- dimy_ == 1 emits only "\r"
- output is byte-for-byte equivalent to the old per-row \x1B[1A walk
- 50-row screen output is >=10x smaller (6 bytes vs 197)
- the clear branch remains per-row (each row keeps its CLEAR_LINE)

Register the new test in cmake/ftxui_test.cmake.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…ce test

Add a Changelog 'Next' > Screen entry describing the per-frame
escape-byte reduction in the non-clear Screen::ResetPosition.

Also fix ResetPositionNonClearEquivalentToPerRowWalk: the collapsed
single-CSI form (\x1B[<n>A) is intentionally NOT byte-equal to the old
per-row walk (\x1B[1A x N), so equivalence is now checked by terminal
effect (total rows moved up) instead of string equality. All 339 ctest
tests pass.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@ArthurSonzogni

Copy link
Copy Markdown
Owner

Thanks! That looks useful!

let's wait for the CQ.

@ArthurSonzogni ArthurSonzogni merged commit 54be276 into ArthurSonzogni:main Jun 4, 2026
21 of 22 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants