chat-perf について
VS Code チャットの開発者向けに、模擬チャットシナリオをベンチマークし、ビルドやバージョンを比較する性能テストツールです。レンダリング、応答時間、メモリを測定し、別途メモリリークも検査できます。
主な機能
- 01模擬チャットシナリオのベンチマーク
- 02ビルドやバージョンの基準比較
- 03レンダリングと応答時間の測定
- 04ヒープ増加と DOM ノード変化の追跡
- 05CI での回帰検出
活用シーン
- 01チャット UI のコード変更を評価
- 022 つの VS Code ビルドを比較
- 03CI で性能回帰を確認
- 04チャット反復によるメモリリークを調査
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ microsoft/ vscode --skill chat-perfインストール後、Claude Code で/chat-perfを入力して読み込まれたか確認します。
詳細
チャットのパフォーマンステスト
使用するタイミング
- チャットのレンダリングコード(
chatListRenderer.ts、chatInputPart.ts、Markdown のレンダリング)を変更する前後 - ストリーミング応答パイプラインや SSE 処理を変更するとき
- チャットコンポーネントの破棄やライフサイクルのパターンを変更するとき
- 2 つの VS Code リリース間のパフォーマンスを比較するとき
- チャット UI コードに触れる PR を CI でゲートするとき
クイックスタート
# 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 が発生すると約 550ms 追加されるため、ベンチマークには絶対に使用しないでください。デフォルトでは無効です。メモリ分析にはヒープスナップショットを推奨します。 |
--cleanup-diagnostics | — | ディスク容量を節約するため、ヒープスナップショット、CPU プロファイル、トレースを削除します。実行中は最新の実行分のファイルのみを保持し、比較後は回帰していないシナリオのファイルを削除します。--ci モードでは自動的に有効になります。 |
--setting <k=v> | — | すべてのビルドに適用する VS Code 設定のオーバーライドを指定します(繰り返し指定できます)。 |
--test-setting <k=v> | — | テスト対象のビルドだけに適用する VS Code 設定のオーバーライドを指定します。 |
--baseline-setting <k=v> | — | ベースラインのビルドだけに適用する VS Code 設定のオーバーライドを指定します。 |
--verbose | — | 応答内容を含む、実行ごとの詳細を出力します。 |
2 つのリモートビルドを比較する
# 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
2 つのローカルビルドを比較する
--build と --baseline-build はどちらも、VS Code 実行ファイルへのローカルパスを受け付けます。これにより、任意の 2 つのビルドを公平に比較できます。
# 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
ローカルパスのベースラインはキャッシュされません(実行ごとにビルドが変わる可能性があるためです)。バージョン文字列のベースラインは再利用のためにキャッシュされます。
ビルドモードと不一致の検出
このツールは、実行ファイルのパスに基づいてビルドを 3 つのモードに分類します。
| モード | ソース | 特徴 |
|---|---|---|
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 経由でローカルのバンドル済みパッケージをビルドでき、リリース版のベースラインと公平に比較できます。これにより、ローカルの変更をテストしながら、開発モードのオーバーヘッドを排除できます。
# 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 の t 検定を使用します。指標がしきい値を超え、かつ統計的に有意である場合にのみ、その指標は REGRESSION として報告されます(p < 0.05)。それ以外の場合は (likely noise — p=X, not significant) として報告されます。
一般的な分散(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() 経由で登録されます。カテゴリは 3 つあります。
- コンテンツのみ — 通常のストリーミング応答(例:
text-only、large-codeblock、rapid-stream) - ツール呼び出し — ツール呼び出しを含む複数ターンのシナリオ(例:
tool-read-file、tool-edit-file) - 複数ターンのユーザー会話 — ユーザーによる追加質問や思考ブロックを含む複数ターンの会話(例:
thinking-response、multi-turn-user、long-conversation)
登録済みシナリオ ID の全一覧を確認するには、npm run perf:chat -- --help を実行します。
収集される指標
- 時間: 最初のトークンまでの時間、完了までの時間、描画完了までの時間(タイプライターアニメーションを含む)
- 描画: レイアウト回数、レイアウト所要時間(ms)、スタイル再計算回数、強制リフロー、長時間タスク(>50ms)、長時間アニメーションフレームの回数と所要時間
- メモリ: GC 前後のヒープ、GC 後のヒープ差分(参考情報。単一リクエストではノイズが多くなります)
- 拡張機能ホスト: CDP インスペクター経由の GC 前後のヒープと差分
リグレッションを引き起こす指標と参考情報の指標
次の指標のみ、しきい値を統計的に有意な形で超えた場合にリグレッション失敗を引き起こします。
timeToFirstToken、timeToComplete— ユーザーが体感するレイテンシlayoutDurationMs— トレースに基づくレイアウトの合計時間(実際のレイアウトコスト)forcedReflowCount— 強制的な同期レイアウトは常に好ましくありませんlongTaskCount— メインスレッドのカクつき
次の指標も報告されますが、参考情報のみです(CI は失敗しません)。
longAnimationFrameCount— 長いアニメーションフレームの数です。ノイズが多く、コンポジターに左右されるため、傾向の把握には追跡しますが、ゲート条件にはしません。実際のカクつきはlongTaskCount/layoutDurationMsで判定します。layoutCount— レイアウト処理の数です。CSS アニメーションによって数が増えます(コンポジター処理のため、低コストです)。ビルドによっては、レイアウト回数が増えても低コストの場合があるため、この回数ではなくlayoutDurationMsをゲート条件にします。recalcStyleCount— スタイル再計算の数です。CSS アニメーションによって数が増えます(コンポジター処理のため、低コストです)。timeToRenderComplete— タイプライターアニメーションの終了までの時間を含みます- メモリ/ヒープ指標 — 1 リクエストのベンチマークではノイズが多すぎます
統計
起動時のばらつきに対処するため、結果では IQR ベースの外れ値除去と中央値(平均ではありません)を使用します。**変動係数(cv)**も報告されます。15%未満なら安定しており、15%を超えると ⚠ 警告が表示されます。ベースラインとの比較では、リグレッションを指摘する前に、生の実行値に対して Welch の t 検定を行い、統計的有意性を判定します。安定した結果を得るには、5 回以上実行してください。
メモリリークのチェック
スクリプト: scripts/chat-simulation/test-chat-mem-leaks.js npm: npm run perf:chat-leak
VS Code セッションを 1 つ起動し、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を取得します。疑わしい指標とシナリオについて、実行ごとの test の中央値(および ±stddev)を比較します。値が跳ね上がった日が、mainに変更が入ったタイミングです。指標が 二峰性(たとえば、raw-run の列が~250 / ~900のような2つのクラスターを示す)になるのは、不安定さの兆候です。安定した変化なら、実際の性能劣化です。 - 測定ノイズではなく、実際の問題かを確認します。 実行ごとの raw テーブル(
ci-summary.md内)を確認します。±stddev が大きい値や二峰性の値は、一様な低速化ではなく、少数の外れ値によって 中央値 が動いていることを示します。 - 原因を詳しく調べます。 遅い実行の
perf-results-<group>(各実行のtrace.jsonを含みます)をダウンロードし、遅い時間帯で何が処理時間を占めているかを調べます。gc_statsのアーティファクトを見つけた手法は次のとおりです。2つの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>で2つのコミットを直接指定します)。
ヒント:
perf-summary-*(rawRunsを含む機械可読なresults.json)は1日後に削除されるため、古い実行についてはchat-perf-summary(raw テーブル、30日間保持)を参照するか、perf-results-*(こちらも30日間保持)からresults.jsonを抽出してください。
アーキテクチャ
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を含むAutoModeAPIResponseを返しますPOST /models/session/intent— モデルルーターPOST /chat/completions— シナリオに一致する SSE ストリーミング応答- エージェント、セッション、テレメトリ、トークンの各エンドポイント
Copilot 拡張機能は、overrideCapiUrl と overrideProxyUrl の設定を使い、IS_SCENARIO_AUTOMATION=1 モードでこのサーバーに接続します。vscode-api-tests 拡張機能は、実際の拡張機能による言語モデルプロバイダーの登録を妨げる、重複した copilot ベンダーを提供するため、無効にする必要があります(--disable-extension=vscode.vscode-api-tests)。
シナリオの追加
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 プロファイルの自動取得を行う場合に使います。