返回所有文章

用接近發佈的建置驗證最佳化:Unity、UE5 與 Godot 的效能量測流程

編輯器裡的影格時間改善了,玩家裝置上的遊戲卻未必更快。比較數字前,先確認它們來自什麼建置、什麼工作負載。

判斷發佈效能,應在目標硬體上執行接近正式發佈的建置。需要找出耗時原因時,改用具備診斷功能的建置;修正後,再回到接近發佈的條件驗證。本文把這種保留少量量測程式碼的建置稱為量測建置。這是專案可採用的約定,並非三個引擎共有的建置組態名稱。

根據問題選擇建置

編輯器效能分析適合在開發中找出耗時的腳本、資源處理和記憶體配置。具備診斷功能的獨立建置(如 Unity 或 Unreal Engine 的 Development 建置)能排除編輯器處理程序的負載,並在目標裝置上深入分析。但這些結果不能直接當作發佈建置的效能。

編輯器:調查 診斷建置:裝置分析 量測建置:驗證

記錄結果時附上建置組態。組態改變時,額外負擔與被測行為都可能改變。

編輯器視窗、資源處理及其他工具會共用裝置資源。診斷建置也可能包含額外程式碼和效能分析的負擔。差異取決於引擎、後端、平台與啟用的選項,沒有固定數值可以直接扣除。診斷建置中找到的高成本函式仍值得調查,只是需要回到玩家實際執行的建置確認影響。

改程式前,先固定執行條件

單靠建置設定無法保證比較可重現。先選一個情境,例如讀取存檔後沿固定路線移動攝影機,並為每次結果記錄以下條件:

  • 建置 ID、提交、引擎版本、腳本後端與建置組態
  • 裝置、GPU 驅動程式、解析度、畫質設定、VSync 與影格率上限
  • 場景、初始狀態、輸入或路線、量測時間或影格數
  • 暖機規則和快取狀態,尤其是著色器編譯與首次載入的處理方式
  • 啟用的量測程式碼、取樣間隔,以及缺漏或丟棄的樣本

先把未修改的基準多跑幾次,再測候選建置。如果基準之間的波動已接近預期改善幅度,應先檢查測試條件。重複執行有助於發現波動,但僅靠重複執行還無法證明結果在統計上可靠。手機與筆記型電腦還要保持相近的供電及溫度條件。

首次載入與暖機後的遊戲過程應分開測試。量測穩定狀態時,排除著色器編譯可能合理;若要調查首次啟動的卡頓,同樣的處理卻會把問題本身排除掉。

設定量測建置

Unreal Engine:引擎支援時使用 Test

Epic 將 Test 定義為保留部分主控台命令、統計與效能分析工具的 Shipping 組態。需要發佈等級的最佳化與這些診斷工具時,可以從 Test 開始。Development 也啟用了大部分最佳化,不能把它等同於未最佳化的 Debug 建置。

先檢查引擎安裝。Installed Build 預設包含 Shipping、Development 與 DebugGame。Test 通常需要從原始碼建置的引擎,或包含 Test 的自訂 Installed Build。在命令中選擇 Test,不會補上缺少的二進位檔。

支援 Test 的引擎可參考以下 Windows 封裝命令。替換專案路徑,並使用安裝目錄中的 Automation Tool 啟動腳本:

RunUAT.bat BuildCookRun -project="C:/Path/MyGame.uproject" -platform=Win64 -clientconfig=Test -cook -stage -pak -package -build

在實際產生的套件中確認所需命令和計數器是否可用。不要假設 UE_LOG 一定存在:Test 和 Shipping 預設會在編譯時移除日誌,變更該設定可能需要重新建置引擎。量測程式碼應限於專案中必要的位置。如果只能用 Development 調查,最後仍應使用專案支援的量測方式在 Shipping 中驗證。

Unity:後端與編譯設定和發佈版本一致

在 Unity 6 建立專用的建置設定檔,進行接近發佈環境的比較時關閉 Development Build。產品使用 IL2CPP,就測 IL2CPP;使用 Mono,就保留 Mono。IL2CPP 的 Release 或 Master 組態也應與產品一致。比較程式修改時同時更換後端,會引入另一個變因。

為 Unity Profiler 保留獨立的 Development 設定檔。連接 Profiler 與 Deep Profiling 是診斷選項,不應無意間留在發佈效能比較中。Unity 6 之前的版本可用 BuildPipeline 腳本維持同樣的區分。

