【厚積薄發】扒一扒Profiler中這幾個“佔坑鬼”

厚積薄發】扒一扒Profiler中這幾個“佔坑鬼”

2016年3月19日 11:01 閱讀 2351

原文地址:http://weibo.com/p/1001603954695990318082#_loginLayer_1472091401699
文章重點總結:

Gfx.WaitForPresent && Graphics.PresentAndSync

這兩個參數在Profiler中經常出現CPU佔用較高的情況,且僅在發佈版本中可以看到。究其原因,其實是CPU和GPU之間的垂直同步(VSync)導致的,之所以會有兩種參數,主要是與項目是否開啓多線程渲染有關。當項目開啓多線程渲染時,你看到的則是Gfx.WaitForPresent;當項目未開啓多線程渲染時,看到的則是Graphics.PresentAndSync


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

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


最後,如何優化並降低這兩個參數的CPU佔用呢? 那就是,忽略Gfx.WaitForPresent 和 Graphics.PresentAndSync這兩個參數,優化其他你能優化的一切!


 WaitForTargetFPS

該參數一般出現在 CPU開銷過低,且通過設定了目標幀率的情況下(Application.targetFrameRate)。當上一幀低於目標幀率時,將會在本幀產生一個WaitForTargetFPS的空閒等待耗時,以維持目標幀率 所以可以對這個參數視而不見。






WaitForTargetFPS、Gfx.WaitForPresent 和 Graphics.PresentAndSync是我們經常會被問到的參數。想必正在讀此文的你也經常在Profiler中遇到過這幾項CPU開銷過大的情況。對此,我們今天就來好好地聊一聊這幾個參數的具體含義和觸發規則。


各位開發朋友,作好準備,下面的文章會很乾,很難啃!

所以小編自說自話地加了張圖....


 WaitForTargetFPS

該參數一般出現在 CPU開銷過低,且通過設定了目標幀率的情況下(Application.targetFrameRate)。當上一幀低於目標幀率時,將會在本幀產生一個WaitForTargetFPS的空閒等待耗時,以維持目標幀率。


解析:該項在Unity引擎的主循環中其實是最早執行的,即引擎實際上是根據上一幀的CPU耗時,在當前幀中通過增補WaitForTargetFPS的方式來將運行FPS維持到目標值。比如,目標幀率爲30幀/秒,上一幀耗時15ms,那麼當前幀中WaitForTargetFPS將會是18(33-15)ms,但是這一幀中其他耗時爲28ms,那麼在Profiler中這一幀的總耗時就變成了46(18+28)ms。


因此,由該值造成了Profiler開銷較高的現象,其實是耗時的“假象”,在優化過程中,你對它可以“視而不見”。


Gfx.WaitForPresent && Graphics.PresentAndSync

這兩個參數在Profiler中經常出現CPU佔用較高的情況,且僅在發佈版本中可以看到。究其原因,其實是CPU和GPU之間的垂直同步(VSync)導致的,之所以會有兩種參數,主要是與項目是否開啓多線程渲染有關。當項目開啓多線程渲染時,你看到的則是Gfx.WaitForPresent;當項目未開啓多線程渲染時,看到的則是Graphics.PresentAndSync


Graphics.PresentAndSync 是指主線程進行Present時的等待時間和等待垂直同步的時間。Gfx.WaitForPresent其字面意思同樣也是進行Present時需要等待的時間,但這裏其實省略了很多的內容。其真實的意思應該是爲了在渲染子線程(Rendering Thread)中進行Present,當前主線程(MainThread)需要等待的時間。聽起來依然很拗口,下面,我們就來進行詳細地解釋。


當項目開啓多線程程渲染時,引擎會將Present等相關工作儘可能放到渲染線程去執行,即主線程只需通過指令調用渲染線程,並讓其進行Present,從而來降低主線程的壓力。但是,當CPU希望進行Present操作時,其需要等待GPU完成上一次的渲染。如果GPU渲染開銷很大,則CPU的Present操作將一直處於等待操作,其等待時間,即爲當前幀的Gfx.WaitForPresent時間,如下圖所示。


