2016年8月2日 星期二

IL2CPP優化:去虛擬化

作者:JOSH PETERSON 原文

Unity的腳本團隊一直在尋找讓程式更快執行的方法。本系列三篇文章的第一篇將為大家介紹IL2CPP AOT編譯器的小優化方式與如何利用它們。儘管這些小優化並不能讓您的程式執行速度提高2-3倍,但它們會在遊戲中起到重要作用,我們希望這些優化能讓您對程式的執行方式有更深入的理解。

近代的編譯器都很擅於執行一些優化來提高程式的執行效率。作為開發者,我們經常可以透過已知功能的程式,透過一定管道傳達給編譯器來幫助編譯器提高效率。本文將詳細講解一個關於 IL2CPP的小優化,並看看它將如何提高現有程式的執行效率。

去虛擬化(Devirtualization)

眾所周知,虛擬方法的呼叫通常比直接呼叫來的耗效能。我們一直致力於改善libil2cpp運行庫的效能以降低虛擬方法呼叫的消耗(下篇文章將介紹更多內容),但某些排序演算法仍然需要一些執行時的搜查。編譯器無法知道哪些方法會在執行時被呼叫,亦或可不可以被呼叫?

去虛擬化是一個很常見的編譯器優化方案,也就是將虛擬方法呼叫改為直接的方法呼叫。當編譯器編譯時可以提供準確的“實際”方法時,這個方案就會被啟用。不幸的是通常很難做到,因為編譯器通常無法看到整個代碼庫。然而如果可以的話,虛擬方法的呼叫會更快速。


典型例子


作為一個年輕的開發者,我學習虛擬方法是從一個常見的動物案例開始的。可能這個例子你看了很眼熟:  然後在 Unity (版本 5.3.5) 中,我們可以使用這些類別來做個小農場:

這裡每次呼叫Speak的都是虛擬方法呼叫。讓我們看看 IL2CPP去虛擬化這些方法呼叫是如何提高效能的。

產生的C++代碼還不錯


我喜歡IL2CPP的一個功能就是它會產生C++程式碼而非組合語言。當然,這些產生的程式與一般人寫的看起來並不一樣,但這種代碼要比組合語言容易理解得多。下面看看產生的 
foreach迴圈:

這裡移除了一些產生的程式來簡化內容。看到那個醜陋的Invoke呼叫了麼?它將會在 vtable中查詢適當的虛擬方法並進行呼叫。vtable的查詢會比直接的函式呼叫慢一些,這可以理解,因為 Animal(動物)可能是 Cow(牛)或者Pig(豬),或者一些其它的派生類型。

下面看看產生的程式中的第二個呼叫Debug.LogFormat,這個看起來更像是直接呼叫:

這個例子中仍然使用的是虛擬方法呼叫! 事實上IL2CPP對於優化非常保守,絕大多數情況下會優先確保正確性。由於它並未對整個專案做完全的分析來確保可以進行直接呼叫,因而選擇了更安全(也慢)的虛擬方法呼叫。

假設我們知道農場中沒有其它類型的牛了,因此Cow(牛)這個類別不會產生衍生類別。如果我們清楚告知編譯器這點,就能獲得一個更好的結果。現將類別的定義改為如下:

關鍵字sealed告訴編輯器 Cow(牛)不會有衍生類(sealed也可以直接應用於 Speak方法)。現在 IL2CPP能進行直接呼叫了:

這裡呼叫 Speak就不會慢了,因為我們已經清楚的告訴編譯器,並且有把握地允許編譯器進行優化。

這種優化不會使你的遊戲執行速度顯著變快。但這對於後來的人閱讀程式和編輯器都會有效許多。如果您想使用IL2CPP編譯,強烈建議仔細閱讀專案產生後的C++代碼,可能會有意想不到的收穫!

下一篇我們將討論為什麼虛擬方法呼叫消耗高,以及怎樣使它變得更快。

1 則留言:

  1. 您好, 想要請教您有關升級64位元問題。據Google Play要求2019年8月前所有APP都必須升級為64位元,我的APP原先是用unity5.6.1f1,原本以為只要使用unity支援64位元的版本build APK即可升級為64位元,但事實卻沒有那麼簡單,在IL2CPP編譯地方一直有問題,所以想要請教您,從32位元(unity5.6.1f1)升級到64位元,應該使用那個unity版本?程式裡面需要做什麼調整嗎?謝謝。

    回覆刪除

著作人