Claude Code や Codex が書いたコードは、単体テストを通っていてもゲームを遅くすることがあります。 リファクタや機能追加が正しく動いていても、フレームタイム、GPU 時間、メモリ使用量まで以前と同じとは限りません。 必要なのは、「動くか」だけでなく「前のビルドより遅くなっていないか」も CI で確認する仕組みです。
テストが通っても、性能低下は見つからない
コードを書く手間が下がると、ボトルネックは「書く」から「確かめる」に移ります。 機能が動くかどうかはテストが自動で答えを出します。 けれど、そのコードが実機のフレームタイムを何ミリ秒か押し上げていないかは、テストの緑では分かりません。
変更の数が増えるほど、その一つひとつを人間が手でプロファイルするのは難しくなります。 描画スレッドに意図しないアロケーションが増える、更新ループの計算量が増える、GC の走る頻度が上がる。 どれも単体テストは通り、コードレビューでも見抜きにくく、実機に載せて初めて体感の劣化として現れます。
機能テストは「動くか」を確かめます。ビルド比較は「前より遅くなっていないか」を確かめます。
変更前より性能が悪くなることを、性能回帰と呼びます。 この性能回帰をレビュアーの気付きや経験則だけで探すのではなく、ビルドごとに同じ基準で確認する必要があります。
性能低下を CI で自動検出する
やることは単純です。 ビルドごとに性能テレメトリを集め、直前の正常なビルドと比べて、しきい値を超えて悪化していたら CI ジョブを失敗させる。 これなら人間が毎回数字を眺めなくても、性能低下を性能チェックの失敗としてマージ前に検出できます。
Framedash の framedash perf-diff は、二つのビルドのフレームタイム、メモリ、GPU 時間について P50 と P95 の両方を出すコマンドです。
--fail-on-regression を付けると、候補ビルドの P50 がしきい値を超えて悪化したときに終了コード 1 を返し、CI ジョブを失敗させます(判定は P50 の比較で行い、P95 は参考として併記されます)。
framedash perf-diff --baseline "$BASE_SHA" --candidate "$GITHUB_SHA" \
--threshold 5 --fail-on-regression
比較の対象はフレームタイム、メモリ、GPU 時間に加えて、マップのロード時間(load_time_ms)とディスク IO(io.*)です。
いずれも小さいほど良い指標として扱われます。
--metric で比較対象を特定の指標に絞る、--map や --platform で比較範囲を限定するといった調整もできます。
比較の単位となるのが build_id です。
どのビルドの数字なのかが一意に決まっていなければ、ビルド間の比較そのものが成り立ちません。
ワークフローの全体像
エージェントが書いたコードを、マージ前の性能チェックに通す流れを追います。
しきい値を超えて悪化したら CI ジョブを失敗させ、テレメトリを使った原因調査に進みます。
CI 側でこの流れをまとめて回すのが framedash run-profile-test です。
このコマンドは、自動セッション用の FRAMEDASH_* 環境変数を書き出し、指定したプロファイリング用ビルドを起動し、そのテレメトリが取り込まれるのを待ってから、ベースラインと perf-diff で比較します。
framedash run-profile-test \
--command "./Build/Game.exe -nullrhi -ExecCmds='Automation RunTest Perf'" \
--scenario nightly --api-key-file ci-read.key \
--baseline "$BASE_SHA" --threshold 5 --fail-on-regression
起動されたゲーム側では、自動テストの入口で自動セッション API を一度呼びます。
SDK が BeginAutomatedSessionFromEnvironment() を呼ぶと、run-profile-test が書き出した FRAMEDASH_BUILD_ID / FRAMEDASH_GIT_BRANCH / FRAMEDASH_GIT_COMMIT / FRAMEDASH_TEST_SCENARIO を読み取り、以後のイベントすべてに CI ビルドと、そのブランチ、コミット、シナリオが自動で乗ります。
イベントごとにタグ付けするコードは要りません。
TelemetrySDK.Instance.BeginAutomatedSessionFromEnvironment();
// ... プロファイリングのシナリオを実行 ...
TelemetrySDK.Instance.EndAutomatedSession();
運用上の注意が一つあります。
短いビルドやヘッドレス実行は、SDK の定期フラッシュより先に終了することがあります。
テレメトリが実際に送信されるまで、プロファイリング実行を終了させずに待ってください(各 SDK の CI とヘッドレス実行の手順に方法があります)。
そのうえで run-profile-test が取り込みを待ってから性能を比較します。
build_id はトップレベルのフィールドとして記録され、ブランチとコミットとシナリオは ci.branch / ci.commit / ci.scenario の属性に乗ります。
これで、エージェントによるコミットの build_id が、そのままビルド比較の候補になります。
API キーの使い分けには注意が必要です。
CI の比較処理は analytics:read 権限のキーでテレメトリを読み、起動されたゲームは別の events:write 権限のキーでテレメトリを送ります。
名前の衝突を避けるため、比較用のキーは FRAMEDASH_API_KEY 環境変数ではなく --api-key-file で渡し、FRAMEDASH_API_KEY はゲームの送信用キーとして空けておきます。
CI が性能低下を検出したら、どこを見るか
perf-diff によって CI ジョブが失敗したとき分かるのは、「どの指標が、どれだけ悪化したか」までです。
どこで悪化したのかは、この数字だけでは分かりません。
そこで使うのがパフォーマンスヒートマップです。 FPS、フレームタイム、GPU 時間、メモリ使用量を、ゲームマップ上にセル単位でオーバーレイして見せます。 デバイスやビルドプロファイルで絞り込めるので、性能が悪化したビルドのどのマップのどのあたりが重くなったのかを、マップ上の位置として特定できます。
マップ上の分布は、位置付きのイベントから描かれます。 プロファイリングのシナリオがプレイヤー位置を登録済みのマップ ID とともに送ると、SDK はそのイベントにカメラ向き(ヨーとピッチ)を自動で添えます。 これがあれば、「build 1042 の desert_ruins、北向きで見た一角で P95 が跳ねる」という粒度まで追えます。 性能低下は、再現可能な調査対象になります。
テレメトリをエージェントの原因調査に使う
ここまでの流れでも、ヒートマップを開いて原因を絞り、修正する工程は人が担っています。
この工程も、エージェントに渡せます。 Framedash の MCP サーバーは、テレメトリに対する読み取り専用のツールとリソースを LLM に公開します。 ヒートマップのグリッドデータ、ダッシュボードの KPI、生の SQL クエリまで、エージェントが自然言語の指示から直接引けます。
claude mcp add framedash \
-e FRAMEDASH_API_KEY=fd_xxx \
-e FRAMEDASH_PROJECT_ID=your-project-uuid \
-- npx -y @framedash/mcp-server
これで、変更を担当したエージェントに、性能が悪化した箇所の調査から修正まで続けさせる流れを組めます。
get_heatmap でマップ上の重い箇所を俯瞰し、query を build_id で絞って候補ビルドのホットスポットを切り出し、原因の見当を付けてコードを直す。
ツールは読み取り専用なので、エージェントがテレメトリを書き換えたり消したりすることはありません。
修正が性能低下を解消したかどうかは、次のビルドで同じ性能比較を行って客観的に確認します。
CI は性能低下を数値で検出し、エージェントは原因候補を調べます。 人間は、しきい値をどこに引くかを決め、修正内容が妥当かを最後に確認します。
始め方
必要なのは、ゲームに SDK を入れて build_id を CI から渡すことと、CI に性能比較を一つ足すことです。
SDK の統合は数行で済み、対応エンジンは Unity、UE5、Godot (C#) です。
- SDK と CI の具体的な設定は CI 連携プロファイリングにまとまっています。
- コマンドの一覧は CLI リファレンスを参照してください。
- エージェントに使わせるなら、Claude Code プラグインが MCP サーバーとスキルを一括で入れます。
claude plugin marketplace add crane-valley/framedash-claude-plugin
claude plugin install framedash@framedash