作者:Ian
翻譯:Kelvin Lo / 海龜
系列回顧:
Unity3D性能優化最佳實踐(一):分析
Unity3D性能優化最佳實踐(二):內存
Coroutine 運作方式和其他腳本程序不同,大多數的程序消耗只會出現在追蹤報告裡的單一位置,在特定的回呼被執行,但是 Coroutine會出現在 CPU 報告的兩個不同地方。
所有在 Coroutine 裡的初始化程序,從方法開始到第一次的 yield,這部分的追蹤數據會出現在 Coroutine 啟動的地方,通常是 StartCoroutine 被呼叫的地方。直接從從 Unity 回呼啟動的 Coroutine(比如回傳值宣告成 IEnumerator 的 Start)則會顯示在各自的 Unity 回呼下。
所有剩下的 Coroutine程序,從第一次被繼續執行到結束,都會出現在 Unity 主循環下的DelayedCallManager 裡。
要知道為何會這樣,就必須了解 Coroutine 的運作方式。
Coroutine 底下有個 C# 編譯程序自動產生的類別的實例, 對程序設計師來說看起來像是一個普通的方法,但在實作上我們需要這個對象在每次呼叫之間保存這個 Coroutine 的狀態。因為 Coroutine 中的局部變量(local-scope variables)在 yield 之間必須保有之前賦予的值,所以這些局部變量會提到先前所說的 C# 編譯程序產生的類別,它的實例在 Coroutine 的執行結束之前會維持配置在堆積內存上(Heap memory)。這個對象也負責追蹤 Coroutine 的執行狀態:它會記住 Coroutine 在 yield 之後下一次該從哪邊繼續。
因此,啟動一個 Coroutine 所引起的內存消耗同等於一個固定的成本加上這個 Coroutine 用到的局部變量總合的大小。
啟動 Coroutine 的程序建構並呼叫這個自動生成對象上的方法,然後 Unity 的 DelayedCallManager 在 Coroutine的 yield 時給的條件(WaitForSeconds、WaitForFixedUpdate 之類的條件)滿足時再次呼叫它。由於 Coroutine通常在其他 Coroutine 之外啟動 ,這會讓它們的執行消耗拆分成上述兩個不同位置。
上圖可以看到 DelayedCallManager 正重新呼叫幾個不同的 Coroutine,像是 PopulateCharacters、AsyncLoad 和 LoadDatabase。
如果可以,儘可能用最少 Coroutine 做最多的事,雖然巢狀 Coroutine(從 Coroutine 再產生 Coroutine)非常好維護也能維持程序簡潔,但每次使用 Coroutine 就要配置新的追蹤用對象,用越多Coroutine 代表內存消耗也越多。
如果 Coroutine 幾乎每一幀都要執行,也沒有用 yield 在需要長時間等待的操作上,改回用 Update 或LateUpdate 會比較好。尤其是對那種周期很長或是無窮的 Coroutine 更是如此。
要記得很重要的一點是 Coroutine「不是」線程(threads),Coroutine 裡的同步(Synchronous)行為仍然是在主線程上執行的,因此如果你的目的是降低主線程在 CPU 上的消耗,就跟呼叫一般方法一樣必須避免在 Coroutine 上面執行會卡住(Blocking)線程的操作。
Coroutine 還是最適合處理長時間的異步操作,比如等候 HTTP 傳輸、資源加載或是檔案 I/O。
熱議丨無止盡加班的代價有多大?
程序丨這些你都看過了嗎?年度盤點top40
如何用代碼快速寫出一個排位榜?教程大公開
想讓你的遊戲運行速度快10倍?你要這樣做!
……
添加小編微信,發送【程序】,可享雙重福利
1.加入GAD程序猿交流基地,獲取行業乾貨資訊,觀看大牛分享直播
2.直接領取60G獨家程序資料庫,地址在小編朋友圈
包括騰訊內部分享、文章教程、視頻教程等全套資料
↓長按添加小編GAD-沫沫↓