140 lines
5.5 KiB
Markdown
140 lines
5.5 KiB
Markdown
# MUD Terminal Performance Optimization Summary
|
||
|
||
## Problem Identified
|
||
|
||
The MUD terminal component had severe performance issues when the message history grew large due to expensive text processing operations being performed in the Svelte template on every render cycle.
|
||
|
||
### Key Issues:
|
||
1. **Expensive re-computation on every render**: `processAnsi()`, `applyHighlights()`, and `splitIntoLines()` were called for every message on every component re-render
|
||
2. **Complex text processing in template**: ANSI color processing, regex highlighting, and line splitting happened in the template using `{@const}` blocks
|
||
3. **Inefficient reactive statements**: Triggered unnecessary DOM work on every history change
|
||
4. **No caching**: The same text processing was repeated multiple times for the same content
|
||
|
||
## Optimizations Implemented
|
||
|
||
### 1. **Pre-processing in Store Layer**
|
||
- Created `textProcessing.ts` utility with all text processing logic
|
||
- Moved expensive operations to happen once when messages are added to store
|
||
- Added `processedOutputHistory` store that contains pre-processed messages
|
||
|
||
### 2. **Intelligent Caching System**
|
||
- Implemented `ProcessedMessage` interface with built-in cache
|
||
- Cache keyed by UI settings that affect rendering (e.g., ANSI color enabled/disabled)
|
||
- Messages are re-processed only when relevant settings change
|
||
- Uses `Map<string, ProcessedContent>` for efficient cache lookups
|
||
|
||
### 3. **Flattened Renderable Lines Store**
|
||
- Created `activeRenderableLines` derived store that provides a flat array of all renderable lines
|
||
- Eliminates complex nested logic in template
|
||
- Each line has pre-computed properties (content, styling, metadata)
|
||
- Single `{#each}` loop instead of nested processing
|
||
|
||
### 4. **Optimized Template Rendering**
|
||
```svelte
|
||
<!-- Before: Complex nested processing -->
|
||
{#each safeOutputHistory as item (item.id)}
|
||
{#if item.isInput}
|
||
<!-- ... -->
|
||
{:else}
|
||
{@const processedContent = applyHighlights(processAnsi(item.text), item.highlights || [])}
|
||
{@const lines = splitIntoLines(processedContent)}
|
||
{#if lines.length <= 1}
|
||
<!-- ... -->
|
||
{:else}
|
||
{#each lines as line, lineIndex}
|
||
<!-- ... -->
|
||
{/each}
|
||
{/if}
|
||
{/if}
|
||
{/each}
|
||
|
||
<!-- After: Simple flat rendering -->
|
||
{#each safeRenderableLines as line (line.id)}
|
||
<div class="mud-terminal-line"
|
||
class:mud-input-line={line.isInput}
|
||
class:mud-terminal-subline={line.isSubline}>
|
||
{#if $uiSettings.showTimestamps && line.lineIndex === 0}
|
||
<span class="mud-timestamp">[{formatTimestamp(line.timestamp)}]</span>
|
||
{/if}
|
||
<div class="mud-terminal-content">
|
||
{@html line.content}
|
||
</div>
|
||
</div>
|
||
{/each}
|
||
```
|
||
|
||
### 5. **Optimized Reactive Statements**
|
||
- Replaced `setTimeout()` with `Promise.resolve().then()` for better microtask scheduling
|
||
- More targeted reactive updates that only trigger when necessary
|
||
- Removed redundant reactive blocks
|
||
|
||
### 6. **Store Architecture Improvements**
|
||
- Separated raw message storage from processed message storage
|
||
- Made `addToOutputHistory()` handle both raw and processed storage
|
||
- Ensured cache consistency when clearing history
|
||
|
||
## Performance Benefits
|
||
|
||
### Before Optimization:
|
||
- **O(n)** text processing operations on every render for **n** messages
|
||
- Multiple expensive regex operations per message per render
|
||
- ANSI-to-HTML conversion happening repeatedly
|
||
- Complex DOM operations during each reactive update
|
||
|
||
### After Optimization:
|
||
- **O(1)** amortized cost per message (processing happens once)
|
||
- **O(1)** cache lookups for repeated operations
|
||
- Text processing only when messages are added or settings change
|
||
- Simple, flat DOM structure with minimal reactive overhead
|
||
|
||
## Technical Implementation Details
|
||
|
||
### New Files Created:
|
||
- `src/lib/utils/textProcessing.ts` - Centralized text processing utilities
|
||
|
||
### Modified Files:
|
||
- `src/lib/stores/mudStore.ts` - Added processed stores and caching
|
||
- `src/lib/components/MudTerminal.svelte` - Simplified template and removed processing functions
|
||
|
||
### Key Data Structures:
|
||
```typescript
|
||
interface ProcessedMessage {
|
||
id: string;
|
||
originalText: string;
|
||
timestamp: number;
|
||
isInput: boolean;
|
||
highlights: HighlightRule[];
|
||
processedContent: string;
|
||
lines: ProcessedLine[];
|
||
processedCache: Map<string, ProcessedContent>; // Cache by UI settings
|
||
}
|
||
|
||
interface ProcessedLine {
|
||
id: string;
|
||
content: string; // Pre-processed HTML content
|
||
isSubline: boolean; // For indentation
|
||
parentId: string; // Reference to parent message
|
||
lineIndex: number; // Position in parent message
|
||
}
|
||
```
|
||
|
||
## Leveraging Svelte's Strengths
|
||
|
||
The optimization takes full advantage of Svelte's reactive system:
|
||
|
||
1. **Derived Stores**: Used for computed values that automatically update when dependencies change
|
||
2. **Keyed Each Blocks**: Ensures efficient DOM updates with `{#each items as item (item.id)}`
|
||
3. **Conditional Classes**: Uses `class:name={condition}` for efficient class toggling
|
||
4. **Reactive Declarations**: Optimized `$:` statements that only run when necessary
|
||
5. **Store Composition**: Layered stores that build upon each other efficiently
|
||
|
||
## Expected Performance Gains
|
||
|
||
For a terminal with 1000+ messages:
|
||
- **Before**: ~1000 × (ANSI processing + regex highlighting + line splitting) per render
|
||
- **After**: ~0 processing per render (cached results)
|
||
- **Memory**: Slightly higher due to caching, but with configurable limits
|
||
- **Responsiveness**: Should feel instant even with large message histories
|
||
|
||
The optimization maintains all existing functionality while dramatically improving performance, especially as message history grows.
|