Skip to content

Conversation

@remorses
Copy link
Contributor

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 #433 (comment)

Add measure cache to avoid redundant measureForDimensions calls during
Yoga layout passes. Yoga probes nodes with different width constraints
(Undefined, AtMost, Exactly) which caused 2-3x redundant text wrapping
calculations per layout pass.

- Add _version counter to TextBuffer incremented on content changes
- Cache measure results keyed by width in TextBufferRenderable
- Invalidate cache on version change or wrapMode change
- Add benchmark showing 5.3x speedup when content unchanged
Copilot AI review requested due to automatic review settings December 29, 2025 21:48
Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR implements a measure cache in TextBufferRenderable to avoid redundant text measurement calculations during Yoga layout passes. During flexbox layout, Yoga often calls the measure function 2-3 times with different width constraints to determine optimal sizing, causing wasteful recomputation of text wrapping. The optimization adds version tracking to detect content changes and caches measurement results by width, providing a 5.3x speedup for unchanged content.

Key changes:

  • Added version counter to TextBuffer that increments on all content mutations
  • Implemented measure cache in TextBufferRenderable keyed by floored width with version-based invalidation
  • Added benchmark to validate the performance improvement

Reviewed changes

Copilot reviewed 3 out of 4 changed files in this pull request and generated 1 comment.

File Description
packages/core/src/text-buffer.ts Adds _version counter incremented by all content-mutating methods (setText, append, loadFile, setStyledText, clear, reset) and exposes it via a getter
packages/core/src/renderables/TextBufferRenderable.ts Implements measure cache with Map keyed by floored width, invalidated on version changes and wrap mode changes
packages/core/src/benchmark/measure-cache-benchmark.ts Adds benchmark comparing cached vs uncached render performance with 144 text renderables in a grid layout
.gitignore Adds *.cpuprofile to ignore CPU profiling output files

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

@kommander
Copy link
Collaborator

The EditorView uses the measureForDimensions as well. I'd put that in the native TextBufferView to have the cache work for both.

Also, the measure function caused a lot of issues in the past, I am very vary. I think there are some invalidations missing, like when wrapMode or viewport changes for example.

- Move cache from TextBufferRenderable to TextBufferView so it works for both TextBufferRenderable and EditorView
- Add cache invalidation on setWrapMode, setWrapWidth, setViewport, setViewportSize
- Add version increment on setTabWidth since it affects measurement
@remorses
Copy link
Contributor Author

remorses commented Dec 30, 2025

Ok I moved the cache inside TextBufferView. Benchmark still shows 5x faster

Added also more invalidations

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