[PR #1163] [CLOSED] TUI Performance Optimization #9838

Closed
opened 2026-02-16 18:14:15 -05:00 by yindo · 0 comments
Owner

📋 Pull Request Information

Original PR: https://github.com/anomalyco/opencode/pull/1163
Author: @entrepeneur4lyf
Created: 7/20/2025
Status: Closed

Base: devHead: tui/performance


📝 Commits (1)

  • f864ae8 # TUI Performance Optimization Changes

📊 Changes

28 files changed (+8935 additions, -348 deletions)

View changed files

packages/tui/internal/btree/btree.go (+574 -0)
packages/tui/internal/btree/btree_test.go (+431 -0)
packages/tui/internal/btree/iterator.go (+271 -0)
packages/tui/internal/cache/memory_bounded.go (+97 -0)
packages/tui/internal/components/chat/batch_benchmark_test.go (+372 -0)
packages/tui/internal/components/chat/batch_processor.go (+249 -0)
📝 packages/tui/internal/components/chat/editor.go (+59 -26)
packages/tui/internal/components/chat/editor_adaptive_test.go (+97 -0)
packages/tui/internal/components/chat/message_broker.go (+145 -0)
📝 packages/tui/internal/components/chat/messages.go (+78 -315)
packages/tui/internal/components/chat/messages_benchmark_test.go (+445 -0)
packages/tui/internal/components/chat/sliding_window.go (+413 -0)
packages/tui/internal/components/chat/sliding_window_test.go (+315 -0)
📝 packages/tui/internal/components/diff/diff.go (+144 -7)
packages/tui/internal/components/diff/diff_benchmark_test.go (+232 -0)
packages/tui/internal/components/diff/syntax_cache.go (+327 -0)
packages/tui/internal/components/textarea/textarea_adaptive.go (+524 -0)
packages/tui/internal/components/textarea/textarea_adaptive_benchmark_test.go (+357 -0)
packages/tui/internal/components/textarea/textarea_benchmark_test.go (+307 -0)
packages/tui/internal/components/textarea/textarea_rope.go (+886 -0)

...and 8 more files

📄 Description

Diff Component

  • Baseline: 27.6ms per diff render
  • Optimized: 3.3µs - 2.4ms (depending on cache hit rate)
  • CPU Usage Reduction: 97.82% → <1%

Messages Component

  • Baseline: 5.54ms for 500 messages (sequential)
  • Optimized: 2.34ms for 500 messages (parallel)
  • Improvement: 2.4x (cold cache), 2.6x (warm cache)
  • Concurrency Scaling: Near-linear with CPU cores

Textarea Component

  • Strategy: Adaptive implementation selection
  • Threshold: 1MB file size
  • Memory Efficiency: O(n) → O(log n) for large files

Technical Implementation

Diff Component Changes

The profiling revealed that syntax highlighting consumed 97.82% of CPU time during diff rendering. I addressed this through:

  1. Batch Processing: Consolidated per-line highlighting into batch operations
  2. LRU Cache: Implemented content-addressable caching with FNV hashing
  3. Dynamic ANSI Detection: Runtime pattern detection for cross-theme compatibility

Key modules:

  • syntax_cache.go: High-performance caching layer with configurable size limits
  • diff.go: Batch highlighting coordinator with fallback mechanisms

Messages Component Changes

I implemented a multi-threaded rendering pipeline optimized for modern multi-core processors:

  1. Concurrent Batch Processor: Work-stealing queue with dynamic batch sizing
  2. Part Cache: Content-based caching using FNV-1a hashing
  3. Piece Table VS Code-inspired data structure for O(log n) operations

Key modules:

  • batch_processor.go: Concurrent rendering orchestrator
  • part_cache.go: Thread-safe caching implementation
  • piece_table.go: Tree-based text management structure

Textarea Component Changes

Implemented an adaptive strategy that selects the optimal data structure based on content characteristics:

  • Original Implementation: Direct string manipulation for files <1MB
  • Rope Implementation: B-tree based structure for files >1MB
  • Automatic Switching: Transparent to the API consumer

Message Memory Management

Implemented a MessageBroker pattern to prevent terminal crashes with large conversations:

MessageBroker

  • Manages message loading and caching between API and UI components - replacing app.Messages
  • Implements windowed access with configurable window size (default: 1000 messages)
  • Integrates with existing memory-bounded cache system (500MB limit)
  • Provides methods: GetMessages(), GetMessage(), GetMessageCount(), InvalidateCache()

Memory Management

  • Prevents loading all conversation messages into memory simultaneously
  • Maintains active window of messages based on viewport requirements
  • Leverages existing LRU cache with memory bounds for rendered content
  • Automatic cache eviction when memory limits are exceeded

Integration Updates

  • Modified sliding window renderer to work with MessageBroker instead of direct message arrays
  • Updated messages component to use broker for all message access
  • Maintained backward compatibility with existing rendering pipeline

Performance

  • Memory usage bounded regardless of conversation size
  • Constant memory overhead for message metadata indexing
  • Efficient batch rendering for visible message ranges

Sliding Window Viewport Optimization

Implemented a sliding window renderer that reduces memory usage and improves rendering performance for large conversations:

Architecture

  • Message Index: Lightweight metadata structure storing position and height information for O(1) lookups
  • Adaptive Window Size: Dynamically calculated based on viewport height (2.5x visible messages, bounded 20-50)
  • Binary Search: Efficient visible message range detection using cumulative line positions
  • Lazy Rendering: Only renders messages within the active window plus buffer

Key Components

  • MessageMeta struct: Stores StartLine, Height, and ContentHash for each message
  • SlidingWindowRenderer: Manages window state and coordinates rendering
  • findVisibleMessageRange(): Binary search for viewport intersection
  • calculateWindowRange(): Centers window on visible area with padding

Performance

  • Memory Usage: Constant regardless of conversation size
  • Rendering Time: Proportional to visible messages only (typically 10-20 messages)
  • Scrolling: Smooth performance through predictive window adjustment
  • Cache Integration: Works with global memory-bounded cache for rendered content

Memory Efficiency

  • Index Size: ~24 bytes per message for metadata only
  • Window Size: Limited to 50 messages maximum in memory
  • Cache Eviction: Automatic cleanup of off-screen content
  • Height Correction: Real heights update estimated values for accuracy

Cache Architecture

All caching implementations use FNV-1a hashing for content addressing, providing:

  • O(1) average lookup time
  • Minimal hash collision probability
  • Consistent performance across different content types

Memory Bounds

  • Diff Cache: 100MB limit with LRU eviction
  • Message Cache: 500MB limit with memory-bounded eviction
  • Part Cache: Unbounded (relies on system memory management)

Concurrency Model

  • Messages: Parallel batch processing with work-stealing queues
  • Diff: Single-threaded with async cache population
  • Textarea: Single-threaded with efficient data structures

Testing and Validation

All optimizations include comprehensive test coverage:

  • Benchmark Tests: Performance regression detection
  • Unit Tests: Correctness verification
  • Integration Tests: End-to-end functionality validation
  • Memory Tests: Resource usage verification

🔄 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/opencode/pull/1163 **Author:** [@entrepeneur4lyf](https://github.com/entrepeneur4lyf) **Created:** 7/20/2025 **Status:** ❌ Closed **Base:** `dev` ← **Head:** `tui/performance` --- ### 📝 Commits (1) - [`f864ae8`](https://github.com/anomalyco/opencode/commit/f864ae83435a1e84047b73c93c26147b95719e5e) # TUI Performance Optimization Changes ### 📊 Changes **28 files changed** (+8935 additions, -348 deletions) <details> <summary>View changed files</summary> ➕ `packages/tui/internal/btree/btree.go` (+574 -0) ➕ `packages/tui/internal/btree/btree_test.go` (+431 -0) ➕ `packages/tui/internal/btree/iterator.go` (+271 -0) ➕ `packages/tui/internal/cache/memory_bounded.go` (+97 -0) ➕ `packages/tui/internal/components/chat/batch_benchmark_test.go` (+372 -0) ➕ `packages/tui/internal/components/chat/batch_processor.go` (+249 -0) 📝 `packages/tui/internal/components/chat/editor.go` (+59 -26) ➕ `packages/tui/internal/components/chat/editor_adaptive_test.go` (+97 -0) ➕ `packages/tui/internal/components/chat/message_broker.go` (+145 -0) 📝 `packages/tui/internal/components/chat/messages.go` (+78 -315) ➕ `packages/tui/internal/components/chat/messages_benchmark_test.go` (+445 -0) ➕ `packages/tui/internal/components/chat/sliding_window.go` (+413 -0) ➕ `packages/tui/internal/components/chat/sliding_window_test.go` (+315 -0) 📝 `packages/tui/internal/components/diff/diff.go` (+144 -7) ➕ `packages/tui/internal/components/diff/diff_benchmark_test.go` (+232 -0) ➕ `packages/tui/internal/components/diff/syntax_cache.go` (+327 -0) ➕ `packages/tui/internal/components/textarea/textarea_adaptive.go` (+524 -0) ➕ `packages/tui/internal/components/textarea/textarea_adaptive_benchmark_test.go` (+357 -0) ➕ `packages/tui/internal/components/textarea/textarea_benchmark_test.go` (+307 -0) ➕ `packages/tui/internal/components/textarea/textarea_rope.go` (+886 -0) _...and 8 more files_ </details> ### 📄 Description ### Diff Component - **Baseline**: 27.6ms per diff render - **Optimized**: 3.3µs - 2.4ms (depending on cache hit rate) - **CPU Usage Reduction**: 97.82% → <1% ### Messages Component - **Baseline**: 5.54ms for 500 messages (sequential) - **Optimized**: 2.34ms for 500 messages (parallel) - **Improvement**: 2.4x (cold cache), 2.6x (warm cache) - **Concurrency Scaling**: Near-linear with CPU cores ### Textarea Component - **Strategy**: Adaptive implementation selection - **Threshold**: 1MB file size - **Memory Efficiency**: O(n) → O(log n) for large files ## Technical Implementation ### Diff Component Changes The profiling revealed that syntax highlighting consumed 97.82% of CPU time during diff rendering. I addressed this through: 1. **Batch Processing**: Consolidated per-line highlighting into batch operations 2. **LRU Cache**: Implemented content-addressable caching with FNV hashing 3. **Dynamic ANSI Detection**: Runtime pattern detection for cross-theme compatibility Key modules: - `syntax_cache.go`: High-performance caching layer with configurable size limits - `diff.go`: Batch highlighting coordinator with fallback mechanisms ### Messages Component Changes I implemented a multi-threaded rendering pipeline optimized for modern multi-core processors: 1. **Concurrent Batch Processor**: Work-stealing queue with dynamic batch sizing 2. **Part Cache**: Content-based caching using FNV-1a hashing 3. **Piece Table** VS Code-inspired data structure for O(log n) operations Key modules: - `batch_processor.go`: Concurrent rendering orchestrator - `part_cache.go`: Thread-safe caching implementation - `piece_table.go`: Tree-based text management structure ### Textarea Component Changes Implemented an adaptive strategy that selects the optimal data structure based on content characteristics: - **Original Implementation**: Direct string manipulation for files <1MB - **Rope Implementation**: B-tree based structure for files >1MB - **Automatic Switching**: Transparent to the API consumer ### Message Memory Management Implemented a MessageBroker pattern to prevent terminal crashes with large conversations: #### MessageBroker - Manages message loading and caching between API and UI components - replacing app.Messages - Implements windowed access with configurable window size (default: 1000 messages) - Integrates with existing memory-bounded cache system (500MB limit) - Provides methods: `GetMessages()`, `GetMessage()`, `GetMessageCount()`, `InvalidateCache()` #### Memory Management - Prevents loading all conversation messages into memory simultaneously - Maintains active window of messages based on viewport requirements - Leverages existing LRU cache with memory bounds for rendered content - Automatic cache eviction when memory limits are exceeded #### Integration Updates - Modified sliding window renderer to work with MessageBroker instead of direct message arrays - Updated messages component to use broker for all message access - Maintained backward compatibility with existing rendering pipeline #### Performance - Memory usage bounded regardless of conversation size - Constant memory overhead for message metadata indexing - Efficient batch rendering for visible message ranges ### Sliding Window Viewport Optimization Implemented a sliding window renderer that reduces memory usage and improves rendering performance for large conversations: #### Architecture - **Message Index**: Lightweight metadata structure storing position and height information for O(1) lookups - **Adaptive Window Size**: Dynamically calculated based on viewport height (2.5x visible messages, bounded 20-50) - **Binary Search**: Efficient visible message range detection using cumulative line positions - **Lazy Rendering**: Only renders messages within the active window plus buffer #### Key Components - `MessageMeta` struct: Stores `StartLine`, `Height`, and `ContentHash` for each message - `SlidingWindowRenderer`: Manages window state and coordinates rendering - `findVisibleMessageRange()`: Binary search for viewport intersection - `calculateWindowRange()`: Centers window on visible area with padding #### Performance - **Memory Usage**: Constant regardless of conversation size - **Rendering Time**: Proportional to visible messages only (typically 10-20 messages) - **Scrolling**: Smooth performance through predictive window adjustment - **Cache Integration**: Works with global memory-bounded cache for rendered content #### Memory Efficiency - **Index Size**: ~24 bytes per message for metadata only - **Window Size**: Limited to 50 messages maximum in memory - **Cache Eviction**: Automatic cleanup of off-screen content - **Height Correction**: Real heights update estimated values for accuracy ### Cache Architecture All caching implementations use FNV-1a hashing for content addressing, providing: - O(1) average lookup time - Minimal hash collision probability - Consistent performance across different content types ### Memory Bounds - **Diff Cache**: 100MB limit with LRU eviction - **Message Cache**: 500MB limit with memory-bounded eviction - **Part Cache**: Unbounded (relies on system memory management) ### Concurrency Model - **Messages**: Parallel batch processing with work-stealing queues - **Diff**: Single-threaded with async cache population - **Textarea**: Single-threaded with efficient data structures ## Testing and Validation All optimizations include comprehensive test coverage: - **Benchmark Tests**: Performance regression detection - **Unit Tests**: Correctness verification - **Integration Tests**: End-to-end functionality validation - **Memory Tests**: Resource usage verification --- <sub>🔄 This issue represents a GitHub Pull Request. It cannot be merged through Gitea due to API limitations.</sub>
yindo added the pull-request label 2026-02-16 18:14:15 -05:00
yindo closed this issue 2026-02-16 18:14:15 -05:00
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: anomalyco/opencode#9838