返回所有文章

用 CI 自動找出 AI 代理所寫遊戲程式碼的效能下降

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 失敗,再進入原因調查。

在 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 就直接成了建置比較的候選。

只有金鑰的區分要留意。CI 的比較程序用 analytics:read 金鑰讀取遙測,被啟動的遊戲則用另一把 events:write 收集金鑰傳送遙測。為避免名稱衝突,比較用的金鑰透過 --api-key-file 傳入,而不是使用 FRAMEDASH_API_KEY 環境變數,讓 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 俯瞰地圖上變重的地方,用按 build_id 過濾的 query 把候選建置的熱點單獨摘出來,形成對原因的判斷,然後改程式碼。工具是唯讀的,所以代理不會改寫或刪除你的遙測。修正有沒有消除效能下降,會由下一個建置中的同一項 CI 效能比較再次確認。

CI 效能比較偵測用同一閾值確認是否比基準更差
熱區圖與 MCP調查由代理調查效能在哪裡變差

CI 根據數字偵測效能下降,代理調查可能的原因。人負責設定閾值,並在最後確認修正方式是否妥當。

如何開始

你需要做的,只是給遊戲接入 SDK、從 CI 傳入 build_id,再往管線裡加一項效能比較。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
程式碼生成再快,也不要把效能檢查落在後面 支援的引擎是 Unity、UE5、Godot (C#)。無需信用卡即可開始。
免費開始 UnityUE5Godot (C#)