Phase 3: Performance Instrumentation
⚠️ SUPERSEDED BY PHASE 4.0 CHECKPOINT
This document describes the Phase 3 instrumentation foundation completed in PR #116 and #117.
Important updates from Phase 4.0:
- Disabled-path runtime overhead has not yet been validated against the pre-Phase-3 implementation
- Previous overhead and heap conclusions from single-shot profiler must not be used
- See
docs/PHASE4.0_CHECKPOINT.mdfor current status and required follow-ups
Overview
Phase 3 adds observation-only instrumentation to measure cache behavior, allocation patterns, and grapheme processing costs. This phase does NOT include any optimizations - it only collects metrics to identify actual bottlenecks.
Note: This is an internal development/profiling API and is not exported in the npm package. It is intended for performance analysis during development and benchmarking.
What Phase 3 Does
✅ Instrumentation Added
- Cell Cache Metrics: createCell calls, cache hit/miss rates, allocation counts, cache clears
- Text Cache Metrics: textCellWidth calls, text width cache hit/miss, wrap cache behavior
- Grapheme Metrics: segmentation calls, Intl.Segmenter vs fallback usage
⚠️ Disabled-Path Overhead Not Yet Validated
- Instrumentation is designed to have a small disabled-path cost
- Hook functions are still invoked from production hot paths when collection is disabled
- The incremental runtime and bundle-size overhead compared to pre-Phase-3 has not yet been validated
- Validation is tracked by issue #119 (required before claiming Phase 3 complete)
✅ Profiler Benchmark
- 6 targeted workloads covering realistic scenarios
- Detailed metrics output for each workload
- Note: Provides counter snapshots only; timing/overhead measurements are not decision-grade
What Phase 3 Does NOT Do
❌ No Optimizations
- Does NOT change
MAX_CACHED_CELLS_PER_STYLE - Does NOT implement partial eviction
- Does NOT add long text cache cap
- Does NOT implement provider-aware caching
- Does NOT add virtual scroll optimization
❌ No Behavior Changes
- All existing functionality unchanged
- Performance impact should be verified by baseline benchmarks (when instrumentation disabled)
- Only adds observation capability
Usage
Enable Instrumentation
import {
enableInstrumentation,
resetMetrics,
getMetrics
} from "../src/core/perf/instrumentation.js";
// Enable instrumentation
enableInstrumentation();
// Reset counters
resetMetrics();
// Run your workload
terminal.write("test", { x: 0, y: 0 });
// Get metrics
const metrics = getMetrics();
console.log('Cell cache hit rate:',
(metrics.cell.cellCacheHitWidth1 /
(metrics.cell.cellCacheHitWidth1 + metrics.cell.cellCacheMissWidth1)) \* 100
);Run Profiler Benchmark
pnpm run bench:profiler # Counter-only snapshotWorkloads
1. Repeated CJK Text
- Scenario: 1000 writes of same CJK line
- Expected: High Cell cache hit rate (>90%)
- Purpose: Validate cache effectiveness for repeated content
2. Unique CJK Logs
- Scenario: 1000 unique CJK log lines
- Expected: Low cache hit rate, many misses and evictions
- Purpose: Measure cache-miss path and eviction behavior
3. Complex Grapheme Text
- Scenario: 2000 textCellWidth calls with ZWJ emoji, combining marks
- Expected: High segmentedGraphemes call count
- Purpose: Measure grapheme segmentation cost
4. Long Text Wrapping
- Scenario: 500 wrapByCells calls on long CJK text at varying widths
- Expected: Wrap cache behavior, potential clears
- Purpose: Understand wrap cache effectiveness
5. Mixed Workload
- Scenario: 1000 log lines with repeated prefixes + unique content
- Expected: Mixed cache behavior (some hits, some misses)
- Purpose: Realistic log viewer scenario
6. Supplementary CJK
- Scenario: 500 writes of supplementary plane CJK
- Expected: width=2 cache behavior
- Purpose: Validate supplementary plane handling
Metrics Collected
Cell Cache Metrics
createCellCalls: Total createCell() invocationscharCellWidthCallsFromCreateCell: Width calculations in createCellnewCellCount: New Cell object allocationscellCacheHitWidth1: Cache hits for width=1 charscellCacheHitWidth2: Cache hits for width=2 charscellCacheMissWidth1: Cache misses for width=1 charscellCacheMissWidth2: Cache misses for width=2 charscellCacheClearWidth1: Cache clears for width=1cellCacheClearWidth2: Cache clears for width=2maxCacheSizeWidth1: Peak cache size for width=1maxCacheSizeWidth2: Peak cache size for width=2
Text Cache Metrics
textCellWidthCalls: Total textCellWidth() callstextWidthCacheHit: Text width cache hitstextWidthCacheMiss: Text width cache missestextWidthCacheSet: Cache insertionstextWidthCacheEvict: LRU evictionsrenderPassTextWidthCacheHit: Render pass cache hitsrenderPassTextWidthCacheMiss: Render pass cache misseswrapByCellsCalls: Total wrapByCells() callswrapCacheHit: Wrap cache hitswrapCacheMiss: Wrap cache misseswrapCacheSet: Wrap cache insertionswrapCacheClear: Wrap cache clearsmaxTextLength: Longest text processedtotalTextLength: Cumulative text lengthasciiCount: ASCII text countnonAsciiCount: Non-ASCII text count
Grapheme Metrics
graphemeSegmentationRequiredCalls: segmentedGraphemes() callsintlSegmenterUsed: Intl.Segmenter path usagefallbackSegmenterUsed: Fallback segmenter usagecomplexGraphemeCount: Complex grapheme detections
Next Steps (Phase 4+)
Only after analyzing instrumentation data:
If Cell cache shows high miss rate or frequent clears:
- Consider adjusting
MAX_CACHED_CELLS_PER_STYLE - Consider partial eviction strategy
- Requires profiler evidence showing bottleneck
- Consider adjusting
If text cache shows retention issues:
- Consider long text admission policy
- Consider cache size tuning
- Requires profiler evidence showing bottleneck
If grapheme segmentation shows high cost:
- Consider grapheme computation optimization
- Consider segment caching
- Requires profiler evidence showing bottleneck
If virtual scroll needed:
- Must first add rendering workload measurements
- Must show frame duration impact
- Requires real browser profiling
Important Notes
- Data-Driven Only: No optimization without profiler evidence
- Observation Phase: This PR only measures, does not optimize
- Disabled-Path Cost: Validation pending in issue #119 (required)
- Targeted Workloads: Each workload tests specific behavior
Files Modified
src/core/perf/instrumentation.ts: New instrumentation modulesrc/core/buffer/buffer.ts: Cell cache instrumentationsrc/vue/utils/text.ts: Text cache instrumentationsrc/utils/grapheme.ts: Grapheme instrumentationscripts/bench-profiler-complete.ts (counter-only snapshot): New profiler benchmarkpackage.json: Addedbench:profilerscript
Verification
# Type check
pnpm run typecheck
# Run profiler
pnpm run bench:profiler # Counter-only snapshot
# Smoke-check benchmark correctness only
# This does not validate pre-Phase-3 vs post-Phase-3 overhead (see #119)
pnpm run bench:perf-baseline:smoke