Try to keep connections alive for longer
This commit is contained in:
139
PERFORMANCE_OPTIMIZATION.md
Normal file
139
PERFORMANCE_OPTIMIZATION.md
Normal file
@@ -0,0 +1,139 @@
|
||||
# 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.
|
||||
Reference in New Issue
Block a user