優化工作向來是個複雜系統的工程,且不說前期的框架預設、大量的版本迭代、美術和性能的需求平衡,單從每個版本的上萬幀測試數據中定位真正的性能瓶頸,其工作量就已可想而知。

無論項目是否複雜或簡單,當我們開展優化工作前,這些問題都需在胸中有丘壑。那麼如何井然有序地定位和排查性能問題呢?我們定位到了性能的真正瓶頸了嗎?今天小編以一個優化前期的MOBA手遊的UWA性能測評報告爲例,深入分析該項目在真機上的運行情況,並講解如何通過報告中的功能模塊更高效地定位性能瓶頸,希望能對大家的項目有所參考。

一、關於性能簡報

在這次優化講解的時候,我們將從報告的“性能簡報”模塊入手(每個項目性能報告的首頁),該模塊作爲UWA性能評測報告中的一大模塊,將引擎性能模塊的數據更有序、更核心地彙總成一個完整的優化體系,建議大家在優化項目之初先仔細參閱該模塊的數據。點擊這裏可以快速瞭解UWA性能簡報。

在性能簡報的第一個模塊,可以直觀查看到最近測試的項目報告中的重要參數、其優化效果和性能變化趨勢。

從性能排名的模塊中,我們提供了測評項目在國內數千款測評項目中,同設備和同類型的橫向排名,從而幫助大家更客觀地發現項目中的優勢和不足。

引擎模塊的性能排名

內存模塊的性能排名

UWA解讀:該項目在CPU模塊和內存上均有大量的提升空間。CPU模塊的壓力主要來自於引擎的渲染和UI,物理、粒子和動畫,邏輯代碼較爲嚴峻。內存模塊中各個資源的佔用內存普遍都較高,Mono堆內存分配也較大。

在性能簡報的最下方,是UWA針對本次性能提測所提供的分析與建議,在這裏將所有引起以上參數開銷的性能問題逐一列出,並通過精準的頁面鏈接跳轉定位到問題源頭,協助開發者進行有效地排查。其中,標紅部分的問題較爲明顯,建議研發團隊優先處理。

優化任務隊列是我們提供給用戶的性能優化切入點,下面我們將逐一分析上圖中羅列的問題。

二、優化隊列詳剖析之內存篇

2.1 內存佔用

本次報告的內存佔用峯值爲660.3MB,高於行業同類型同設備,當前行業均值爲305MB,因此需要研發團隊密切關注。特別的,如果要保證在一些1GB內存的設備比如iPhone 6或6 Plus上不閃退,我們建議儘量不要超過280MB。

內存的主要構成來自資源內存和Mono堆內存,在此我們做下詳細分析。

2.1.1 資源內存之紋理 (內存超標)

排查方向:

1) 紋理的分辨率、格式

如性能簡報中顯示,RGBA32和ARGB32都不是Android原生支持的格式,不但內存佔用較大,且加載效率也低下,建議嘗試轉成ETC1等硬件支持的格式。如果效果不滿意也可以嘗試通過Dither算法,對某些高精度的紋理進行處理,轉成RGBA4444或RGB565格式,以降低內存佔用。

補充說明:在圖表中我們也看到不少N/A資源:一般是new出來沒有命名的紋理資源,建議在這些紋理new出來之後,給.name賦值,以方便進行紋理資源管理。2)在查看紋理的具體使用情況時,我們同時發現其冗餘情況較爲嚴重。下圖爲按峯值倒序排序的資源列表截圖,可以看到數量峯值大於1的紋理有100多個,當兩張紋理的名字、尺寸、格式等屬性都相同,UWA就會將其視爲具有高風險冗餘的資源。

關於冗餘的排查優化方法:

a) 檢查AssetBundle(即打包的時候就有冗餘)中是否有冗餘,該問題可通過AseetBundle資源檢測服務來定位;

b) 檢查AssetBundle重複加載情況,是否有AseetBundle反覆加載導致的緩存漏洞。

2.1.2 資源內存之網格 (內存超標)

排查方向:

1)表格中頂點數大於5000的屬於超大網格,需要進一步排查是否有必要。

按Vertex數量進行排序,可以看到第一頁的網格頂點數都在1w左右,這個會造成一定程度的內存壓力。

2)上圖中也可發現,大量網格含有Tangent屬性,Tangent屬性一般爲導入引擎時生成,會增大網格的內存佔用,如果實際上不需要Tangent屬性,可在Inspector面板中修改設置,關閉Tangent導入選項。

