[PR #456] [CLOSED] perf(core): cache measureFunc results in TextBufferRenderable #1331

Closed
opened 2026-03-14 09:30:54 +03:00 by kerem · 0 comments
Owner

📋 Pull Request Information

Original PR: https://github.com/anomalyco/opentui/pull/456
Author: @remorses
Created: 12/29/2025
Status: Closed

Base: mainHead: measure-cache-optimization


📝 Commits (6)

  • fa398bb perf(core): cache measureFunc results in TextBufferRenderable
  • ce214c9 fix: consistent height handling in measure cache hit/miss paths
  • 36179a4 refactor: move measure cache to TextBufferView
  • a69497e fix: use unlimited height for measure cache to avoid truncation
  • 35bc607 Merge branch 'main' into measure-cache-optimization
  • 9d0e95e chore: restore Math.floor in measureForDimensions

📊 Changes

5 files changed (+213 additions, -2 deletions)

View changed files

📝 .gitignore (+1 -0)
packages/core/src/benchmark/measure-cache-benchmark.ts (+169 -0)
📝 packages/core/src/renderables/TextBufferRenderable.ts (+0 -1)
📝 packages/core/src/text-buffer-view.ts (+31 -1)
📝 packages/core/src/text-buffer.ts (+12 -0)

📄 Description

Summary

Adds a measure cache to TextBufferRenderable to avoid redundant measureForDimensions calls during Yoga layout passes.

Why Yoga calls measureFunc multiple times:

During flexbox layout, Yoga probes nodes with different width constraints to determine optimal sizing:

  1. First with Undefined mode to get intrinsic size
  2. Then with AtMost to constrain to available space
  3. Sometimes again with Exactly after resolving flex grow/shrink

This means measureForDimensions (which does text wrapping calculations) was being called 2-3x per layout pass per text node.

Solution:

  • Add _version counter to TextBuffer that increments on content changes
  • Cache measure results in TextBufferRenderable keyed by width
  • Invalidate cache when textBuffer.version changes or wrapMode changes

Benchmark results (144 text renderables in 8x6 grid):

Cached renders (no content change): 1.04ms avg
Uncached renders (content changes): 5.44ms avg

Cache speedup: 5.3x faster when content unchanged

References https://github.com/sst/opentui/pull/433#issuecomment-3697326260


🔄 This issue represents a GitHub Pull Request. It cannot be merged through Gitea due to API limitations.

## 📋 Pull Request Information **Original PR:** https://github.com/anomalyco/opentui/pull/456 **Author:** [@remorses](https://github.com/remorses) **Created:** 12/29/2025 **Status:** ❌ Closed **Base:** `main` ← **Head:** `measure-cache-optimization` --- ### 📝 Commits (6) - [`fa398bb`](https://github.com/anomalyco/opentui/commit/fa398bbfdafd90737a7729e39bab7c2655f94a93) perf(core): cache measureFunc results in TextBufferRenderable - [`ce214c9`](https://github.com/anomalyco/opentui/commit/ce214c9b37f9aa61af0ccd14440f67e07898fe68) fix: consistent height handling in measure cache hit/miss paths - [`36179a4`](https://github.com/anomalyco/opentui/commit/36179a49bbb0c45356516eaa9f0fede15e5f2462) refactor: move measure cache to TextBufferView - [`a69497e`](https://github.com/anomalyco/opentui/commit/a69497ea66715a008ad4624b682d853e65ece7a0) fix: use unlimited height for measure cache to avoid truncation - [`35bc607`](https://github.com/anomalyco/opentui/commit/35bc607ae786b63b6bba92406715b0b860410487) Merge branch 'main' into measure-cache-optimization - [`9d0e95e`](https://github.com/anomalyco/opentui/commit/9d0e95ecde2c627351eb9293942fd989c8029560) chore: restore Math.floor in measureForDimensions ### 📊 Changes **5 files changed** (+213 additions, -2 deletions) <details> <summary>View changed files</summary> 📝 `.gitignore` (+1 -0) ➕ `packages/core/src/benchmark/measure-cache-benchmark.ts` (+169 -0) 📝 `packages/core/src/renderables/TextBufferRenderable.ts` (+0 -1) 📝 `packages/core/src/text-buffer-view.ts` (+31 -1) 📝 `packages/core/src/text-buffer.ts` (+12 -0) </details> ### 📄 Description ## Summary Adds a measure cache to `TextBufferRenderable` to avoid redundant `measureForDimensions` calls during Yoga layout passes. **Why Yoga calls measureFunc multiple times:** During flexbox layout, Yoga probes nodes with different width constraints to determine optimal sizing: 1. First with `Undefined` mode to get intrinsic size 2. Then with `AtMost` to constrain to available space 3. Sometimes again with `Exactly` after resolving flex grow/shrink This means `measureForDimensions` (which does text wrapping calculations) was being called 2-3x per layout pass per text node. **Solution:** - Add `_version` counter to `TextBuffer` that increments on content changes - Cache measure results in `TextBufferRenderable` keyed by width - Invalidate cache when `textBuffer.version` changes or `wrapMode` changes **Benchmark results (144 text renderables in 8x6 grid):** ``` Cached renders (no content change): 1.04ms avg Uncached renders (content changes): 5.44ms avg Cache speedup: 5.3x faster when content unchanged ``` References https://github.com/sst/opentui/pull/433#issuecomment-3697326260 --- <sub>🔄 This issue represents a GitHub Pull Request. It cannot be merged through Gitea due to API limitations.</sub>
kerem 2026-03-14 09:30:54 +03:00
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
starred/opentui#1331
No description provided.