性能分析

原文鏈接:https://docs.unity3d.com/Manual/BestPracticeUnderstandingPerformanceInUnity1.html

? ? ? ? 當討論性能時,至關(guān)重要的是知道所有的優(yōu)化嘗試必須從發(fā)現(xiàn)過程開始。剖析一個應(yīng)用程序的性能來發(fā)現(xiàn)其性能熱點是必要的第一步,然后針對項目的技術(shù)和資源的架構(gòu)對性能測試的結(jié)果進行分析。

? ? ? ? 請注意:本章在底層代碼分析追蹤中發(fā)現(xiàn)的函數(shù)名是從Unity5.3中得到的,方法名也許會在未來的版本中更改。


工具

? ? ? ? 為了進行性能分析,Unity開發(fā)者可以使用許多不同的工具。Unity有一批內(nèi)置的工具,比如說CPU分析器、內(nèi)存分析器和5.3中新的內(nèi)存分析器。

? ? ? ? ?然而,最好是從平臺特定的工具中生成數(shù)據(jù)。它們包括:

? ? ? ? ·對于iOS平臺:Instruments和XCode Frame Debugger

? ? ? ? ·對于Android平臺:Snapdragon Profiler

? ? ? ? ·對于運行Intel CPU/GPU的平臺:VTune和Intel GPA

? ? ? ? ·對于PS4平臺:Razor suite

? ? ? ? ·對于Xbox平臺:Pix tool

? ? ? ? 這些工具通常在那些可以利用IL2CPP來產(chǎn)生一個C++版本的項目中最有用處。這些底層代碼提供在Mono下運行實現(xiàn)不了的清晰的調(diào)用棧和高精度的方法計時。

? ? ? ? Unity已經(jīng)創(chuàng)建了一個使用Instruments來分析IOS游戲的基礎(chǔ)指導(dǎo),請看使用Instruments分析性能(鏈接見原網(wǎng)頁)。


剖析啟動數(shù)據(jù)

? ? ? ? 在查看啟動時間的數(shù)據(jù)時,有兩個關(guān)鍵方法需要檢查。這兩個方法是配置、資源和代碼這些可能影響啟動時間的主要的地方。

? ? ? ? 請注意在不同平臺上啟動時間清單不同,在大多數(shù)平臺上,它是作為一個靜態(tài)的閃動屏幕對使用者可見。

(圖片見原網(wǎng)頁)

? ? ? ? 上面的截圖是在一臺IOS設(shè)備上運行的示例項目的Instruments的數(shù)據(jù)。在平臺特定的startUnity函數(shù)中,請注意UnityInitApplicationGraphics和UnityLoadApplication函數(shù)。

? ? ? ? UnityInitApplicationGraphics執(zhí)行大量的內(nèi)部工作,比如說建立圖形設(shè)備以及初始化許多Unity的內(nèi)部系統(tǒng)。另外,它初始化Resources系統(tǒng)。為了完成這個任務(wù),它必須加載Resources系統(tǒng)中包含的文件的索引。

? ? ? ? 每個在名字叫“Resources”(請注意,這只應(yīng)用于在項目Assets文件夾下名字叫“Resources”的文件夾,也包括所有那些“Resources”文件夾下的子文件夾)文件夾下的資源文件都包含在Resources系統(tǒng)數(shù)據(jù)內(nèi)。因此,初始化Resources系統(tǒng)所需的時間與“Resources”文件夾下的文件數(shù)量至少呈遞增的線性相關(guān)。

? ? ? ? UnityLoadApplication包含加載和初始化項目中的第一個場景的函數(shù)。這包含反序列化和實例化所有顯示第一個場景所必須的數(shù)據(jù),比如編譯shader,上傳紋理和實例化游戲物體。此外,此時也執(zhí)行所有第一個場景中的MonoBehaviour的Awake()回調(diào)函數(shù)。

? ? ? ? 這些流程意味著如果有任何長時間運行的代碼在一個項目第一個場景的Awake回調(diào)函數(shù)中,那么這個代碼對拖慢此項目的初始啟動時間負有責任。要解決這個問題要么牽扯到消除這些執(zhí)行慢的代碼,要么就在應(yīng)用程序生命周期函數(shù)的其他地方執(zhí)行這些代碼。


剖析運行時數(shù)據(jù)

? ? ? ? 對于在初始化啟動時間之后分析運行數(shù)據(jù)快照,最主要的關(guān)注點是PlayerLoop函數(shù),這是Unity的主要循環(huán),其中的代碼每幀運行一次。

(圖片見原網(wǎng)頁)

? ? ? ? 上面的截圖來自于運行在Unity5.4示例工程的性能分析工具,舉例說明一些PlayerLoop中一些最應(yīng)該注意的函數(shù)。請注意在不同Unity版本的PlayerLoop中的函數(shù)名也許會不一樣。

? ? ? ? PlayerRender是運行Unity渲染系統(tǒng)的函數(shù)。這包含剔除對象,計算動態(tài)批處理,并且向GPU提交繪制指令。任何圖像效果或者基于渲染的腳本回調(diào)(例如OnWillRenderObject)都在這里運行。通常,在項目交互時,它應(yīng)該是CPU時間的最大消費者。

? ? ? ? BaseBehaviourManager調(diào)用CommonUpdate的三個模板化的版本。其執(zhí)行當前場景中激活的游戲物體上掛載的MonoBehaviour中的某些回調(diào)函數(shù)。