可使用設定檔專屬的腳本定義符號,讓診斷標記只在量測建置中生效:

using System.Diagnostics;

public static class Measure
{
    [Conditional("MEASUREMENT")]
    public static void Mark(string label)
    {
        UnityEngine.Debug.Log($"[measure] {label} @ {UnityEngine.Time.frameCount}");
    }
}

在設定檔的 Scripting Defines 中加入 MEASUREMENT,再於測試情境的開始、結束等位置呼叫 Measure.Mark。這是日誌標記,不是影格時間收集器。不要每個影格都呼叫:字串格式化和日誌寫入也有成本。沒有該符號時,[Conditional] 會移除呼叫位置,但不會從組件中移除方法本體。

Godot:發佈匯出加量測功能標籤

複製匯出預設,關閉 Export With Debug。算繪器、平台與匯出範本應和預計發佈的版本一致。僅為建立量測預設,無需編譯自訂引擎範本。

在預設中加入 measurement 等自訂功能:

custom_features="measurement"

C# 專案可在測試情境開始處讀取標籤:

if (OS.HasFeature("measurement"))
{
    GD.Print($"[measure] scenario-start frame={Engine.GetFramesDrawn()}");
}

自訂功能標籤只對匯出的專案生效,一般的編輯器執行不會使用它們。請檢查匯出的執行檔。和 Unity 範例一樣,這段程式碼僅標記情境中的位置,指標需要另外收集;收集成本與設定也應在各次執行中保持一致。

依實際取樣粒度判讀指標

60 FPS 的影格預算約為 16.67 ms,30 FPS 約為 33.33 ms。除了平均 FPS,也要檢查影格時間分布。P95 是 95% 的觀測值不超過的數值。更罕見的嚴重卡頓仍可能被隱藏,因此還應視需要查看更高百分位、超出影格預算的次數與時間軸。

先確認統計對象。定期上傳的遙測樣本算出的 P95,是這些樣本的 P95,不一定代表每一個算繪影格。比較兩份報告前,應記錄取樣方式。

影格時間記錄量測來源與時間區間,同時檢查典型影格和耗時較長的影格。
記憶體註明計數器與單位,觀察峰值及反覆載入、卸載的變化。數值較高不代表發生洩漏。
載入時間固定開始與結束的定義,首次載入和暖快取載入分開比較。
儲存 I/O將讀取與等待對照影格時間軸。傳輸量本身無法解釋卡頓原因。

若問題與位置有關,附上場景、座標及攝影機方向。「站在門口、朝向物件密集的房間時出現慢影格」比整段工作階段的平均值更有助於重現,但還不能確定是哪個物件或系統造成了成本。

找到原因後,重跑原來的測試

依記錄的條件重現問題,再用 Unity Profiler、Memory Profiler、Unreal Insights 或 Godot 的效能分析器調查。Godot 內建腳本效能分析器適用於 GDScript;深入分析 C# 程式碼時,需要合適的 .NET 效能分析器。

修改一個疑似成本來源後,在量測建置中重跑原來的情境。確認多次執行仍有改善,並檢查記憶體、載入或影格內的其他工作是否惡化。診斷追蹤變快只是中間結果。

同樣的資料也能協助判斷是否增加特效或物件。但算繪執行緒有餘裕,不代表 GPU 時間和記憶體也有餘裕。加入內容後,仍需在相同的目標裝置條件下量測。

用 Framedash 保留可檢查的比較結果

Framedash 的儀表板熱圖可在地圖上查看已收集的效能樣本,並依建置、平台和已記錄的屬性篩選。各引擎的 SDK 設定與可用收集器不同,請參閱 Unity、UE5 及 Godot C#。使用圖表前,先確認目標建置實際傳送了哪些指標。

Unity 使用者可從效能執行比較試行功能開始:在自己的 PC 上收集基準、未修改的重複執行與候選執行,再檢查條件和影格間隔摘要,不需要地圖。這些間隔量的是 SDK Update 回呼之間的時間,不是 GPU 完成或畫面呈現時間。試行功能會說明證據是否可比較,不會自動判定效能回歸合格與否。

先選一個可重現的情境與一個待驗證的修改。把基準結果、執行條件和候選結果放在一起,下次才能繼續回答同一個問題。