Skip to content

Benchmarks

这页记录 vue-tui 当前公开 benchmark 口径。它用于 1.0 release quality bar 和后续回归对比,不用于宣称对 @opentui/solid 或其他 TUI renderer 的横向性能优势。

Scope

当前 benchmark 覆盖的是 vue-tui 自身的可复现回归指标:

  • dirty rows、scanned nodes、painted nodes
  • frame task coalescing、dropped updates、commit count
  • DOM renderer cache hit、span reuse、segment reuse
  • TVirtualList wheel burst
  • TLogView append、retention、wrap、ANSI、OSC8 links、search、exact visual index
  • Markdown 图片热绘制、Kitty placement 移动的 stdout bytes、TVirtualMarkdown 单行滚动脏行数

当前 benchmark 不覆盖:

  • 真实 terminal emulator input-to-paint latency
  • 长时间 streaming 后的 GC heap 曲线
  • 常规滚动、输入和 append 场景的 stdout bytes per frame(Markdown Kitty 移动已有专项数据)
  • @opentui/solid 的同机器、同 terminal、同场景横向结果

这些缺口必须在 release notes 中明确说明,不能用现有 baseline 推导出 raw throughput 或竞品性能结论。

Reproduce

bash
pnpm run bench:dom-renderer
pnpm run bench:scroll-mailbox
pnpm run bench:phase2
pnpm run bench:markdown-graphics
pnpm run bench:baseline
pnpm run bench:baseline:timing

bench:baseline 是 release gate,默认只检查行为预算。bench:baseline:timing 会额外检查 timing budget,适合本地 release review 或 nightly,不作为默认 CI blocker。

bench:markdown-graphics 是 Markdown 图形专项 benchmark。它测量 1 MiB 图片的重复热绘制、256 KiB Kitty 图片的 100 次移动,以及 TVirtualMarkdown 单行滚动的脏行数。耗时用于同机 before/after 比较;stdout bytes、图片传输次数和脏行数是可重复的行为指标,当前不属于 release gate。

Environment

Non-release Local Sample

This sample is collected outside the release matrix. It is kept only to show result shape and make the sample values below traceable. Do not use it as the 1.0.0 release benchmark record. When cutting 1.0.0, add a new release benchmark record and keep this local sample only as historical context.

FieldValue
Date2026-05-25
Git SHA1b36fb4a3b80b8165aa09443f008604440e854f9
Package@simon_he/vue-tui@1.0.0-rc.0
HostDarwin arm64, Darwin Kernel Version 23.5.0
Nodev23.11.0
pnpm10.33.4
Bun1.3.13
DOM envhappy-dom synthetic benchmark
CommandBENCH_TIMING=1 pnpm exec tsx scripts/check-bench-baselines.ts
Result[bench:baseline] passed
RawJSON, summary
NoteStable release notes should use Node 20 or another runtime-matrix Node version, and record the exact release machine, tarball digest, workflow URLs, raw artifacts, and run output.

Release Gate Budgets

These budgets come from scripts/bench-baselines.json.

ScenarioBudgetRelease gate
DOM cache-hit plain rowscacheHits = 100yes
DOM single styled row reusespansReused = 100yes
DOM changed multi-segment row reusesegmentReuseRows = 100yes
DOM mixed row-key prepassrowKeyPrepassChecks = 100yes
DOM renderer second flush timingsecondFlushDurationMs <= 20/30/50 depending scenariomanual / nightly
1000 render nodes, dirty 1 rowdirtyRows = 1, scannedNodes = 1, paintedNodes = 1yes
1000 render nodes, dirty 1 row timingdurationMs <= 10manual / nightly
TVirtualList 100k rows, wheel burst 100frames = 1, coalescingRatio >= 100, avgScannedNodes <= 1, avgPaintedNodes <= 1yes
TVirtualList 100k rows timingmaxFrameMs <= 5manual / nightly
DOM sync flush 20 dirty rowsdirtyRows = 20yes
DOM sync flush 20 dirty rows timingdomFlushMs <= 10manual / nightly
TLogView 1000 lines burstframes <= 2, getLineCalls <= 30, dirtyRows <= 10, domFlushRows <= 20yes
TLogView 1000 lines burst timingmaxFrameMs <= 10manual / nightly
TLogView retention 100k, max 1000retainedLineCount = 1000, getLineCalls <= 30yes
TLogView retention timingmaxFrameMs <= 10manual / nightly
TLogView retained search, wrapped linesmatchCount = 2000, getLineCalls <= 25000yes
TLogView search wrap long lines timingmaxFrameMs <= 10manual / nightly
TLogView exact index retention appendmeasuredLineCount = 1000yes
TList wheel / TLogView append / TVirtualList wheel burstframeTaskCount = 1, droppedUpdates >= 999, dirtyRows <= 20, commits = 1yes

Non-release Local Sample Results

Timing values below are examples from the non-release local run above. Treat them as context, not as portable guarantees across machines.

