天天看點

一次MMO手遊的性能診斷全紀錄

1. 概覽(定位CPU耗時的瓶頸)

我們先來看下CPU的平均耗時占用情況,在測試的十多分鐘内産生了14539幀資料,其中幀數大于33ms的CPU耗時占比是44.2%,而一般我們推薦開發者将這名額盡量控制在10%以下來保證遊戲整體的流暢度。

一次MMO手遊的性能診斷全紀錄

從上圖中CPU走勢上來看,戰鬥過程中有比較大的波動,下圖是各大子產品的耗時占比,可以看到占比較大的是渲染子產品(42%),其次是腳本(39%),動畫(4%),加載等。

一次MMO手遊的性能診斷全紀錄

值得大家注意的是,腳本子產品的統計中不僅包含了邏輯代碼的開銷,還包括了UI子產品的開銷,因為NGUI、UGUI都屬于腳本的開銷。由于CPU子產品中的渲染、腳本、動畫子產品開銷比較大,是以後續我們将對這些子產品進行比較詳細的解讀。

2. 渲染子產品 & 粒子系統

從渲染子產品的資料可以看到,半透明渲染均值為5.7ms,不透明渲染均值4.8ms,耗時加起來超過10ms,該值稍高,現在渲染子產品的峰值是12W(屬于偏高的級别),在中低端機器上會造成一定的壓力。

一次MMO手遊的性能診斷全紀錄

在這裡我們來看下半透明渲染和不透明渲染的耗時走勢。不透明渲染的數值可以對應三角形面片,我們可以看到峰值處兩者是比較一緻的。一般來說,不透明物體的渲染優化需要簡化場景中渲染面片量。

一次MMO手遊的性能診斷全紀錄

同時,我們還能觀察到,多數情況下半透明渲染的耗時和Draw Call的走勢接近。

一次MMO手遊的性能診斷全紀錄

對于半透明渲染優化,一般主要從兩部分入手:粒子系統和UI子產品,為此我們來看下粒子系統的資料。

一次MMO手遊的性能診斷全紀錄

上文我們截取了半透明和不透明渲染的耗時走勢,下面我們就将其和粒子系統的走勢做個對比。大緻看到,紫色半透明和粒子系統的渲染曲線在很大程度上是吻合的;同時,我們也可以看到半透明渲染的耗時曲線在後續都高于10ms,由于粒子系統主要集中在3-4ms,是以我們可以大緻判斷出,渲染的問題很大程度上不是粒子系統的問題,具體分析可以見以下視訊。

由上推斷,我們不妨來看下UI子產品耗時的走勢。下圖中,黃色線是NGUI中UIPanel.LateUpdate的開銷,大緻可以了解為NGUI在做網格重建和更新時的開銷。正常情況下應該是穩定的,但是出現重建的時候會有峰值,在這個項目中有時候接近10ms甚至20ms。

一次MMO手遊的性能診斷全紀錄

一般來說,出現比較大的重建時,會對渲染子產品的性能産生明顯影響。是以通過UI子產品和粒子子產品的資料對比來看,我們基本上可以判定,渲染子產品的開銷主要集中在UI子產品和粒子系統,相對來說,UI子產品更多一點。針對這兩個子產品,從現在的資料來看主要優化UI子產品,因為單從UI子產品的資料上來看,6.1ms的CPU均值非常高,我們推薦控制在3ms。

另外需要研發團隊考慮的一點是堆記憶體配置設定總值(143.8MB),主要是由NGUI網格重建所緻,重建操作越頻繁,該總值越高。

一次MMO手遊的性能診斷全紀錄

從截圖畫面來看,UI偏向于靜态的面闆,HUD類似于飄動的字型、血條的使用頻率應該沒那麼高,是以10~20ms的開銷不是很合理,需要研發團隊對複雜的面闆進行檢測,盡量保證這些大的面闆中沒有頻繁變動的UI元素。

