IL2CPP的优化 : Devirtualization 去虚拟化
Unity的腳本虛擬機(jī)團(tuán)隊(duì)一直在尋找讓代碼運(yùn)行得更快的方法。這是三篇介紹關(guān)于IL2CPP AOT編譯器小優(yōu)化文章的第一篇,另外這篇文章也會(huì)教大家如何進(jìn)行優(yōu)化。雖然這些優(yōu)化并不會(huì)讓你的代碼運(yùn)行速度提升兩到三倍,但是它們也會(huì)對(duì)游戲起到非常重要的幫助,我們也希望它們能幫助您了解你的代碼是如何運(yùn)行的。
現(xiàn)代編譯器非常擅長(zhǎng)執(zhí)行各種優(yōu)化來(lái)提高代碼運(yùn)行時(shí)的性能。作為開(kāi)發(fā)人員,我們通常可以向編譯器顯式的傳達(dá)一些代碼信息來(lái)幫助編譯器提升性能。今天,我們將詳細(xì)討論IL2CPP的一個(gè)小優(yōu)化,看看它如何改進(jìn)現(xiàn)有代碼的運(yùn)行效率。
Devirtualization
眾所周知,虛方法的調(diào)用通常比函數(shù)直接調(diào)用開(kāi)銷(xiāo)更大。我們對(duì)libil2cpp的運(yùn)行時(shí)庫(kù)中進(jìn)行了一些性能優(yōu)化,以降低虛函數(shù)的調(diào)用開(kāi)銷(xiāo)(在下一篇文章中會(huì)有更多的介紹),但是它們?nèi)匀恍柙谶\(yùn)行時(shí)進(jìn)行一些查找有一些開(kāi)銷(xiāo)。編譯器無(wú)法知道在運(yùn)行時(shí)那個(gè)函數(shù)會(huì)被調(diào)用,或者是否可以被調(diào)用?
Devirtualization 是一種常見(jiàn)的編譯器優(yōu)化策略,它將通過(guò)虛方法通過(guò)虛表的調(diào)用轉(zhuǎn)換為直接調(diào)用。當(dāng)編譯器在編譯時(shí)能夠準(zhǔn)確地知道運(yùn)行時(shí)實(shí)際會(huì)調(diào)用哪種方法時(shí),編譯器就會(huì)使用這種策略優(yōu)化。但不幸的是,這點(diǎn)往往很難做到,因?yàn)榫幾g器通常無(wú)法了解整個(gè)代碼庫(kù)的代碼。但是如果可以做到的話,它可以使虛擬方法的調(diào)用變的更快。
典型的例子
當(dāng)我作為一個(gè)年輕開(kāi)發(fā)者的時(shí)候,我通過(guò)一個(gè)相當(dāng)常見(jiàn)的動(dòng)物例子學(xué)習(xí)了虛方法的相關(guān)知識(shí)。下面這段代碼您可能也很熟悉:
?
復(fù)制代碼
接下來(lái)在Unity(5.3.5版)中,我們可也以使用這些類來(lái)做一個(gè)小農(nóng)場(chǎng):
?
復(fù)制代碼
這里的每次調(diào)用都是一個(gè)虛方法的調(diào)用。讓我們看看能否讓IL2CPP對(duì)這些方法調(diào)用做出優(yōu)化直接調(diào)用來(lái)提高執(zhí)行性能。
生成的C++代碼
我非常喜歡IL2CPP的一個(gè)特性就是它時(shí)生成C++代碼而不是匯編代碼。當(dāng)然,這段代碼看起來(lái)不像一般手寫(xiě)的C++代碼,但是還是比匯編更容易理解。讓我們看看生成的foreach里的代碼:
?
復(fù)制代碼
我已經(jīng)刪除了一些其他的生成代碼來(lái)做簡(jiǎn)化。二手手機(jī)號(hào)轉(zhuǎn)讓看到那個(gè)丑陋的Invoke調(diào)用了嗎?它先在虛表中查找真正被調(diào)用的虛方法,然后才調(diào)用它。顯而易見(jiàn),虛表的查找會(huì)比直接調(diào)用函數(shù)慢很多。因?yàn)檫@種動(dòng)物可以是一頭?;蛞活^豬,也可以是某種其他類型的動(dòng)物。
接下來(lái)讓我們看看第二段代碼生成的C++代碼。第二段代碼我們new了一個(gè)Cow,然后調(diào)用了LogFormat打印Cow的Speak函數(shù)的返回值,這看上去應(yīng)該是直接調(diào)用函數(shù)了吧:
復(fù)制代碼
但即使在這種情況下,我們可以看到編譯器仍然在通過(guò)虛表調(diào)用函數(shù)!IL2CPP在優(yōu)化方面相當(dāng)保守,在大多數(shù)情況下都更傾向于保證正確性。由于它沒(méi)有對(duì)全程序進(jìn)行分析來(lái)確定這是一個(gè)可以直接調(diào)用的函數(shù),因?yàn)榭赡芘R灿信缮?#xff0c;所以它選擇了更安全(和更慢)的虛方法調(diào)用。
但是假如我們知道農(nóng)場(chǎng)里沒(méi)有其他種類的牛了,牛沒(méi)有其他派生類了。那么我們就可以把這些信息顯式傳達(dá)給編譯器,讓編譯器優(yōu)化,我們就能得到一個(gè)更好的結(jié)果。讓我們對(duì)Cow做一些修改:
?
復(fù)制代碼
sealed關(guān)鍵字可以告訴編譯器,Cow不會(huì)有派生類了(sealed 也可以修飾Speak函數(shù))。這樣IL2CPP就能確信可以直接進(jìn)行方法調(diào)用了:
?
復(fù)制代碼
可以看到這次調(diào)用就是直接調(diào)用不會(huì)再慢了,因?yàn)槲覀円呀?jīng)明確的告訴編譯器相關(guān)信息,可以讓編譯器進(jìn)行優(yōu)化了。
雖然這種優(yōu)化可能不會(huì)讓您的游戲運(yùn)行速度有顯著的提升,但是對(duì)于代碼的閱讀和編譯器本身來(lái)說(shuō),這都是一個(gè)非常好的實(shí)踐,清楚的表達(dá)您寫(xiě)的代碼的意圖。如果您使用IL2CPP進(jìn)行編譯,那么我強(qiáng)烈建議您閱讀一下編譯后生成的C++代碼,或許會(huì)有意想不到的收獲!
總結(jié)
以上是生活随笔為你收集整理的IL2CPP的优化 : Devirtualization 去虚拟化的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 揭密微信《跳一跳》小游戏那些外挂
- 下一篇: 如何快速找到最优路线?深入理解游戏中寻路