程序丨Unity3D性能優化最佳實踐(一):分析

2021-02-15 騰訊GWB遊戲無界

作者: Ian    

翻譯: Kelvin Lo / 海龜

說到優化,不得不說所有優化的源頭都是從發現問題開始,第一步是分析,根據項目技術和資源結構的分析報告結果來劃出項目問題的可能範圍。 


注意:本文裡使用的一些追蹤程序代碼是基於Unity 5.3版本,在未來這些功能有可能會變動。

注意:這個章節討論的原生(Native)方法(Method)名稱是由 Unity 5.3 的執行檔擷取出來的,這些方法名稱可能在未來的版本有所改變。

關於分析,有許多能幫助 Unity 開發者分析項目的工具,而 Unity 本身有一套內建工具像是 CPU Profiler、Memory Profiler、或是 5.3 才有的 Memory Analyzer。 

但是最好的分析報告通常來自於該平臺的特有工具,像是:

iOS:Instruments 和 Xcode Frame Debugger

Android:SnapdragonProfiler

採用 Intel CPU/GPU的平臺:VTune 和 Intel GPA

PS4:Razorsuite

Xbox:Pixtool

這些工具大部分都能夠分析IL2CPP所包出來的C++項目,這些原生程序能執行原本在Mono下無法執行的高精度計時(high-resolution method timings)和更透明的堆棧呼叫(callstacks)。

要充分利用這些工具通常需要在這些平臺上啟用 IL2CPP 然後使用轉換成 C++ 版本的項目。在原生程序代碼狀態下可以透過原生工具得到完整的呼叫堆棧還有高精度計時,這些在透過 Mono 執行的時候都無法取得。 

當查看啟動時間的軌跡時,有兩個關鍵的方法來檢視,分別是從項目設定、資源和程序可能影響啟動時間的可能範圍。

請注意:啟動時間在不同的平臺上會有差距,大多都是用啟動到顯示 Unity Logo(splash screen)的時間來判定。

注意 Unity 的啟動時間在不同平臺上可能表現會不同,大部分的平臺的行為是會讓用戶會在靜態的 Unity Logo(Splash screen)等待得時間變長。

上圖為 iOS 上用 Instruments 抓的啟動時間追蹤,可以看到 iOS 平臺特有的 startUnity 方法呼叫了 UnityInitApplicationGraphics 和 UnityLoadApplication 方法。

UnityInitApplicationGraphics 執行很多內部工作,像是圖形裝置的設定工作和初始化很多Unity的內部系統。不但如此,他還負責初始化 Unity 的資源系統(Resources system)。所以它需要先加載資源系統所含的所有檔案索引。 

所有目錄名為 Resources 裡面的資源文件(在 Assets 目錄下的 Resources 目錄,還有 Resources 目錄下的目錄)都會被算在資源系統裡。因此初始化資源系統所需要的時間將會隨著 Resources 目錄裡的檔案數量增加而變久。

UnityLoadApplication 的工作包含加載和初始化項目最開始的場景。這包含反串行化(deserializing)和實例化(Instantiating)展示第一個場景需要的數據,例如編譯著色器(compiling Shaders)、上傳貼圖和實例化遊戲對象(GameObject)等等所有為了顯示初始場景所需要的資料。此外,第一個場景中所有的 MonoBehaviours 都會在這個時間點執行 Awake 回呼(Callback)。

這樣的呼叫結構代表如果你在項目的第一個場景裡的某個 Awake 回呼執行了非常耗時的程序,會拖慢整個項目的啟動時間。要解決這樣問題當然是要修改拖慢的程序代碼或是移到別的地方來執行。

解析執行軌跡 啟動初始化之後主要是 PlayerLoop 的追蹤。這是 Unity 主要周期循環,裡面的程序每幀會執行一次(注一)

上圖是一個 Unity 5.4 項目的分析報告,顯示了幾個 PlayerLoop 會呼叫的最有趣的方法。請注意,PlayerLoop 裡方法的名稱可能會因不同版本的 Unity 而有所不同。

PlayerRender 是執行 Unity 渲染系統的方法。也負責剔除不顯示的對象、計算動態批次計算(Dynamic batches)和對 GPU 送出繪圖指令。所有的影像後制(Image Effects)或是基於渲染的程序回呼(例如 OnWillRenderObject)也都在此處理。一般來說,當項目跟玩家互動時 CPU 大部分時間都會花在這裡。