同理,當項目未開啓多線程渲染時,引擎會在主線程中進行Present(當前絕大多數的移動遊戲均在使用該中操作),當然,Present操作同樣需要等待GPU完成上一次的渲染。如果GPU渲染開銷很大,則CPU的Present操作將一直處於等待操作,其等待時間,即爲當前幀的Graphics.PresentAndSync時間,如下圖所示。


我們做了一個較爲極端的例子來展示這種情況在Unity 5.3.3版本上,創建60個全屏UIPanel,分別開啓和關閉多線程渲染,並不設置TargetFPS。那麼,在三星S6設備上該參數的CPU開銷如下:


開啓多線程渲染時: 


關閉多線程渲染時: 

所以,如果你的項目中,Gfx.WaitForPresent或Graphics.PresentAndSync的CPU耗時非常高時,其實並不是它們自己做了什麼神祕的操作,而是你當前的渲染任務太重,GPU負載過高所致。


同時,對於開啓垂直同步的項目而言,Gfx.WaitForPresent 和 Graphics.PresentAndSync也會出現CPU佔用較高的情況。在解釋這種問題之前,我們先以“大家乘坐地鐵”來舉個例子。一般來說,地鐵到達每一站的時間均是平均且一定的,假設每10分鐘一班接走一批乘客。但是幾乎沒有多少乘客可以按點到達,如果提前兩分鐘到達,則只需要等待兩分鐘即可乘上地鐵,但是,如果你錯過了,哪怕只差了一分鐘,那麼你也不得不再等待九分鐘才能乘上地鐵。


上述的情況我們經常會遇到。在GPU的渲染流水線中,其轉換front buffer和back buffer的工作原理和“乘坐地鐵”其實是一致的。大家可以把GPU的流水線簡單地想象成爲一列地鐵。對於移動設備來說,GPU的幀率一般爲30幀/秒或60幀/秒,即VSync每33ms或每16.6ms“到站一次”,CPU的Present即爲“乘客乘上地鐵”,然後前往各自的目的地。與乘客的早到和晚到一樣,CPU的Present也會出現類似的情況,比如:


● CPU端開銷非常小,Present在很早即被執行,但此時VSync還沒到,則會出現較高的等待時間,即Gfx.WaitForPresent 和 Graphics.PresentAndSync的CPU開銷看上去很高。下圖爲Unity 5.3.3版本上,一個空場景在不開啓多線程渲染、不設置TargetFPS的情況下,Graphics.PresentAndSync在三星S6設備上的CPU佔用情況。 


● CPU端開銷很高,使得Present執行時錯過了VSync操作,這樣,Present將不得不等待下一次VSync的到來,從而造成了Gfx.WaitForPresent 和 Graphics.PresentAndSync的CPU開銷較高。這種情況在CPU端加載過量資源時特別容易發生,比如WWW加載較大的AssetBundle、Resource.Load加載大量的Texture等等。


通過以上的講解,我們希望此刻的你已經對Gfx.WaitForPresent 和 Graphics.PresentAndSync已經有了深入的理解。這兩個參數無論CPU佔用多少,其實都不是這兩個參數的自身問題,而是項目的其他部分造成。對此,我們做一個總結,以方便你進一步加深印象。


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

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


最後,如何優化並降低這兩個參數的CPU佔用呢? 那就是,忽略Gfx.WaitForPresent 和 Graphics.PresentAndSync這兩個參數,優化其他你能優化的一切!


 有沒有一種被欺騙很久的感覺,還好侑虎君救了你呀~O(∩_∩)O



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

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


最後,如何優化並降低這兩個參數的CPU佔用呢? 那就是,忽略Gfx.WaitForPresent 和 Graphics.PresentAndSync這兩個參數,優化其他你能優化的一切!

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章