列表中顯示Tangent數量爲-1的網格,表示Read/Write選項被關閉了,所以統計不到。在此,UWA建議研發團隊提交一個臨時的打開該選項的版本,來檢查網格的屬性是否合理。

3)存在冗餘的問題,解決方法同紋理

另外,我們在排查資源清單的時候發現了大量的內置資源,在這裏提醒大家關於內置資源的處理方法可參考這篇文檔:零冗餘解決方案(其他類型的資源可借鑑)

2.1.3 動畫資源(內存超標)

根據性能簡報的提示頁面轉到動畫文件的使用信息列表,如下圖所示:

對於大於200KB且時長較短的動畫資源,UWA認爲可以進一步排查。這裏看到資源列表按照內存佔用排序,前3頁內存佔用大小都超過了200KB,建議使用一些精度處理的方法來進行優化:通過降低浮點數精度的方法來減小動畫文件的大小。需要注意的是,這種方案會導致動畫精度一定程度上的損失,需要大家衡量下結果。

2.1.4 粒子系統內存 (數量和內存超標)

排查方向:

  1. Active的粒子系統數量過多,UWA建議在中低端設備上不超過50個,可以考慮通過一些設備分級的策略做改善(剔除某些在部分機型上表現力較差的粒子系統)
  2. 緩存的數量過大(UWA建議不超過600個),如下圖中實際粒子的使用走勢圖所示,藍線表示內存中的粒子系統數量,紫線表示實際上Active的量,從而得知大量粒子系統始終沒有Active過(表格中顯示Active數量峯值爲0),但在運行時都被加載了。

  1. 在下圖中,對於始終未被Active的粒子系統,我們建議研發團隊針對配表優化。

2.1.5 材質(數量超標)

對於Material資源的把控主要在於數量而非內存,UWA建議材質數量控制在300以內。

排查方向:冗餘

(1)對於Instance類型的冗餘Material資源,兩種優化方法:

1)嘗試通過《使用MaterialPropertyBlock來替換Material屬性操作》方法對其進行優化;

2)當我們修改了一些特定GameObject的資源屬性時,引擎會爲該GameObject自動實例化一份資源供其使用,比如Material、Mesh等。以Material爲例,我們在研發時經常會有這樣的做法:在角色被攻擊時,改變其Material中的屬性來得到特定的受擊效果。這種做法則會導致引擎爲特定的GameObject重新實例化一個Material,後綴會加上(Instance)字樣。其本身沒有特別大的問題,但是當有改變Material屬性需求的GameObject越來越多時,其內存中的冗餘數量則會大量增長。一般情況下,資源屬性的改變情況都是固定的,並非隨機出現。比如,假設GameObject受到攻擊時,其Material屬性改變隨攻擊類型的不同而有三種不同的參數設置。那麼,對於這種需求,我們建議直接製作三種不同的Material,在Runtime情況下通過代碼直接替換對應GameObject的Material,而非改變其Material的屬性。這樣,成百上千的Instance Material在內存中消失了,取而代之的則是這三個不同的Material資源。

(2)對於非Instance類型的冗餘Material,建議研發團隊通過資源檢測服務來查看AssetBundle中是否存在冗餘資源。

以上是主流資源的使用情況和分析建議,下面我們再關注下報告中檢測到的其他資源的使用情況:

2.1.6 字體(內存超標)

主要問題:冗餘

2.1.7 Shader

排查問題和關注方向:

1)Shader冗餘

2)部分常用的Shader是否可以做成常駐的,考慮做法是Shader加載後放在腳本中,引用之後不卸載;

3)Shader的內存佔用往往不會造成明顯的問題,但是數量如果過大,容易導致Shader.Parse在中低端上的高耗時;

4)資源列表中查到有不少Standard Shader,如下圖所示,由於其會帶來不必要的Shader計算開銷,從而大幅增加GPU中對應像素的計算壓力。對此,建議研發團隊嘗試對其用普通的Shader來替換。

2.2 Mono 堆內存

2.2.1 單次分配過大

報告顯示部分函數的Mono堆內存單次分配較大,建議大家通過詳細的堆內存報告中定位堆內存分配過大的時刻。這裏推薦用UWA GOT針對部分函數進行打點拆分。

2.2.2 堆內存泄露

經分析,報告也存在堆內存泄露的風險,我們通過對比相同遊戲場景(均爲副本)的不同採樣點,發現某些函數存在堆內存駐留的問題,如下圖運行到16000幀和4000幀時,遊戲場景均爲戰鬥副本中,但是函數Asset.WaitReadFileBywww有1.99MB的堆內存駐留,經過與研發團隊討論,初布判斷這裏疑似泄露。