BaseBehaviourManager 會呼叫三種不同版本的 CommonUpdate,它們各自會呼叫場景裡被啟動(Active)的 GameObject 上的 MonoBehaviours 的特定回呼。

CommonUpdate<UpdateManager> 會呼叫 Update 回呼。

CommonUpdate<LateUpdateManager>會呼叫 LateUpdate 回呼。

CommonUpdate<FixedUpdateManager> 會呼叫 FixedUpdate(如果物理系統被觸發)

一般來說 BaseBehaviourManager::CommonUpdate<UpdateManager> 是最有意思的方法,因為它在 Unity 項目裡大多數腳本的進入點。

還有幾個有趣的方法:

如果項目有用到 Unity UI 系統,UI::CanvasManager 會呼叫幾個不同的回呼。這行為包含了Unity UI 的批次運算和排版更新,這兩個操作最常造成 CanvasManager 出現在分析報告裡。

DelayedCallManager::Update 執行共例程(Coroutines)。在底下的 Coroutines 章節會有更詳細的說明。

PhysicsManager::FixedUpdate 執行 PhysX 物理系統。涉及到執行 PhysX 的內部程序,並受到場景內有物理行為對象的數量影響(像是帶有 Rigidbody 和各種 Collider 的物件)。然而,跟物理有關的回呼也會出現在此,特別是 OnTriggerStay 和 OnCollisionStay。

假如項目用的是2D物理(Box2D),類似的結構會出現在 Physics2DManager::FixedUpdate 之下。

當使用 IL2CPP 跨平臺編譯時,可以找看看 ScriptingInvocation 這行包含的內容,這是從 Unity 內部原生程序進入到用戶腳本的分界點。(注二)

上圖是一個 Unity 5.4 項目的另一個分析報告,附掛在 RuntimeInvoker_Void 底下的所有方法都是交叉編譯過的 C# 腳本的一部分,每幀執行一次。

這些追蹤還蠻容易理解的,每一個命名規則都是「原始類別_原始方法」,例如上圖範例範例能找到 EventSystem.Update、PlayerShooting.Update 以及其他幾個 Update 方法。這些都是在MonoBehaviours 裡能找到的標準 Unity Update 回呼。

透過展開這些方法,可以更精確的定位誰在消耗 CPU 時間。這包含了項目的腳本方法、Unity API 和 C# 函式庫。

上面顯示出 StandaloneInputModule.Process 方法每幀會對整個 UI 進行一次 Raycasting,檢查是否有任何 UI 事件被點擊或滑過的動作觸發。主要消耗是在逐一檢查所有 UI 元素,並測試滑鼠的位置是否在其範圍內。(注三)

資源加載也能從 CPU 追蹤裡找到,加載資源的主要方法是SerializedFile::ReadObject,它透過名為「Transfer」的方法將檔案透過 2 進位(binary)的數據串流連接到 Unity 的串行化系統(Serialization system)。Transfer 方法可以在所有資源的加載過程中看到,例如材質、MonoBehaviours 和粒子系統。

上圖顯示出一個場景正被加載,這會讀取並反串行化(Deserialize)場景中所有資源,可以看到SerializedFile::ReadObject 之下有各種不同 Transfer 呼叫。

一般來說,如果在執行時遇到了效能問題,並追蹤看到 SerializedFile::ReadObject 用掉大量的時間,代表FPS會下降是因為資源正在加載。要注意的是絕大部分的情況下,只有當透過 SceneManager、Resources 或 AssetBundle API 請求同步(Synchronous)載入時,才會在主線程上看到 SerializedFile::ReadObject。

這種效能問題通常能簡單解決:可以採用異步方式(Asynchronous)加載資源(將比較吃重的 ReadObject 呼叫放到工作線程(Worker thread)上),或預先加載那些較大的資源。

請注意,當複製(Cloning)對象時也會產生 Transfer 呼叫(在追蹤表的CloneObject方法裡)。假如一個Transfer 呼叫出現在 CloneObject 下就代表它不是從硬碟加載而是從一個舊的對象數據轉移到一個新的對象。Unity 做法是對舊的對象進行串行化之後將產生的數據反串行化成為新的對象。

