关于 chat-perf
面向 VS Code 聊天界面开发者的性能测试工具,可基准测试模拟聊天场景,并比较不同构建或版本。它同时测量渲染、响应耗时和内存,并提供单独的内存泄漏检查。
主要功能
- 01模拟聊天场景并测量性能
- 02比较构建或版本基线
- 03采集渲染与响应耗时指标
- 04分析堆增长和 DOM 节点变化
- 05支持 CI 回归检测
适用场景
- 01评估聊天界面代码改动
- 02比较两个 VS Code 构建
- 03在 CI 中检测性能回归
- 04检查多轮聊天的内存泄漏
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ microsoft/ vscode --skill chat-perf安装后在 Claude Code 中输入/chat-perf确认已加载。
详细介绍
聊天性能测试
适用场景
- 修改聊天渲染代码(
chatListRenderer.ts、chatInputPart.ts、Markdown 渲染)前后 - 更改流式响应管线或 SSE 处理时
- 修改聊天组件中的 disposable/lifecycle 模式时
- 比较两个 VS Code 版本的性能时
- 在 CI 中为涉及聊天 UI 代码的 PR 设置门禁
快速开始
# Run perf regression test (compares local dev build vs VS Code 1.115.0):
npm run perf:chat -- --scenario text-only --runs 3
# Run all scenarios with no baseline (just measure):
npm run perf:chat -- --no-baseline --runs 3
# Compare two local builds (apples-to-apples):
npm run perf:chat -- --build /path/to/build-A --baseline-build /path/to/build-B --runs 5
# Build a local production package and compare against a release:
npm run perf:chat -- --production-build --baseline-build 1.115.0 --runs 5
# Run memory leak check (10 messages in one session):
npm run perf:chat-leak
# Run leak check with more messages for accuracy:
npm run perf:chat-leak -- --messages 20 --verbose
性能回归测试
脚本: scripts/chat-simulation/test-chat-perf-regression.js npm: npm run perf:chat
通过 Playwright Electron 启动 VS Code,打开聊天面板,发送一条带有模拟 LLM 响应的消息,并测量耗时、布局和渲染指标。默认情况下,会下载 VS Code 1.115.0 作为基线,对其进行基准测试,然后对本地开发构建进行基准测试并进行比较。
并非总是需要基线。 基线仅用于比较(检测回归)。如果你只想获取当前构建的数据——分析单项改动、捕获跟踪记录/堆快照,或迭代测试场景——请传入
--no-baseline,完全跳过基线的下载和基准测试(运行时间大约减半)。基线比较能将原始测量结果转化为通过/失败判定;没有基线时,你仍能获得全部指标,只是没有判定结果。
主要标志
| 标志 | 默认值 | 说明 |
|---|---|---|
--runs <n> | 5 | 每个场景的运行次数。次数越多,结果越稳定。CI 中建议使用 5 次以上。 |
--scenario <id> / -s | all | 要测试的场景(可重复指定)。参见 common/perf-scenarios.js。 |
--build <path|ver> / -b | local dev | 要测试的构建。接受路径或版本(1.110.0、insiders、提交哈希)。 |
--baseline <path> | — | 与之前保存的基线 JSON 文件进行比较。 |
--baseline-build <path|ver> | 1.115.0 | 要作为基线进行基准测试的版本或本地路径。 |
--no-baseline | — | 完全跳过基线,只测量测试构建(无需下载、无需比较,速度约快 2 倍)。仅需原始数据而不需要回归判定时使用。 |
--save-baseline | — | 将结果保存为新基线(需要 --baseline <path>)。 |
--resume <path> | — | 恢复之前的运行,并增加迭代次数以提高置信度。 |
--threshold <frac> | 0.2 | 回归阈值(0.2 = 慢 20% 时标记)。 |
--production-build | — | 通过 gulp vscode 构建本地捆绑包,以便与发布基线比较。 |
--no-cache | — | 忽略缓存的基线数据,始终重新运行。 |
--force | — | 跳过构建模式不匹配确认提示。 |
--ci | — | CI 模式:将 Markdown 摘要写入 ci-summary.md(隐含 --no-cache、--heap-snapshots、--cleanup-diagnostics)。 |
--heap-snapshots | — | 每次运行后获取堆快照(速度较慢;在 --ci 模式下自动启用)。 |
--gc-object-stats | — | 仅用于 GC 深入分析。 启用 V8 gc_stats 跟踪(每次 GC 都转储各类型的堆对象)。⚠️ 会破坏所有耗时指标——请求过程中发生一次主要 GC 就会增加约 550 ms,因此切勿用于基准测试。默认关闭;内存分析时优先使用堆快照。 |
--cleanup-diagnostics | — | 删除堆快照、CPU 配置文件和跟踪记录以节省磁盘空间。运行期间仅保留最近一次运行的文件;比较后会删除未发生回归场景的文件。在 --ci 模式下自动启用。 |
--setting <k=v> | — | 为所有构建设置 VS Code 设置覆盖项(可重复指定)。 |
--test-setting <k=v> | — | 仅为测试构建设置 VS Code 设置覆盖项。 |
--baseline-setting <k=v> | — | 仅为基线构建设置 VS Code 设置覆盖项。 |
--verbose | — | 输出每次运行的详细信息,包括响应内容。 |
比较两个远程构建
# Compare 1.110.0 against 1.115.0 (no local build needed):
npm run perf:chat -- --build 1.110.0 --baseline-build 1.115.0 --runs 5
比较两个本地构建版本
--build 和 --baseline-build 都接受 VS Code 可执行文件的本地路径。这使你能够对任意两个构建版本进行公平比较:
# Compare two dev builds (e.g. feature branch vs main):
npm run perf:chat -- \
--build .build/electron/Code\ -\ OSS.app/Contents/MacOS/Code\ -\ OSS \
--baseline-build /path/to/other/Code\ -\ OSS.app/Contents/MacOS/Code\ -\ OSS \
--runs 5
# Compare two production builds:
npm run perf:chat -- \
--build ../VSCode-darwin-arm64-feature/Code\ -\ OSS.app/Contents/MacOS/Code\ -\ OSS \
--baseline-build ../VSCode-darwin-arm64-main/Code\ -\ OSS.app/Contents/MacOS/Code\ -\ OSS \
--runs 5
本地路径基准版本不会缓存(构建版本可能会在两次运行之间发生变化)。版本字符串基准版本会缓存,以便重复使用。
构建模式和不匹配检测
该工具根据可执行文件路径将构建版本分为三种模式:
| 模式 | 来源 | 特征 |
|---|---|---|
dev | .build/electron/(本地开发) | 未打包的源代码、VSCODE_DEV=1、NODE_ENV=development。内存和启动开销较高。 |
production | ../VSCode-<platform>-<arch>/(通过 gulp vscode 构建) | 已打包的 JS,无开发标志。符合发布版本的特征,但使用本地源代码。 |
release | .vscode-test/(通过 @vscode/test-electron 下载) | 官方发布的构建版本。 |
当测试构建版本和基准构建版本的模式不同时(例如 dev 与 release),该工具会显示警告并提示确认。使用 --force 或 --ci 可跳过提示。
使用 --production-build 会通过 gulp vscode 构建本地打包版本,以便与 release 基准版本进行公平比较。这样既能消除开发模式的开销,又能测试本地更改。
# Production build vs release baseline (fair comparison):
npm run perf:chat -- --production-build --baseline-build 1.115.0 --runs 5
设置覆盖
使用 --setting、--test-setting 和 --baseline-setting 可向启动的实例注入 VS Code 设置。这适用于对实验性功能进行 A/B 测试:
# Enable a feature for the test build only:
npm run perf:chat -- --test-setting chat.experimental.incrementalRendering.enabled=true --runs 3
# Compare two builds with different settings:
npm run perf:chat -- \
--baseline-build "../vscode2/.build/electron/Code - OSS.app/Contents/MacOS/Code - OSS" \
--baseline-setting chat.experimental.incrementalRendering.enabled=true \
--test-setting chat.experimental.incrementalRendering.enabled=false \
--runs 3
# Set a value for both builds:
npm run perf:chat -- --setting chat.mcp.enabled=false --runs 3
优先级:对于相同的键,--test-setting / --baseline-setting 会覆盖 --setting。值会自动解析:true/false 会转换为布尔值,数字会转换为数字,其他值均保留为字符串。
恢复运行以提高可信度
当结果超过阈值但统计显著性不足时,该工具会打印 --resume 提示。使用它可向现有运行中添加更多迭代:
# Initial run with 3 iterations — may be inconclusive:
npm run perf:chat -- --scenario text-only --runs 3
# Add 3 more runs to the same results file (both test + baseline):
npm run perf:chat -- --resume .chat-simulation-data/2026-04-14T02-15-14/results.json --runs 3
# Keep adding until confidence is reached:
npm run perf:chat -- --resume .chat-simulation-data/2026-04-14T02-15-14/results.json --runs 5
--resume 会加载之前的 results.json 及其关联的 baseline-*.json,为两个构建版本各运行 N 次迭代,合并 rawRuns,重新计算统计数据,并重新进行比较。更新后的文件会原地写回。你可以多次恢复运行,样本会逐步累积。
统计显著性
回归检测使用 Welch's t-test,以避免嘈杂测量结果造成误报。只有当某项指标既超过阈值又具有统计显著性(p < 0.05)时,才会标记为 REGRESSION。否则会报告为 (可能是噪声 — p=X,不显著)。
在典型方差(cv ≈ 20%)下,需要:
- 每个构建版本 n ≥ 5,才能以 95% 的置信度检测到 35% 的回归
- 每个构建版本 n ≥ 10,才能可靠地检测到 20% 的回归
报告的置信度等级:high(p < 0.01)、medium(p < 0.05)、low(p < 0.1)、none。
退出码
0— 所有指标均在阈值范围内,或虽超过阈值但统计显著性不足1— 检测到统计显著的回归,或所有运行均失败
场景
场景定义在 scripts/chat-simulation/common/perf-scenarios.js 中,并通过 registerPerfScenarios() 注册。分为三类:
- 仅内容 — 纯流式响应(例如
text-only、large-codeblock、rapid-stream) - 工具调用 — 包含工具调用的多轮场景(例如
tool-read-file、tool-edit-file) - 多轮用户对话 — 包含用户后续提问和思考块的多轮对话(例如
thinking-response、multi-turn-user、long-conversation)
运行 npm run perf:chat -- --help 可查看已注册场景 ID 的完整列表。
收集的指标
- 时间: 首个令牌耗时、完成耗时、渲染完成耗时(包括打字机动画)
- 渲染: 布局次数、布局耗时(ms)、样式重新计算次数、强制重排、长任务(>50ms)、长动画帧次数和耗时
- 内存: GC 前后的堆内存、GC 后的堆内存变化(仅供参考,单次请求的结果波动较大)
- 扩展宿主: 通过 CDP inspector 获取 GC 前后的堆内存及其变化
回归触发指标与信息性指标
只有以下指标会触发回归失败(当其超过阈值且具有统计显著性时):
timeToFirstToken、timeToComplete— 用户感知延迟layoutDurationMs— 跟踪记录中的总布局耗时(实际布局开销)forcedReflowCount— 强制同步布局总是不利的longTaskCount— 主线程卡顿
以下指标会报告,但仅供参考(不会导致 CI 失败):
longAnimationFrameCount— 长动画帧数量;该指标噪声较大且由合成器驱动,因此会跟踪以便观察趋势,但不会作为门槛。实际卡顿由longTaskCount/layoutDurationMs把关。layoutCount— 布局操作数量;CSS 动画会使其数值偏高(由合成器驱动,开销低)。某个构建版本可能执行更多但开销更低的布局,因此应以layoutDurationMs而非此计数作为门槛。recalcStyleCount— 样式重新计算次数;CSS 动画会使其数值偏高(由合成器驱动,开销低)timeToRenderComplete— 包含打字机动画的尾部时间- 内存/堆指标 — 对单次请求基准测试而言噪声太大
统计数据
结果使用基于 IQR 的异常值剔除和中位数(而非平均值)来处理启动抖动。报告变异系数(cv)——低于 15% 表示稳定,高于 15% 时会发出 ⚠ 警告。基线对比会对原始运行值使用 Welch's t 检验,以在标记回归前判断其是否具有统计显著性。建议至少运行 5 次,以获得稳定结果。
内存泄漏检查
脚本: scripts/chat-simulation/test-chat-mem-leaks.js npm: npm run perf:chat-leak
启动一个 VS Code 会话,依次发送 N 条消息,在每条消息之间强制执行 GC,并测量渲染进程堆和 DOM 节点数量。使用样本进行线性回归,计算每条消息的增长率,并将其与阈值进行比较。
主要标志
| 标志 | 默认值 | 描述 |
|---|---|---|
--messages <n> / -n | 10 | 要发送的消息数量。数量越多,斜率越准确。 |
--build <path|ver> / -b | 本地开发版 | 要测试的构建版本。 |
--threshold <MB> | 2 | 每条消息的最大堆增长量,单位为 MB。 |
--setting <k=v> | — | 设置 VS Code 设置覆盖项(可重复指定)。 |
--verbose | — | 打印每条消息的堆/DOM 计数。 |
测量内容
- 堆增长斜率(MB/条消息)——对强制 GC 后的堆样本进行线性回归。泄漏会表现为持续的正斜率。
- DOM 节点增长(节点/条消息)——用于捕捉元素未清理的渲染泄漏。正常的聊天会对旧消息进行虚拟化处理,因此节点数量会趋于稳定。
结果解读
0.3–1.0 MB/msg— 正常(V8 内部开销、字符串驻留)>2.0 MB/msg— 可能存在泄漏,请检查被保留的对象- 第一条消息之后 DOM 节点数量保持稳定 — 正常(聊天列表虚拟化正常工作)
- DOM 节点数量线性增长 — 渲染泄漏,请检查可释放资源的清理情况
CI 运行与回归定位
性能和泄漏检查通过 .github/workflows/chat-perf.yml 工作流在 CI 中运行(每日定时触发 workflow_dispatch,并支持手动触发)。每次运行都会将当前 main 作为测试构建版本,与固定的发布基线进行基准测试(来自 config.jsonc,例如 1.122.0)。由于基线固定,各次每日运行中某项指标的测试中位数会描绘 main 随时间的变化轨迹,这使你能够通过二分查找确定回归或不稳定情况发生的时间。
查找和查看历史运行记录
# List recent runs (most recent first) — note the run IDs and dates
gh run list --workflow chat-perf.yml -R microsoft/vscode --limit 30 \
--json databaseId,status,conclusion,createdAt,headBranch \
--jq '.[] | [.databaseId, (.conclusion//.status), .createdAt, .headBranch] | @tsv'
# See a run's per-job results
gh run view <run-id> -R microsoft/vscode --json jobs \
--jq '.jobs[] | [.name, (.conclusion//.status)] | @tsv'
# Trigger a run manually against any ref/version:
gh workflow run chat-perf.yml -R microsoft/vscode --ref main \
-f test_build=<branch|sha|version> -f baseline_build=<version>
每次运行的构件(及保留期限——这对较早的运行记录很重要)
| 构件 | 内容 | 保留期限 |
|---|---|---|
chat-perf-summary | 统一的 ci-summary.md:判定结果、各指标中位数 ± 标准差、逐次运行的原始数据表 | 30 天 |
perf-results-<group> | 下方所有内容,另加跟踪记录、CPU 配置文件、堆快照(体积较大) | 30 天 |
leak-results | 泄漏日志 + chat-simulation-leak-results.json | 30 天 |
perf-summary-<group> | results.json(完整的逐次运行指标,包括 rawRuns)+ baseline-*.json | 1 天 |
# List a run's artifacts + whether they've expired
gh api repos/microsoft/vscode/actions/runs/<run-id>/artifacts \
--jq '.artifacts[] | [.name, .expired] | @tsv'
# Download the human-readable summary (best first stop; survives 30 days)
gh run download <run-id> -R microsoft/vscode -n chat-perf-summary
定位指标回退 / 变得不稳定的时间点
- 按日期二分查找。 从覆盖该时间段的多次运行中提取
chat-perf-summary/ci-summary.md。逐次比较可疑指标和场景运行的测试中位数(及 ±stddev)——指标开始跳变的那一天,就是main发生变更的时间。指标呈现双峰分布(例如,原始运行列显示两个簇,如~250 / ~900)是不稳定的特征;稳定的偏移则是真正的回归。 - 确认问题真实存在,而非测量噪声。 检查每次运行的原始表格(位于
ci-summary.md中)——较高的 ±stddev / 双峰值意味着少数异常运行拉动了中位数,而不是出现了普遍的变慢。 - 深入分析原因。 下载慢速运行对应的
perf-results-<group>(其中包含每次运行的trace.json),检查慢速时间窗口中占主导的内容。发现gc_stats产物的技巧:计算两个code/chat/*标记(例如willCollectInstructions→didCollectInstructions)之间主线程RunTask的持续时间总和——如果该窗口的忙碌时间约为 0%,说明这是异步等待或 GC 暂停,而非实际工作;接着查看该窗口中持续时间最长的X阶段事件(MajorGC、Layout等)。 - 在本地重现可疑提交以精确二分查找:
npm run perf:chat -- --build <sha> --baseline-build <ver> --runs 7(也可以直接比较两个提交:--build <shaA> --baseline-build <shaB>)。
提示:
perf-summary-*(包含机器可读的results.json和rawRuns)会在 1 天后删除,因此对于较早的运行,请依赖chat-perf-summary(原始表格,保留 30 天),或从perf-results-*中提取results.json(同样保留 30 天)。
架构
scripts/chat-simulation/
├── common/
│ ├── mock-llm-server.js # Mock CAPI server matching @vscode/copilot-api URL structure
│ ├── perf-scenarios.js # Built-in scenario definitions (content, tool-call, multi-turn)
│ └── utils.js # Shared: paths, env setup, stats, launch helpers
├── config.jsonc # Default config (baseline version, runs, thresholds)
├── fixtures/ # TypeScript fixture files used by tool-call scenarios
├── test-chat-perf-regression.js
└── test-chat-mem-leaks.js
模拟服务
模拟 LLM 服务端(common/mock-llm-server.js)实现了来自 @vscode/copilot-api 的 DomainService 的完整 CAPI URL 结构:
GET /models— 返回模型元数据POST /models/session— 返回包含available_models和session_token的AutoModeAPIResponsePOST /models/session/intent— 模型路由器POST /chat/completions— 返回与场景匹配的 SSE 流式响应- Agent、会话、遥测和令牌端点
copilot 扩展通过 IS_SCENARIO_AUTOMATION=1 模式连接到此服务,并使用 overrideCapiUrl 和 overrideProxyUrl 设置。必须禁用 vscode-api-tests 扩展(--disable-extension=vscode.vscode-api-tests),因为它会注册重复的 copilot 供应商,从而阻止真实扩展注册语言模型提供程序。
添加场景
- 使用
common/mock-llm-server.js中的ScenarioBuilderAPI,在common/perf-scenarios.js的相应对象(CONTENT_SCENARIOS、TOOL_CALL_SCENARIOS或MULTI_TURN_SCENARIOS)中添加新条目 registerPerfScenarios()会自动注册该场景——无需手动更新 ID 列表- 运行:
npm run perf:chat -- --scenario your-new-scenario --runs 1 --no-baseline --verbose
相关技能
- heap-snapshot-analysis — 当性能回归或泄漏检查发现内存增长较高时,使用 heap-snapshot-analysis 技能深入分析。它可以解析
.heapsnapshot文件、比较前后快照、对对象差异分组,并追踪保留路径,以找出导致已释放对象仍然存活的原因。chat-perf 泄漏检查用于测量整体堆斜率;heap-snapshot-analysis 则用于找出导致问题的具体对象。 - auto-perf-optimize — 用于启动 VS Code、运行场景,并在进行底层分析前自动捕获堆快照或 CPU 配置文件。