Tessera Performance Plan
This plan defines V1 performance goals, measurement methodology, and release gates.
Goals and SLO Targets
Primary metrics:
- startup time to first rendered frame
- frame time under workload
- allocations per rendered frame
- p95 input latency
SLO targets (Release build, local terminal, same machine profile per run):
- Startup (DataWorkbench):
<= 250 msto first frame - Startup (OpsWatch):
<= 250 msto first frame - Frame time p95 (normal UI load):
<= 16 ms - Frame time p95 (heavy styled output):
<= 33 ms - Allocations/frame (normal UI load):
<= 32 KB - Allocations/frame (heavy styled output):
<= 96 KB - Input latency p95 (normal UI load):
<= 12 ms - Input latency p95 (heavy load):
<= 25 ms
Scenario Matrix
Required benchmark scenarios:
- Static dashboard:
- low churn, mixed controls
- validates baseline frame cost
- Log tail stream:
- high append rate + scrolling
- validates sustained render/update behavior
- Large table:
- wide + tall dataset with selection movement
- validates list/table throughput
- Overlay stress:
- command palette/context overlays on active base layout
- validates layered composition and focus transitions
- Resize storm:
- repeated terminal size changes
- validates recomposition and runtime stability
- Styled heavy output:
- frequent style/state changes across many cells
- validates style diffing/render overhead
Supplemental benchmark coverage (not part of the six-scenario gate checklist):
- viewport no-decoration render loop (
LogView) - validates hot-path viewport rendering with and without final materialization
- startup/input-latency p95 SLO lane (
SloLatencyBenchmarks) - validates startup first-frame p95 plus normal/heavy input latency p95 with machine-readable gate output
Harness Approach
Two-layer harness:
- Micro + component performance:
- BenchmarkDotNet scenarios for hot paths and control rendering loops
- deterministic inputs, fixed terminal sizes
- End-to-end smoke metrics:
- integration runs with timed startup/frame/input probes
- scenario scripts with stable seeds and fixed event sequences
Measurement rules:
- Release configuration only
- benchmark project enables Release-only
AllowUnsafeBlocksfor BenchmarkDotNet-generated harness compatibility - same terminal profile and dimensions per comparison
- warmup included before recorded samples
- explicit release-gate runner uses
2warmups +10measured iterations per scenario - BenchmarkDotNet trend lanes (
all|scenario|shortlist*) keep BenchmarkDotNet-managed iteration selection unless a lane explicitly overrides it
Benchmark modes:
- render-only mode:
- measures control/layout/style work up to render calls
- excludes string materialization (
canvas.Render())
- render+materialize mode:
- includes final buffer/string materialization (
canvas.Render()) - captures end-to-end frame cost and allocation impact seen by app authors
- includes final buffer/string materialization (
Gating policy by mode:
- render-only is the regression gate for renderer/layout internals (hot-path control cost)
- render+materialize is the release-facing gate for frame/allocation budgets
- Public V1 perf gate requires both mode families to remain within regression budget
Harness quick commands:
- List benchmarks:
dotnet run --project benchmarks/Tessera.Benchmarks --configuration Release -- --list flat
- Run all benchmarks in Release:
dotnet run --project benchmarks/Tessera.Benchmarks --configuration Release -- --inProcess --filter "*"
- Run a single benchmark scenario filter:
dotnet run --project benchmarks/Tessera.Benchmarks --configuration Release -- --inProcess --filter "*LargeTable*"
- Mode-specific examples (dual-mode instrumentation):
- render-only slice:
dotnet run --project benchmarks/Tessera.Benchmarks --configuration Release -- --inProcess --filter "*Only" - render+materialize slice:
dotnet run --project benchmarks/Tessera.Benchmarks --configuration Release -- --inProcess --filter "*Frame" --filter "*ScrollLogTail" --filter "*FirstFrameRender" --filter "*OverlayStressFrames" --filter "*ResizeStormFrames" - viewport slice:
dotnet run --project benchmarks/Tessera.Benchmarks --configuration Release -- --inProcess --filter "*Viewport*"
- render-only slice:
- Future V1 gate scenario filters (when benchmark classes are present):
dotnet run --project benchmarks/Tessera.Benchmarks --configuration Release -- --filter "*Resize*"dotnet run --project benchmarks/Tessera.Benchmarks --configuration Release -- --filter "*Overlay*"dotnet run --project benchmarks/Tessera.Benchmarks --configuration Release -- --filter "*LogTail*"
- Direct gate path:
- SLO gate baseline input:
docs/perf-baselines/v1-slo-gate-baseline.json - SLO gate machine-readable output:
docs/perf-baselines/latest-slo-gate-result.json - runtime e2e machine-readable output:
docs/perf-baselines/latest-runtime-e2e-result.json - SLO gate run:
dotnet run --project benchmarks/Tessera.Benchmarks --configuration Release -- --perf-gate --baseline docs/perf-baselines/v1-slo-gate-baseline.json --output docs/perf-baselines/latest-slo-gate-result.json
- SLO gate dry-run:
dotnet run --project benchmarks/Tessera.Benchmarks --configuration Release -- --perf-gate --baseline docs/perf-baselines/v1-slo-gate-baseline.json --output docs/perf-baselines/latest-slo-gate-result.json --dry-run
- runtime e2e:
dotnet run --project benchmarks/Tessera.Benchmarks --configuration Release -- --runtime-e2e --output docs/perf-baselines/latest-runtime-e2e-result.json
- SLO gate baseline input:
BenchmarkDotNet artifacts/report directory:
benchmarks/Tessera.Benchmarks/bin/Release/net10.0/BenchmarkDotNet.Artifacts/
Expected --list flat scenarios:
Tessera.Benchmarks.StartupRenderBenchmarks.StartupLikeFirstFrameRenderTessera.Benchmarks.StartupRenderBenchmarks.StartupLikeFirstFrameRenderOnlyTessera.Benchmarks.LogTailStreamBenchmarks.AppendAndScrollLogTailTessera.Benchmarks.LogTailStreamBenchmarks.AppendAndScrollLogTailOnlyTessera.Benchmarks.LargeTableBenchmarks.RenderLargeTableFrameTessera.Benchmarks.LargeTableBenchmarks.RenderLargeTableFrameOnlyTessera.Benchmarks.OverlayStressBenchmarks.RenderOverlayStressFramesTessera.Benchmarks.OverlayStressBenchmarks.RenderOverlayStressFramesOnlyTessera.Benchmarks.ResizeStormBenchmarks.RenderResizeStormFramesTessera.Benchmarks.ResizeStormBenchmarks.RenderResizeStormFramesOnlyTessera.Benchmarks.StyledHeavyOutputBenchmarks.RenderStyledHeavyFrameTessera.Benchmarks.StyledHeavyOutputBenchmarks.RenderStyledHeavyFrameOnlyTessera.Benchmarks.ViewportRenderBenchmarks.RenderViewportNoDecorationTessera.Benchmarks.ViewportRenderBenchmarks.RenderViewportNoDecorationOnlyTessera.Benchmarks.SloLatencyBenchmarks.StartupFirstFrameP95MsTessera.Benchmarks.SloLatencyBenchmarks.InputLatencyNormalP95MsTessera.Benchmarks.SloLatencyBenchmarks.InputLatencyHeavyP95Ms
Comparison Protocol vs Other TUIs
Comparison is methodology-first, not marketing-first:
- compare identical scenario definitions
- same machine, same terminal, same resolution
- same runtime mode (release/optimized)
- report median, p95, and memory/alloc deltas
- include caveats for non-equivalent feature sets
Fairness rules:
- do not compare feature-rich scenario against minimal baseline scenario
- publish benchmark scripts and raw output
- avoid cherry-picked single-run numbers
Regression Budget and Release Gate
Regression budget (relative to last accepted baseline):
- startup regression > 10% -> fail gate
- frame time p95 regression > 10% -> fail gate
- allocations/frame regression > 15% -> fail gate
- input latency p95 regression > 10% -> fail gate
Release gate for Public V1:
- all required scenarios executed
- no metric exceeds regression budget
- SLO targets met or variance explained with approved mitigation plan
- perf report attached to release checklist
Supplemental release-confidence lane:
- runtime e2e probe executes through the public hosting seam (
TesseraHost+ terminal adapter + decoder + renderer flush) - current V1 policy: collect and attach the runtime e2e result, but do not fail RC solely on that lane until a baseline policy is accepted
Iteration Reporting (Before/After)
Purpose:
- capture one comparable before/after pair for the same commit range, machine, terminal, and runtime config
- make gate decisions explicit by mode (
render-onlyandrender+materialize)
Workflow:
- run shortlist in the selected mode on baseline commit (before)
- run shortlist in the same mode on candidate commit (after)
- append one iteration row per scenario with both modes in the same row
- mark gate result (
pass/fail) with short reason
Minimum report fields:
- date (UTC), before commit, after commit
- scenario name, render-only before/after mean+alloc, materialize before/after mean+alloc
- mean/alloc delta % for each mode
- gating result