在UWA對某些項目進行深度優化的時候,會經常看到技能的面闆,因為出現冷卻的遮罩或者數字,引起了整個UI Panel的重建,導緻較高的UIPanel.LateUpdate的開銷。我們建議大家将消失和出現的UI元素從複雜的面闆中獨立出來,進而将網格重建的範圍減小,也會對半透明渲染提升性能。

渲染子產品的細節部分還可以通過UWA報告中代碼效率的Camera.Render來看。

一次MMO手遊的性能診斷全紀錄

可以看到半透明渲染(Render.TransparentGeometry)占了40%,MeshRenderer.Render占了21%,拼合(ParticleSystem.RenderSingle)和沒有拼合(ParticleSystem.RenderBatch)的加起來10%左右。是以半透明渲染這塊,可以認為粒子系統占了10%,UI 和場景中半透明物件占了20%。就該項目的目前場景而言,其開銷主要是UI界面造成的,是以,UI子產品是目前半透明渲染的瓶頸,且具有比較大的優化空間。

現場提問 Q:空中的視野會影響渲染子產品的性能嗎? A:這裡的渲染面片數不是指所有場景遊戲中的模型之和,而是看到的模型面數。一般來說,空中視角會比戰鬥的視角範圍大很多,一般戰鬥中不會看到非常大的模型,是以三角面片不會很高。空中戰鬥的話壓力就比較高了。 Q:UI的網格重建怎麼了解? A:大家可以通過我們官方部落格(blog.uwa4d.com)上的一些技術推文和直播回顧對UI優化有些大緻的把握,了解UI 網格重建會影響到UIPanel.lateupdate等機制等概念。比如說一個很小的元素,我僅僅改個顔色,或者隐藏了,就會導緻整個UI Panel的重建,這樣就造成更高的耗時。 Q:我們的策劃要求每個UI都會動畫,這樣優化是不是就很難? A:這是有可能的,因為從資料上來看,現在還是有很多不合理的地方, 我們可以看到圖中大量動态的UI元素還是比較少,我們猜測應該是某些UI 元素在頻繁消失和出現,導緻整個UI Panel在重建,這裡就需要大家自行去定位哪些UI Panel有問題了。

3. 動畫子產品

從報告中看到大家同時用到Animator和Animation兩個元件,前者耗時均值0.7 ms,後者耗時均值為1 ms,偶爾有些峰值,但大部分都在合理範圍内。

一次MMO手遊的性能診斷全紀錄

在動畫子產品的資料中,MeshSkinning的耗時有點偏高。比如上圖這段區間内基本接近30 ms,看上去是和Boss戰鬥,推測是Boss的頂點數比較高導緻,需要研發團隊進一步确認。MeshSkinning的優化主要是通過降低頂點數的數量來優化。

動畫子產品的性能細節還能從UWA報告的代碼效率檢視,下圖是Animation.Update的具體走勢。

一次MMO手遊的性能診斷全紀錄

我們可以看到,在多數情況下都是1ms左右,偶爾有峰值,點開該場景下的堆棧資訊按鈕,我們看到詳細的堆棧配置設定情況:

一次MMO手遊的性能診斷全紀錄

很明顯,Animation.Update的耗時峰值主要由于Animation.RebuidInternalState 這個函數所緻,該函數一般出現激活或執行個體化帶有Animation元件的GameObject時,每次出現時開銷都會比較高,避免的方法還是比較簡單:我們現在看到大部分角色的緩存,我們都會禁用掉,但是如果開啟禁用比較頻繁,會導緻這個函數經常出現。一種比較好的優化方式是将怪物在隐藏或者放到緩沖池時,不是把GameOject的根節點禁用掉,而是把上面的Animation元件禁用掉(Enabled屬性),激活時隻需要開啟Animation元件就可以,這樣就可以避免這個函數的開銷,進而降低這裡的峰值。然後把一些邏輯等停掉,但是Animation根節點依然還在激活狀态,不受影響。