其他函數也存在堆內存泄露的情況,建議研發團隊逐一排查。

三、優化隊列詳剖析之引擎篇

3.1 渲染模塊

渲染模塊的開銷較大,我們建議直接從堆棧信息中定位。通過性能簡報提示,我們查看Camera.Render的堆棧信息如下所示,其中藍色底紋的是需要大家關注的重點部分,即主要的渲染開銷瓶頸。

RenderForwardAlpha.Render是指半透明渲染的耗時,在這層函數中,我們看到有兩個高耗時的函數:

1)Mesh.DrawVBO和CreateVBO

這個函數在半透明下,就表示繪製半透明網格的耗時,半透明網格一般就是UI、場景裏的半透明物件,還有半透明的Mesh特效。如果只是一幀其實問題不大,但如果是頻繁開銷,則需要研發團隊特別注意。

經和研發團隊的交流討論,我們瞭解到的確存在Mesh特效過大的情況,因此我們建議是否可以設定一些設備分級的策略。

2)Material.SetPassFast

Material.SetPassFast是Unity引擎在渲染過程中Material的輪循切換開銷,一般在Unity 5.0~Unity 5.3版本中出現(項目版本是Unity 5.3.5)。根據下圖中的堆棧走勢可發現這種情況是幾乎每一幀都發生的,有渲染的地方就會有Material的切換。

從問題圖中可以看出,在運行的21000幀中,Material.SetPassFast一共被調用118萬次,頻率較高。建議研發團隊在報告中的具體信息頁面查看材質是否有過多“冗餘”的材質出現(本文2.1.5中有提及),儘可能降低材質的使用冗餘度。

3.2 UI模塊

從圖表中可以發現UIPanel.LateUpdate()的重建較高,這塊需要大家關注下UI製作是否合理,由於這塊內容和研發團隊的製作方法緊密相關,需要具體問題具體分析。所以詳細的交流探討細節我們不在此展開,建議同樣有着這方面困惑的開發者可以參閱UWA Blog上關於UI的視頻:Unity UI性能優化,這裏我們提到了面對多種UI性能優化的技巧。

3.3 物理模塊

從UI模塊可知,研發團隊使用的是NGUI,在每個Panel上都放置了一個Rigidbody,所以當UI Widgets擺在同一深度並存在相互疊加的情況時,會造成較多不必要的Contacts。建議研發團隊看下是否能把UI層之間的碰撞檢測關掉。

另外,在Unity 5.3.5版本上,如果想進一步降低物理模塊的開銷,在完全沒有使用物理的情況下,可以將Fixed Timestep設置爲0.05或0.1均可,降低它被調用的頻率。同時,儘可能優化其他模塊耗時,讓每幀的總體耗時儘可能降低。另外,需要注意的是,修改Fixed Timestep也會影響FixedUpdate的調用,在修改之前一定要檢測項目中是否有使用FixedUpdate。

3.4 粒子模塊

粒子系統的耗時多與Active的粒子數量有關,這塊建議結合粒子模塊的內存問題(文中2.1.4有提及)一併優化。

四、GPU優化

最後還需要關注的是GPU問題,UWA在測試的時候發現該遊戲在某些高端設備上耗電發熱明顯。

在GPU性能的Overdraw頁面,我們可以看到GPU信息如下圖所示:

下圖爲該幀下的截圖,可以看出,特效和UI的屏幕佔比過大,且存在UI疊層的概率較高。因此,研發團隊需要特別關注技能特效和UI界面的製作情況,避免給GPU造成大量的計算浪費。

五、優化任務隊列總結

1)AssetBundle的依賴打包方式

解決資源內存佔用過大的問題

2)Mono堆內存分配

解決Mono堆內存大小和GC卡頓的情況

3)Mesh特效

解決渲染模塊的耗時問題和GPU壓力過大問題

以上就是該遊戲的診斷內容,我們從UWA的性能簡報出發,圍繞CPU、內存、GPU三大模塊的性能問題展開,通過數據報告的查看對比,整理出了一條較爲完整的優化思路,希望能對大家的自身項目有所啓發。目前,UWA的線上測評服務已經爲數千款項目提供瞭解決方案,我們希望能幫到更多遊戲開發者節約優化的成本,提升項目的性能表現力,迎接“精品”時代的挑戰!

查看原文 >>
相關文章