ScenarioResult
1000 render nodes, dirty 1 rowdurationMs = 0.361, dirtyRows = 1, scannedNodes = 1, paintedNodes = 1
TVirtualList 100k rows, wheel burst 100frames = 1, maxFrameMs = 0.323, coalescingRatio = 100, avgScannedNodes = 1
DOM sync flush 20 dirty rowsdirtyRows = 20, domFlushMs = 0.218
TLogView 1000 lines burstframes = 2, maxFrameMs = 0.486, getLineCalls = 20, dirtyRows = 10
TLogView long-line burstframes = 2, maxFrameMs = 1.611, getLineCalls = 20, dirtyRows = 10
TLogView wrapped long-line burstframes = 2, maxFrameMs = 0.439, getLineCalls = 1, dirtyRows = 10
TLogView retention 100k, max 1000frames = 1, maxFrameMs = 0.444, retainedLineCount = 1000, getLineCalls = 20
TLogView ANSI retention, max 1000frames = 1, maxFrameMs = 2.622, retainedLineCount = 1000, getLineCalls = 20
TLogView OSC8 links retainedframes = 1, maxFrameMs = 8.919, retainedLineCount = 1000, getLineCalls = 20
TLogView retained search wrapped linesmatchCount = 2000, getLineCalls = 20000, maxFrameMs = 0.432
TLogView exact index retention appendmeasuredLineCount = 1000, visualRowCount = 4000, maxFrameMs = 0.513
TLogView Lab smokelineCount = 600, visibleLinks = 14, matchCount = 67, maxFrameMs = 16.196

Markdown Graphics Optimization Sample

这是 2026-08-13 在同一台机器上,用 bench:markdown-graphics 分别运行 origin/main (bd678c6d2f905ca67de6b9a8e4d72ab95cd6399d) 和本 PR 工作树得到的非 release 样本。耗时是 5 次样本的中位数;不能跨机器解释为性能保证。

Scenarioorigin/mainThis PRChange
1 MiB image hot paint, 100 iterations732.373 ms1.500 ms488.2x faster
256 KiB Kitty image move, 100 moves810.515 ms126.773 ms6.4x faster
Kitty move stdout bytes13,362,925465,88496.5% fewer
Kitty image transmissions50198.0% fewer
Kitty placement operations5099placement-only after initial transmit
Kitty delete operations990no clear/retransmit cycle
TVirtualMarkdown dirty rows for one-row scroll6183.3% fewer

Environment: macOS 14.5 / Darwin 23.5.0 arm64, Node v24.16.0, pnpm 10.34.5. Raw values and comparison metadata are recorded in the JSON artifact and the result summary.

DOM Renderer Cache Sample

ScenarioResult
Cache-hit plain rowscacheHits = 100, secondFlushDurationMs = 0.599
Changed plain rowsplainTextRows = 100, secondFlushDurationMs = 0.942
Single styled row reusespansReused = 100, secondFlushDurationMs = 2.86
Changed multi-segment row reusesegmentReuseRows = 100, spansReused = 200, secondFlushDurationMs = 8.304
Mixed row-key prepassrowKeyPrepassChecks = 100, cacheHits = 55, secondFlushDurationMs = 1.443

Mailbox Sample

ComponentScenarioResult
TListwheel burstframeTaskCount = 1, droppedUpdates = 999, dirtyRows = 20, commits = 1
TLogViewwheel burstframeTaskCount = 1, droppedUpdates = 999, dirtyRows = 20, commits = 1
TLogViewappend burstframeTaskCount = 1, droppedUpdates = 999, dirtyRows = 20, commits = 1
TVirtualListwheel burstframeTaskCount = 1, droppedUpdates = 999, dirtyRows = 20, commits = 1

User-Perceived Metrics Still Needed

The next benchmark report should add real terminal/browser runs for:

MetricTarget
input-to-paint p95Small chrome/input updates at 80x24 and 120x40, with p95 and p99 latency
long streaming heap100k line append with retention, GC-after-run heap, and no sustained growth across repeated run
stdout bytes/frameANSI bytes written per frame for scroll, input, and append scenarios

These should be measured before using performance claims in external comparison copy.

@opentui/solid Comparison Status

There is no professional same-scenario comparison yet.

OpenTUI's public docs describe OpenTUI as a native Zig terminal UI core with TypeScript bindings and currently Bun-first installation. The @opentui/solid npm page describes Solid.js support for OpenTUI, and the OpenTUI renderer docs say createCliRenderer() loads the native Zig rendering library.

That means the credible comparison today is product fit, not raw speed:

Dimensionvue-tui@opentui/solid
FrameworkVue 3 components and Vue reactivitySolid JSX binding
Runtime targetNode runtime package target plus browser DOM, CLI stdout, and headlessBun-first OpenTUI terminal app path
Renderer routeJS terminal buffer with DOM renderer, stdout renderer, and headless useOpenTUI native Zig renderer through TypeScript binding
Strong storyVue, browser-hosted terminal, Node/Vite install, logs/markdown/agent UISolid JSX plus native OpenTUI terminal rendering
Claim to avoidDo not claim faster raw renderer throughput without same-scenario dataNot applicable

If vue-tui compares against @opentui/solid, use the same machine, same terminal, same viewport, same runtime constraints, and application-level scenarios. The full comparison protocol lives in Compare With OpenTUI Solid.

ScenarioMetrics
100k virtual list scrollcold start, first paint, 100 scroll events total, p95 input-to-paint, frames, stdout bytes, heap delta, CPU
streaming log / transcriptappend throughput, p95/p99 frame time, render calls per visible frame, heap after retention, detached scroll
agent consolemarkdown update p95, input typing p95, overlay open/close p95, stdout bytes/frame, dropped frames over 60s
browser-hosted terminalvue-tui-only DOM renderer data: first render, dirty row flush, cache-hit flush, Vite import, SSR import

Bug reports, feature requests, and documentation issues are tracked on GitHub Issues.