Q:Animator也是有這個函數嗎? Animator.initialize和剛剛這個函數對應。這裡的峰值也是因為帶有Animator的元件做了SetActive(true)的操作,大家也可以後續做下檢測。

4.GC

在關注CPU的時候,我們也會關注GC調用的情況。

一次MMO手遊的性能診斷全紀錄

GC的調用頻率接近1000,目前看下來資料是比較合理的;另外GC的耗時在371ms,這個數值相對來說偏高。要優化CG耗時主要是通過兩種方法:1)降低調用頻率,即盡可能跑更多的幀再調用,2)降低GC每次的耗時。兩種優化的方式不太一樣,我們分别說明:

1)降低調用頻率可以通過減少GC累積的配置設定資料來實作。我們可以跳轉至代碼效率-堆記憶體使用的頁面。

一次MMO手遊的性能診斷全紀錄

這些是堆記憶體累積的配置設定量,堆記憶體配置設定越少,則GC觸發也越少。可以看到, 排名前兩個函數的累積配置設定加起來有200MB左右,這裡有大量的提升空間。第一個函數UIPanel.LateUpdate()是NGUI的函數,優化的方法并不是通過優化代碼,而是盡可能優化NGUI網格重建的頻率;第二個函數UERoot.Update()是大家的主邏輯,需要通過代碼函數去定位堆記憶體的配置設定,現在我們已經有Mono堆記憶體測試功能,大家可以通過Mono報告去看具體函數的開銷,定位起來相對起來會容易得多(後文将詳細說明)。總而言之,GC調用頻率的優化主要通過這個面闆去找累積堆記憶體配置設定最高的函數,一點點去優化。

2)降低GC的耗時可以通過優化堆記憶體的峰值來實作。GC調用一次的開銷和堆記憶體裡的對象有關,即對象越多、峰值越高,則GC越高。該項目接近100MB的堆記憶體峰值是較高的,我們推薦降低到40MB範圍以内。

堆記憶體的峰值一方面影響記憶體的大小、另一方面影響GC的CPU開銷,需要大家特别注意。

其他子產品的性能較為正常,在此不多做說明。

在UWA報告的記憶體子產品來看,在測試的十多分鐘内,記憶體峰值達到378MB,堆記憶體峰值達到92.6MB,都是屬于比較偏高的,下面我們将通過記憶體子產品的幾大構成來逐一分析。

一次MMO手遊的性能診斷全紀錄

1.堆記憶體

在堆記憶體的走勢圖中我們發現兩個情況:首先是剛剛測試的時候就配置設定了68MB左右的堆記憶體,另一方面是堆記憶體的不斷增長,最終達到了92MB。

一次MMO手遊的性能診斷全紀錄

關于前者,多數是配置檔案占用的空間比較大,或者緩存機制所緻;針對後者,在這裡建議大家可以參考UWA的Mono報告中高堆記憶體留存函數清單。如下圖示範,通過點選右邊的“藍色箭頭”,可以檢視某些函數中生成的駐留在記憶體中的詳細變量情況,進而能更快地判斷和定位堆記憶體的洩露點。

一次MMO手遊的性能診斷全紀錄

2.資源記憶體—紋理

除了堆記憶體,我們再來看下資源記憶體。紋理的記憶體占用峰值為91MB,這個值在我們測試過的大量項目中看是屬于偏高的。到底是哪些資源占了那麼多的空間?我們可以跳轉到UWA報告的具體資源資訊中來各個擊破。在這些資源的屬性中,我們先來關注下數量峰值,如下圖,我們可以看到不少資源的數量峰值出現了2、3等數值, 即相同的資源在測試包中出現了兩份、三份。

一次MMO手遊的性能診斷全紀錄