注一:這隻適用於在」Assets」底下的」Resources」目錄以及底下所有名為」Resources」的子目錄。

注二:技術上來說,執行IL2CPP之後,C#/JS腳本也會轉為原生程序(Native code)。然而,這種交叉編譯程序主要是透過IL2CPP的框架來執行,不會像手動寫程序那麼嚴謹。

注三:在Unity 5.4之前,StandaloneInputModule在沒有滑鼠的裝置上查詢滑鼠輸入存在著一些缺陷,還好後面的版本已經修復,大大降低了StandaloneInputModule的CPU消耗

相關焦點

  • 程序丨Unity3D性能優化最佳實踐(三):協程
    作者:Ian翻譯:Kelvin Lo / 海龜系列回顧:Unity3D性能優化最佳實踐
  • 程序丨Unity3D性能優化最佳實踐(二):內存
    作者:Ian  翻譯: Kelvin Lo / 海龜系列回顧:Unity3D性能優化最佳實踐(一):分析
  • 程序丨Unity3D性能優化最佳實踐(四):資源審查
    作者: Ian 翻譯: Kelvin Lo / 海龜系列回顧:Unity3D性能優化最佳實踐
  • Unity3d遊戲程序開發培訓靠譜嗎?好就業嗎?
    Unity的遊戲開發引擎如此受歡迎,而市場上Unity3d的遊戲程序開發者卻供不應求,這就吸引了一大批遊戲愛好者想學習Unity3d遊戲程序開發,甚至未來想轉行Unity3d遊戲程序開發。那麼學習Unity3d遊戲程序開發,肯定靠自學是完全不夠的,還得需要參加專業的Unity3d遊戲程序開發課程培訓學習,只有這樣才能更系統更全面地掌握Unity3d遊戲程序開發技術,你說是不是呢。
  • 了解CPI對分析程序性能的意義
    本小節講述為什麼使用 CPI 分析程序性能的意義。如果已經非常了解 CPI 對分析程序性能的意義,可以跳過本小節的閱讀。 1.1 程序怎麼樣才能跑得快 ? 理解什麼是 CPI,首先讓我們思考一個問題:在一個給定的處理器上,如何才能讓程序跑得更快呢?
  • etcd 的性能怎麼樣?需要優化嗎?
    再來看 Storage 層,磁碟 IO fdatasync 延遲會影響 etcd 性能,索引層鎖的 block 也會影響 etcd 的性能。除此之外,boltdb Tx 的鎖以及 boltdb 本身的性能也將大大影響 etcd 的性能。從其他方面來看,etcd 所在宿主機的內核參數和 grpc api 層的延遲,也將影響 etcd 的性能。
  • 前端性能優化之利用 Chrome Dev Tools 性能分析
    ,這篇文章,我將詳細說明怎樣利用 Chrome Dev Tools 進行頁面性能分析及性能報告數據如何解讀。這個是最常用的,一般大概分析頁面性能的時候都是點這個就夠了選擇要展示的性能記錄。你可能進行了多次分析,這裡可以切換去看每次的結果這裡,我以京東的一個頁面為例,勾選 disable cache,網絡情況為 Fast 3G,點擊2,然後就結果來說明一下,應該如何理解性能結果,找出優化點。
  • QQ音樂Android客戶端Web頁面通用性能優化實踐
    一、問題與目標作為一款注重於內容運營的應用程式,QQ 音樂 Android 客戶端的 Web 頁面日均 PV 達到千萬量級,評論頁、MV 頁等核心頁面均有 Web 頁面參與,或完全由 Web 實現。客戶端內 Web 頁面的打開耗時與 Native 頁面相距甚遠,需要系統性優化。
  • Golang程序性能分析(一)pprof和go-torch
    前言最近計劃用三篇文章講述一下Golang應用性能分析,本文是第一篇,先來介紹Go語言自帶的性能分析庫pprof怎麼使用,後面兩篇會講解怎麼用pprof對Echo或者Gin框架開發的應用進行性能分析以及如何使用pprof對gRPC 服務進行性能分析。
  • Android性能優化:帶你全面實現內存優化
    僅限今日,24小時後恢復原價若想直接學習課程,掃碼學習(以下內容為正文)作者丨Harvey_Specterhttps://www.jianshu.com/p/42516966a6ff前言在 Android開發中,性能優化策略十分重要
  • 幾乎是史上最全最實用的Android性能全面分析與優化方案研究
    藉助性能優化工具分析解決問題性能優化指標性能問題分類1、渲染問題: 過度繪製、布局冗雜2、內存問題: 內存浪費(內存管理)、內存洩漏3、功耗問題: 耗電性能優化原則和方法使用各種性能工具測試及快速定位問題(主要針對開發同學)。使用低配置的設備:同樣的程序,在低端配置的設備中,相同的問題會暴露得更為明顯。權衡利弊:在能夠保證產品穩定、按時完成需求的前提下去做優化。
  • Java性能優化指南(一)
    一直覺得偏向技術實踐類的東西,單純的聽和單純的講收穫都很有限,最好的做法是閱讀學習-理解-實踐-總結,這樣的方式。這一份原來是我在閱讀《Java性能優化權威指南》時候的閱讀筆記,最近整理後在這邊做一下分享。
  • 前端性能優化之利用 Chrome Dev Tools 進行頁面性能分析
    ,這篇文章,我將詳細說明怎樣利用 Chrome Dev Tools 進行頁面性能分析及性能報告數據如何解讀。這個是最常用的,一般大概分析頁面性能的時候都是點這個就夠了選擇要展示的性能記錄。你可能進行了多次分析,這裡可以切換去看每次的結果這裡,我以京東的一個頁面為例,勾選 disable cache,網絡情況為 Fast 3G,點擊2,然後就結果來說明一下,應該如何理解性能結果,找出優化點。
  • Java Web服務性能優化實踐
    就像當初 JAVA 大行其道的時候性能成為其致命詬病一樣,Web 服務也同樣面臨性能問題,似乎「性能問題」天生就是「平臺無關」揮之不去的冤家。但問題終歸要解決,實踐是檢驗和分析問題的唯一途徑,讓我們先來創建一個簡單的 Web 服務再來審視和分析隱含其中的性能問題。
  • 在K8S/OpenShift上開發應用程式的14種最佳實踐
    》和 《容器最佳實踐》有異曲同工之妙。我的目標是為開發人員提供指導和最佳實踐,以幫助他們成功地將應用程式部署到生產環境中。如果您是在K8S/OpenShift之上構建應用程式的開發人員,那麼您可能會對此博客感興趣。該博客包括兩類最佳實踐。第一類列出了提高應用程式可靠性的實踐,第二類包括了提高安全性的實踐。請注意,這兩個類別之間有些重疊。
  • 五大頂級CSS性能優化工具,值得一試!
    TestMyCSS是一款免費的在線優化工具,具有很多功能。它可用來檢查代碼冗餘,驗證錯誤,未使用的CSS和尋求最佳做法。  StyleLint是一款相當強大的CSS linter,它與PostCSS(一種開發工具)一起編寫了最先進的CSS,linter是一個可通過代碼捕獲潛在錯誤的程序。
  • A-Tune:基於AI的自優化能力,讓應用發揮極致性能
    困境一:參數對象龐大,業務複雜度高。  當前硬體和基礎軟體組成的應用環境涉及高達7000多個配置對象,隨著業務複雜度和調優對象的增加,調優所需的時間成本呈指數級增長,導致調優效率急劇下降,給用戶帶來巨大挑戰。
  • Unity3D遊戲開發如何優化性能?—— LOD技術
    1、Erenow —— 在此之前今天我們來說下Unity遊戲開發時,性能優化用到的一項簡單技術
  • Microsoft推出Azure架構完善的最佳實踐
    微軟從Amazon Web Services(AWS)一書的雲計算平臺上摘錄了一頁,介紹了自己的Azure架構完善的框架,提供了一組體系結構的最佳實踐,可幫助用戶構建和交付出色的解決方案並改善雲工作負載的質量。
  • 海量挑戰:騰訊雲ES可用性及性能優化實踐
    豐富的各語言SDK,方便快速的對服務質量分析平臺前後臺進行快速切換,實際從代碼修改到上線完成只用了一天的時間。 二、 高可用及性能方向的優化 1.