關於 Unity Profiler 的使用以及代碼建議

1.參數說明

A. WaitForTargetFPS:

Vsync(垂直同步)功能鎖,即顯示當前幀的CPU等待時間 。

該參數一般出現在 CPU開銷過低,且通過設定了目標幀率的情況下(Application.targetFrameRate)。當上一幀低於目標幀率時,將會在本幀產生一個WaitForTargetFPS的空閒等待耗時,以維持目標幀率。該項在Unity引擎的主循環中其實是最早執行的,即引擎實際上是根據上一幀的CPU耗時,在當前幀中通過增補WaitForTargetFPS的方式來將運行FPS維持到目標值。比如,目標幀率爲30幀/秒,上一幀耗時15ms,那麼當前幀中WaitForTargetFPS將會是18(33-15)ms,但是這一幀中其他耗時爲28ms,那麼在Profiler中這一幀的總耗時就變成了46(18+28)ms。

B. Overhead:

Profiler總體時間-所有單項的記錄時間總和。用於記錄尚不明確的時間消耗,以幫助進一步完善Profiler的統計。

C. Physics.Simulate:

當前幀物理模擬的CPU佔用時間。

D. Camera.Render:

相機渲染準備工作的CPU佔用量

E. RenderTexture.SetActive:

設置RenderTexture操作.
底層實現:

  • 比對當前幀與前一幀的ColorSurface和DepthSurface.
  • 如果這兩個Buffer一致則不生成新的RT,否則則生成新的RT,並設置與之相對應的Viewport和空間轉換矩陣.

F. Monobehaviour.OnMouse_ :

用於檢測鼠標的輸入消息接收和反饋,主要包括:SendMouseEvents和DoSendMouseEvents。(只要Edtor開起來,這個就會存在)

G. HandleUtility.SetViewInfo:

僅用於Editor中,作用是將GUI和Editor中的顯示看起來與發佈版本的顯示一致。

H. GUI.Repaint:

GUI的重繪(說明在有使用原生的OnGUI)

I. Event.Internal_MakeMasterEventCurrent:

負責GUI的消息傳送

J. Cleanup Unused Cached Data:

清空無用的緩存數據,主要包括RenderBuffer的垃圾回收和TextRendering的垃圾回收。

  • RenderTexture.GarbageCollectTemporary:存在於RenderBuffer的垃圾回收中,清除臨時的FreeTexture.
  • TextRendering.Cleanup:TextMesh的垃圾回收操作

K. Application.Integrate Assets in Background:

遍歷預加載的線程隊列並完成加載,同時,完成紋理的加載、Substance的Update等.

L. Application.LoadLevelAsync Integrate:

加載場景的CPU佔用,通常如果此項時間長的話70%的可能是Texture過長導致.

M. UnloadScene:

卸載場景中的GameObjects、Component和GameManager,一般用在切換場景時.

N. CollectGameObjectObjects:

執行上面M項的同時,會將場景中的GameObject和Component聚集到一個Array中.然後執行下面的Destroy.

O. Destroy:

刪除GameObject和Component的CPU佔用.

P. AssetBundle.LoadAsync Integrate:

多線程加載AwakeQueue中的內容,即多線程執行資源的AwakeFromLoad函數.

Q. Loading.AwakeFromLoad:

在資源被加載後調用,對每種資源進行與其對應用處理.

2. CPU Usage

A. Device.Present:

Device.PresentFrame的耗時顯示,該選項出現在發佈版本中.

B. Gfx.WaitForPresent &&Graphics.PresentAndSync:

GPU上的顯示和垂直同步耗時.該選項出現在發佈版本中. 是CPU和GPU之間的垂直同步(VSync)導致的,之所以會有兩種參數,主要是與項目是否開啓多線程渲染有關。當項目開啓多線程渲染時,你看到的則是Gfx.WaitForPresent;當項目未開啓多線程渲染時,看到的則是Graphics.PresentAndSync。

造成這兩個參數的CPU佔用較高的原因主要有以下三種原因:

  • CPU開銷非常低,所以CPU在等待GPU完成渲染工作或等待VSync的到來;
  • CPU開銷很高,使Present錯過了當前幀的VSync,即不得不等待下一次VSync的到來;
  • GPU開銷很高,CPU的Present需要等待GPU上一幀渲染工作的完成。

C. Mesh.DrawVBO:

GPU中關於Mesh的Vertex Buffer Object的渲染耗時.

D. Shader.Parse:

資源加入後引擎對Shader的解析過程.

E. Shader.CreateGPUProgram:

根據當前設備支持的圖形庫來建立GPU工程.

3. Memory Profiler

A. Used Total:

當前幀的Unity內存、Mono內存、GfxDriver內存、Profiler內存的總和.

B. Reserved Total:

系統在當前幀的申請內存.

C. Total System Memory Usage:

當前幀的虛擬內存使用量.(通常是我們當前使用內存的1.5~3倍)

D. GameObjects in Scene:

當前幀場景中的GameObject數量.

E. Total Objects in Scene:

當前幀場景中的Object數量(除GameObject外,還有Component等).

F. Total Object Count:

Object數據 + Asset數量.

4. Detail Memory Profiler

A. Assets:

Texture2d:記錄當前幀內存中所使用的紋理資源情況,包括各種GameObject的紋理、天空盒紋理以及場景中所用的Lightmap資源.

B. Scene Memory:

記錄當前場景中各個方面的內存佔用情況,包括GameObject、所用資源、各種組件以及GameManager等(天般情況通過AssetBundle加載的不會顯示在這裏).

5. Other:

ManagedHeap.UseSize:代碼在運行時造成的堆內存分配,表示上次GC到目前爲止所分配的堆內存量.
SerializedFile(3):
WebStream:這個是由WWW來進行加載的內存佔用.
System.ExecutableAndDlls:不同平臺和不同硬件得到的值會不一樣。

5. 優化重點

A. CPU-GC Allow:

關注原則:

  • 檢測任何一次性內存分配大於2KB的選項
  • 檢測每幀都具有20B以上內存分配的選項.

B. Time ms:

記錄遊戲運行時每幀CPU佔用(特別注意佔用5ms以上的).

C. Memory Profiler-Other:

  1. ManagedHeap.UsedSize: 移動遊戲建議不要超過20MB.
  2. SerializedFile: 通過異步加載(LoadFromCache、WWW等)的時候留下的序列化文件,可監視是否被卸載.
  3. WebStream: 通過異步WWW下載的資源文件在內存中的解壓版本,比SerializedFile大幾倍或幾十倍,重點監視.

D. Memory Profiler-Assets:

  1. Texture2D: 重點檢查是否有重複資源和超大Memory是否需要壓縮等.
  2. AnimationClip: 重點檢查是否有重複資源.
  3. Mesh: 重點檢查是否有重複資源.

6. 項目中可能遇到的問題

A. Device.Present:

  1. GPU的presentdevice確實非常耗時,一般出現在使用了非常複雜的shader.
  2. GPU運行的非常快,而由於Vsync的原因,使得它需要等待較長的時間.
  3. 同樣是Vsync的原因,但其他線程非常耗時,所以導致該等待時間很長,比如:過量AssetBundle加載時容易出現該問題.
  4. Shader.CreateGPUProgram:Shader在runtime階段(非預加載)會出現卡頓(華爲K3V2芯片).

B. StackTraceUtility.PostprocessStacktrace()StackTraceUtility.ExtractStackTrace():

  1. 一般是由Debug.Log或類似API造成.
  2. 遊戲發佈後需將Debug API進行屏蔽.

C. Overhead:

  1. 一般情況爲Vsync所致.
  2. 通常出現在Android設備上.

D. GC.Collect:

原因:

  1. 代碼分配內存過量(惡性的)
  2. 一定時間間隔由系統調用(良性的).

佔用時間:

  1. 與現有Garbage size相關
  2. 與剩餘內存使用顆粒相關(比如場景物件過多,利用率低的情況下,GC釋放後需要做內存重排)

E. GarbageCollectAssetsProfile:

  1. 引擎在執行UnloadUnusedAssets操作(該操作是比較耗時的,建議在切場景的時候進行).
  2. 儘可能地避免使用Unity內建GUI,避免GUI.Repaint過渡GC Allow.
  3. if(other.tag == GearParent.MogoPlayerTag)改爲other.CompareTag(GearParent.MogoPlayerTag).因爲other.tag爲產生180B的GC Allow.

F. 少用foreach,因爲每次foreach爲產生一個enumerator(約16B的內存分配),儘量改爲for. 但如果是泛型字典集合的話直接採用foreach遍歷。鏈表直接用for

G. Lambda表達式,使用不當會產生內存泄漏.

H. 儘量少用LINQ

  1. 部分功能無法在某些平臺使用.
  2. 會分配大量GC Allow.

I. 控制StartCoroutine的次數:

  1. 開啓一個Coroutine(協程),至少分配37B的內存.
  2. Coroutine類的實例 – 21B.
  3. Enumerator16B.

J. 使用StringBuilder替代字符串直接連接.

K. 緩存組件:

  1. 每次GetComponent均會分配一定的GC Allow.
  2. 每次Object.name都會分配39B的堆內存.
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章