同時,我們發現五頁左右的紋理都存在備援兩份的情況,其中不少是100KB的紋理資源的情況,是以造成了不少紋理資源的浪費,建議大家通過UWA資源檢測工具來檢視AssetBundle中是否有備援,接下來檢測代碼,是否有反複加載和反複Unload導緻紋理依然殘留的情況。此外,從紋理資源的格式上來看是比較正常的,基本上用了各個平台支援的格式。

3.資源記憶體—網格

網格的記憶體峰值在24MB,相對合理,但從走勢上來看也會有持續向上的趨勢,數量稍微偏高。同樣,網格資源也存在一定的備援問題,如下圖所示。

一次MMO手遊的性能診斷全紀錄

除了這張數量峰值,我們剛剛提到Color和Tangent屬性,這兩個屬性對于大多數的Shader來說都用不上,是以需要研發團隊進一步确認,是否有開啟不必要的頂點屬性。

Q:Tangent是否在NGUI中會被用到? 預設不會開啟,最多生成normal。一般情況下是有color,但是沒有normal和tangent。

4.資源記憶體—動畫資源

動畫資源的數量也是比較多的,是以記憶體是明顯偏高的。雖然存在一種可能,即如果大家采用了動畫子產品的緩存機制,的确會不斷上漲,但建議研發團隊也确認下這部分能否優化。畢竟報告中的數值相當高,足以引起大家重視了。

一次MMO手遊的性能診斷全紀錄
Q:為什麼資源會有備援的情況? A: AssetBundle之間本身有備援,它們分别被加載進來後就會産生備援;解除安裝後再加載,也會産生備援。 Q:這個無法達到沒有備援的吧? A:可以做到零備援,主要控制好管理的機制。1、避免AB本身沒有備援;2、通過管理的方式提前知道這個紋理是否已經被加載等。> 在UWA部落格中有幾篇相關的技術文章,大家可以參考下。

5.資源記憶體—Shader

Shader的記憶體一向都是比較小的,這裡主要還是看數量峰值,因為Shader資源數量會造成Shader.Parse函數的開銷。在UWA報告的“重要參數解析”一欄中,可以看到頻繁出現了56-60ms的峰值,這些都是Shader的解析造成。是以Shader的備援可以了解成Shader.Parse的使用頻率更多了。檢查備援的辦法和上述的一樣。

一次MMO手遊的性能診斷全紀錄

在記憶體篇中,我們看到總體記憶體峰值為378MB,其中資源記憶體峰值将近227MB、堆記憶體的峰值92.6MB,那麼剩餘的将近60MB記憶體占用去哪兒了呢?這時,我們千萬不能忽略這兩大殺手:WebStream和SerializedFile。

1)WebStream記憶體占用

WebStream為Unity 5.3 以前版本的項目,通過特定API(new WWW、CreateFromMemory等)加載AssetBundle檔案所開辟的較大塊記憶體。主要用于存放AssetBundle的原始資料和解壓後資料。

2)序列化資訊記憶體占用

Unity引擎的序列化資訊種類繁多,其中最為常見且記憶體占用較大的為 SerializedFile。在Unity 5.3之前的版本中,該序列化資訊的記憶體配置設定主要為項目通過特定API(WWW.LoadFromCacheOrDownload、CreateFromFile等)加載AssetBundle檔案所緻。

對于這部分的優化,這就要結合UWA的另一項黑科技—資源管理。我們可以在該子產品裡看到加載資源時的總次數、加載方式、加載耗時等具體資訊。

一次MMO手遊的性能診斷全紀錄

從表中看,AssetBundle加載的頻率是比較一緻的,基本上都是十幾次,并且有一定間隔,但是大家可以注意到有些AssetBundle重複加載的頻率比較高,如下圖中連續三次加載,這可能就是存在一些問題,比如緩存時間過短等等。

一次MMO手遊的性能診斷全紀錄

