扒一扒Profiler中這幾個「佔坑鬼」

2021-09-19 05:28:44 字數 3397 閱讀 1416

waitfortargetfps、gfx.waitforpresent 和 graphics.presentandsync是我們經常會被問到的引數。想必正在讀此文的你也經常在profiler中遇到過這幾項cpu開銷過大的情況。對此,我們今天就來好好地聊一聊這幾個引數的具體含義和觸發規則。

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

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

這兩個引數在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這兩個引數,優化其他你能優化的一切

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

扒一扒Profiler中這幾個「佔坑鬼」

解析 該項在unity引擎的主迴圈中其實是最早執行的,即引擎實際上是根據上一幀的cpu耗時,在當前幀中通過增補waitfortargetfps的方式來將執行fps維持到目標值。比如,目標幀率為30幀 秒,上一幀耗時15ms,那麼當前幀中waitfortargetfps將會是18 33 15 ms,但...

扒一扒Profiler中這幾個「佔坑鬼」

waitfortargetfps gfx.waitforpresent和graphics.presentandsync是我們經常會被問到的引數。想必正在讀此文的你也經常在profiler中遇到過這幾項cpu開銷過大的情況。對此,我們今天就來好好地聊一聊這幾個引數的具體含義和觸發規則。解析 該項在un...

扒一扒vue的資料追蹤原理

大家都知道在angular或vue中,資料的更新會實時的反應到dom上,那麼到底是什麼原理呢,下面就vue 一下。開啟vue的官網,可以看到它關於vue如何追蹤變化的原理。把乙個普通物件傳給 vue 例項作為它的 data 選項,vue.js 將遍歷它的屬性,用 object.defineprope...