? ? ? ? ·CommonUpdate<UpdateManager>調(diào)用Update回調(diào)

? ? ? ? ·CommonUpdate<LateUpdateManager>調(diào)用LateUpdate回調(diào)

? ? ? ? ·CommonUpdate<FixedUpdateManager>如果物理系統(tǒng)被使用,則調(diào)用FixedUpdate回調(diào)

? ? ? ? 總體來說,BaseBehaviourManager::CommonUpdate<UpdateManager>是最值得關(guān)注的函數(shù)組,因為它是運行在一個Unity項目內(nèi)大多數(shù)腳本代碼的入口點。

? ? ? ? 還有其他的一些函數(shù)值得關(guān)注:

? ? ? ? UI::CanvasManager,如果一個項目使用UGUI它會執(zhí)行一些不同的回調(diào)。這包括UGUI的batch計算和布局更新,這兩個操作總會使CanvasManager出現(xiàn)在profiler中。

? ? ? ? DelayedCallManager::Update,運行協(xié)程。這在本文檔的協(xié)程章節(jié)中有詳細的描述。

? ? ? ? PhysicsManager::FixedUpdate,運行PhysX物理系統(tǒng)。這主要是包含運行PhysX的內(nèi)部代碼,并且被當前場景中物理對象的數(shù)量所影響,比如說剛體與碰撞體。并且,基于物理的回調(diào)也經(jīng)常出現(xiàn)在這里-特別是OnTriggerStay和OnCollisionStay。

? ? ? ? 如果項目使用2D物理,那么在Physics2DManager::FixedUpdate下會出現(xiàn)類似的一組調(diào)用。


剖析一個腳本的函數(shù)

? ? ? ? 當一個腳本在IL2CPP交叉編譯的平臺上執(zhí)行時,查找包含一個ScriptingInvocation對象的數(shù)據(jù)行。這是Unity內(nèi)部的底層代碼為了執(zhí)行腳本代碼轉(zhuǎn)換到腳本運行時的點(請注意,從技術(shù)上講,在被通過IL2CPP運行過之后,C#/JS腳本代碼也會變成底層代碼。然而,這種交叉編譯代碼主要通過IL2CPP運行框架執(zhí)行函數(shù),而且從嚴格意義上來講也不想手寫C++那樣)。

(圖片見原網(wǎng)頁)

? ? ? ? 上面的截圖是從一個運行在Unity5.4的示例項目的其他一些數(shù)據(jù)中截取的。所有嵌套在RuntimeInvoker_Void這一行下的函數(shù)都是交叉編譯的C#腳本的一部分,它們被每幀執(zhí)行一次。

? ? ? ? 這些一行行的追蹤數(shù)據(jù)相當容易閱讀,每一個前面都是原始的類名后面加上下劃線跟上原始的函數(shù)名。在這個示例追蹤數(shù)據(jù)中,可以看到EventSystem.Update,PlayerShooting.Update和其他一些Update函數(shù)。這些是MonoBehaviour中標準的Unity的Update回調(diào)。

? ? ? ? 通過展開這些函數(shù),可以在它們中間準確的知道每個函數(shù)所花的CPU時間。這也包括項目中其他腳本的函數(shù),Unity的API以及C#庫中的代碼。

? ? ? ? 上面的追蹤數(shù)據(jù)顯示StandaloneInputModule.Process函數(shù)每幀向全部UI發(fā)射一次射線,以檢測是否有任何觸摸事件發(fā)生在任何激活的UI元素上。主要的消耗是迭代所有的UI元素,并且測試鼠標的位置是否在它們的矩形邊界內(nèi)。


資源加載

? ? ? ? 資源加載也可以在CPU跟蹤數(shù)據(jù)中找到,表明加載資源的主要函數(shù)是:SerializedFile::ReadObject。這個函數(shù)通過一個名字叫Transfer的函數(shù)將一個文件的二進制數(shù)據(jù)流與Unity的序列化系統(tǒng)相連接。Transfer函數(shù)可以在所有的資源類型中找到,比如說紋理,MonoBehaviour和粒子系統(tǒng)。

(圖片見原網(wǎng)頁)

? ? ? ? 在上面的截圖中,一個場景被加載。這需要Unity讀取并反序列化場景中的所有資源,在SerializedFile::ReadObject之下顯示著被調(diào)用的各種Transfer函數(shù)。

? ? ? ? 一般來講,如果一次性能卡頓被觀測到發(fā)生在運行時,并且性能追蹤數(shù)據(jù)顯示大量的時間花在了SerializedFile::ReadObject上,那么就是由于資源加載原因?qū)е碌膸氏陆?。請注意,在大多?shù)情況下,只有當使用SceneManager, Resources或是AssetBundle的API時同步資源時SerializedFile::ReadObject才會在主線程中出現(xiàn)。

? ? ? ? 這種性能卡頓可以常規(guī)的方式解決:您可以異步加載資源(移動大型的ReadObject到工作線程),或是可以預(yù)加載某些大型的資源。

? ? ? ? 請注意當克隆對象時Transfer也會發(fā)生調(diào)用(顯示在追蹤數(shù)據(jù)的CloneObject函數(shù)中)。如果在CloneObject下出現(xiàn)Transfer調(diào)用,那么資源不會從硬盤中加載。而是從舊的對象數(shù)據(jù)傳輸?shù)叫碌膶ο?。為此,Unity序列化舊的對象并且將結(jié)果數(shù)據(jù)反序列化成一個新的對象。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容