另外可以通過UWA報告中的具體AssetBundle使用情況子產品檢視AssetBundle在記憶體中的駐留情況,如下圖所示。大家在Unity 4.x 版本上用的是WWW,是以記憶體中具有一定的WebStream的占用,加載的AssetBundle比較多,WebStream也會較大。目前來看,AssetBundle最高值13個,在合理範圍之内。

一次MMO手遊的性能診斷全紀錄

可以看到,資源加載主要是通過AB.load和AB.loadasync兩個API,這個加載次數是比較合理的。

一次MMO手遊的性能診斷全紀錄

同時,我們也能通過該面闆檢視到具體的資源加載和解除安裝的情況:

一次MMO手遊的性能診斷全紀錄

資源執行個體化

做執行個體化操作的時候會有明顯的開銷,對于一些元素的SetActive的時候也會有開銷。比如執行個體化的時候被操作了上百次,研發團隊需要考慮是否在戰鬥中頻繁出現,有這些元素存在的話,我們建議在關卡前做一次預加載,之後用到的時候通過緩沖池進行激活、禁用等等,來減少執行個體化的開銷。

一次MMO手遊的性能診斷全紀錄

該項目SetActive操作比較高,總共有16萬次的操作,我們看下頻率較高的幾個。

我們看到Skillicon元素 (藍色線)在戰鬥過程中有持續的SetActive的操作,由于我們是每十幀彙總一次資料,是以每幀就會有5 個的調用,是以大家要特别注意這些元素,如果隻是個空的Object,那麼SetActive的開銷是非常小的,但是如果這個元素身上帶了些元件,大家需要确認下這些元件身上是否存在一些每幀都先禁用然後通過某些條件再打開的的一些操作,導緻一些問題。

是以從資源管理的面闆中,我們可以看到AssetBundle加載、駐留、資源執行個體化和激活等情況,幫助我們把加載部分做得更流暢。一旦資源加載和執行個體化發生在戰鬥中,那麼峰值基本上是難避免,現在我們看到相對比較合理的方法是:執行個體化操作在戰鬥剛剛開始時候發生,然後随着戰鬥的時間慢慢加長,後面的執行個體化時間應該盡可能避免掉,利用緩沖池的方法等去優化。

最後,我們看來下該遊戲在三星S6上的GPU的耗時情況。

1. Overdraw

我們主要關注填充倍數均值,4.0x 我們可以認為在測試的過程中,平均每一幀的像素會被填充4次,該值較高,一般我們建議把這個數值控制在3.0x左右。

一次MMO手遊的性能診斷全紀錄

雖然Overdraw總體問題不算大,但是我們也會通過曲線去找一下是否有比較高的地方。比方說某些區域的填充倍數會到18的情況,從對應的畫面來看,可能是和BOSS戰鬥,或者進入一些區域的時候,看上去螢幕上的特效比較多,在峰值區域特效比較多,是以畫面會比較亮,其實Overdraw較高在多數情況下就是因為半透明特效比較多、區域比較大所緻。

一次MMO手遊的性能診斷全紀錄

另外 UI界面展開的情況下也會比較大 。這種情況下如果遊戲時間比較長,GPU的負載比較高,導緻發熱比較快。是以Overdraw比較高的地方需要大家關注,特别是持續時間比較長的界面,類似UI界面(因為UI都是半透的,是以Overdraw會疊起來)。對此,我們一般建議減少UI和其背後場景的重疊,比如下圖中UI後面的場景還在正常進行的(後面的人物和背景都看得到),大家可以考慮下能否在全屏UI出現後把相機關掉,這樣的話可以減少不必要的Overdraw開銷。

一次MMO手遊的性能診斷全紀錄

最後還有一些面積很大的特效,從截圖上來看還好出現的時間比較短,是以優化的優先級略低。

原文出處:侑虎科技

本文作者:admin

轉載請與作者聯系,同時請務必标明文章原始出處和原文連結及本聲明。