IL2CPP

1备籽、IL2CPP組成:

(1)AOT編譯器(il2cpp.exe)

unity中IL2CPP編譯步驟如下:

a厅目、將 Unity Scripting API 代碼編譯為常規(guī) .NET DLL(托管程序集)早抠。

b江耀、應(yīng)用托管字節(jié)碼剝離萨咳。此步驟可顯著減小構(gòu)建的游戲大小截粗。

c垮刹、將所有托管程序集轉(zhuǎn)換為標(biāo)準(zhǔn) C++ 代碼达吞。

d、使用本機(jī)平臺(tái)編譯器編譯生成的 C++ 代碼和 IL2CPP 的運(yùn)行時(shí)部分荒典。

e宗挥、將代碼鏈接到可執(zhí)行文件或 DLL,具體取決于目標(biāo)平臺(tái)种蝶。

圖1-1 IL2CPP構(gòu)建項(xiàng)目自動(dòng)步驟圖

(2)運(yùn)行時(shí)庫(kù)(libil2cpp)

il2cpp頭文件和源碼位置:

Unity安裝目錄下:Unity\Editor\Data\il2cpp\libil2cpp

2契耿、IL2CPP特點(diǎn):

(1)針對(duì)C#中計(jì)算密集型代碼性能相比Mono提高很多

(2)只支持AOT編譯

(3)相對(duì)于Mono,構(gòu)建時(shí)間更長(zhǎng)螃征,生成代碼量更大

3搪桂、IL2CPP生成的C++代碼解析(Unity版本:2019.4.23)

(1)成員變量

圖3-1 il2cpp生成的成員變量C++代碼

由上圖可以看出,il2cpp將成員變量的生成分為兩個(gè)結(jié)構(gòu)體盯滚,一個(gè)結(jié)構(gòu)體包含所有普通成員變量踢械,另一個(gè)包StaticFields包含所有的靜態(tài)成員變量。每一個(gè)成員變量都會(huì)生成一個(gè)get和一個(gè)set的內(nèi)聯(lián)函數(shù)魄藕。

圖3-2 il2cpp生成的靜態(tài)成員變量調(diào)用C++代碼

因?yàn)殪o態(tài)成員是所有實(shí)例共享的數(shù)據(jù)内列,因此在運(yùn)行的時(shí)候,Varient_t75DD1618D504050BE978835922A46B83C2969565_StaticFields只有一份背率。所有的Varient_t75DD1618D504050BE978835922A46B83C2969565實(shí)例都共享這個(gè)數(shù)據(jù)话瞧。調(diào)用時(shí)如上圖所示嫩与,在Varient_t75DD1618D504050BE978835922A46B83C2969565的元信息結(jié)構(gòu)中有一個(gè)指向Varient_t75DD1618D504050BE978835922A46B83C2969565_StaticFields結(jié)構(gòu)的指針,獲取staticfield指針再調(diào)用對(duì)應(yīng)的get方法交排。

(2)普通成員函數(shù):

圖3-3 自定義普通成員函數(shù)
圖3-4 自定義普通成員函數(shù)生成的C++代碼

(3)靜態(tài)方法

圖3-5 自定義靜態(tài)函數(shù)
圖3-6??
圖3-7
圖3-8 il2cpp生成的函數(shù)聲明代碼
圖3-9? 自定義靜態(tài)函數(shù)生成的C++實(shí)現(xiàn)代碼

如上面幾個(gè)圖所示划滋,所有的函數(shù)都被聲明成了IL2CPP_EXTERN_C也就是extern “C”類型,這樣一來(lái)埃篓,在需要的時(shí)候就可以騙過(guò)C++編譯器讓其認(rèn)為所有這些函數(shù)都是一個(gè)類型处坪。

所有的函數(shù)都不是成員函數(shù)。函數(shù)的第一個(gè)參數(shù)永遠(yuǎn)都是“this”指針架专。對(duì)于托管代碼中的靜態(tài)函數(shù)同窘,IL2CPP會(huì)忽略這個(gè)參數(shù)也就相當(dāng)于傳遞NULL作為第一個(gè)參數(shù)的值。這么做的好處是可以讓il2cpp.exe轉(zhuǎn)換代碼的邏輯更加簡(jiǎn)單并且讓代理函數(shù)的處理變得更加容易部脚。所有的函數(shù)還有一個(gè)額外的RuntimeMethod*參數(shù)用來(lái)描述函數(shù)的元信息塞椎。這些元信息是虛函數(shù)調(diào)用的關(guān)鍵。Mono使用和特定平臺(tái)相關(guān)的方法來(lái)傳遞這些元信息睛低。而IL2CPP出于可移植方面的考慮案狠,并沒(méi)有使用這些和平臺(tái)相關(guān)的特定代碼。在this指針和RuntimeMethod*之間的就是函數(shù)實(shí)際用到的參數(shù)钱雷。

托管代碼中的類型會(huì)被加上“_t”的后綴骂铁,函數(shù)則是加上“_m”后綴和一個(gè)唯一的MD5碼來(lái)避免名字的重復(fù)。

(4)異常處理

圖3-10 托管代碼中異常處理
圖3-11 il2cpp生成異常捕獲代碼

托管代碼中的異常都會(huì)被il2cpp轉(zhuǎn)換成C++的異常罩抗。如圖3-11拉庵,所有托管異常都被封裝在C++中的IL2CppExcetionWrapper類型,當(dāng)C++代碼捕獲異常后套蒂,會(huì)解析獲取對(duì)應(yīng)的托管異常钞支,通過(guò)il2cpp_codegen_class_is_assignable_from()判斷該異常是否要捕獲的異常,如果是則跳轉(zhuǎn)至Catch代碼塊執(zhí)行操刀,否則拋出一個(gè)C++異常烁挟。

(5)goto

如圖3-11所示,我們發(fā)現(xiàn)生成的C++代碼中包含goto語(yǔ)句骨坑。這是因?yàn)镮L中沒(méi)有while撼嗓、do和for循環(huán)以及if/else結(jié)構(gòu),它們都是使用標(biāo)簽欢唾、相等goto與條件goto語(yǔ)句實(shí)現(xiàn)的且警。所以il2cpp在處理IL代碼時(shí),也直接使用標(biāo)簽+goto的方式實(shí)現(xiàn)礁遣。

(6)il2cpp.exe不針對(duì)每一個(gè)類中的函數(shù)生成單獨(dú)的一個(gè)cpp文件斑芜,而是將很多類的函數(shù)放在一個(gè).cpp文件。如上圖祟霍,Assetbly-CSharp中所有的函數(shù)被生成在5個(gè).cpp文件中杏头。il2cpp之所以這么做的原因是C++編譯器在編譯相同代碼量的情況下盈包,處理大量的文件的用時(shí)比集中處理少量文件長(zhǎng)很多。

圖3-12 il2cpp生成的.cpp文件

(7)泛型共享

il2cpp針對(duì)泛型參數(shù)是引用類型還是值類型生成不同的共享代碼大州。

對(duì)于引用類型的泛型共享续语,由于所有托管代碼中的引用類型都繼承自System.Object垂谢,轉(zhuǎn)成C++代碼后都能用RuntimeObject *指針表示厦画。所以所有引用類型都可以共享一份代碼實(shí)現(xiàn)。

而對(duì)于值類型滥朱,泛型T的大小不同根暑,只能針對(duì)不同的值類型生成特有的代碼。

如圖3-14徙邻,當(dāng)泛型類型是string和AnyClass兩個(gè)引用類型排嫌,最終調(diào)用的構(gòu)造函數(shù)是同一個(gè)函數(shù)即GenericType_1__ctor_mAF3DF5964EBFD3F6A60BB493C66276FA1FCF431B_gshared。

而當(dāng)泛型參數(shù)是DateTime和Int兩個(gè)值類型時(shí)缰犁,針對(duì)各自類型生成了特有的構(gòu)造函數(shù):

GenericType_1__ctor_mBD3620561730BBF48F1E983A56B62AD1A7487645_gshared和GenericType_1__ctor_mC02B2327528BC3724FACBF36E3CA75FA335AC1B7_gshared淳地。

另外il2cpp總是先生成全共享代碼(參數(shù)是引用類型的泛型類)。其它參數(shù)是值類型的泛型代碼是在托管代碼中有用到時(shí)才會(huì)生成特有類型的代碼帅容,從而減少代碼體量颇象。

另外泛型類中的函數(shù)也是和泛型類性綁定的,不論函數(shù)是否使用泛型類型參數(shù)并徘。如圖3-13中的UseGenericParameter()和DoesNotUseGenericParameter()都生成了類型特有的方法遣钳。如圖3-15所示。

圖3-13 托管泛型代碼
圖3-14 生成的泛型類構(gòu)造方法
圖3-15 泛型類中函數(shù)生成代碼

(8)P/Invloke封裝

P/Invoke就是托管代碼調(diào)用封裝在DLL中的非托管函數(shù)麦乞。雖然IL2CPP會(huì)將托管代碼轉(zhuǎn)換為C++代碼蕴茴,但I(xiàn)L2CPP針對(duì)C#中數(shù)據(jù)類型生成的C++數(shù)據(jù)類型和原生的C++類型會(huì)存在區(qū)別,所有IL2CPP運(yùn)行時(shí)就需要進(jìn)行類型轉(zhuǎn)換操作姐直。

托管代碼中倦淀,數(shù)據(jù)類型可以分為兩類:blittable和non-blittable。blittable類型數(shù)據(jù)在托管和原生代碼中內(nèi)存表示一致声畏,如:byte晃听、int、float等砰识;

而non-blittable類型數(shù)據(jù)在托管代碼中和C++原生代碼中內(nèi)存表現(xiàn)不同能扒,如:bool、string辫狼、array等初斑。而進(jìn)行類型轉(zhuǎn)換就會(huì)引發(fā)分配新的內(nèi)存。

a膨处、內(nèi)存轉(zhuǎn)換non-blittable類型

圖3-16 外部非托管代碼生成的C++代碼
圖3-17 封裝的非托管代碼xlua_getglobal

如圖3-16所示是生成的C++代碼见秤,其中string類型參數(shù)被轉(zhuǎn)換成char*砂竖,產(chǎn)生了新的內(nèi)存分配,并且在函數(shù)執(zhí)行結(jié)束后鹃答,調(diào)用il2cpp_codegen_marshal_free釋放char*內(nèi)存乎澄。所以non-blittable類型內(nèi)存轉(zhuǎn)換相對(duì)于blittable類型的轉(zhuǎn)換是個(gè)耗時(shí)又要有新內(nèi)存開(kāi)銷的操作。

b测摔、內(nèi)存轉(zhuǎn)換數(shù)組

如果轉(zhuǎn)換的是一個(gè)元素為blittable類型的數(shù)組置济,只是調(diào)用il2cpp_codegen_marshal_array()返回托管代碼中數(shù)組的首地址。如圖3-19锋八。

如果轉(zhuǎn)換元素為non-bilttable類型的數(shù)組浙于,則需要調(diào)用il2cpp_codegen_marshal_allocate_array()分配一個(gè)新的數(shù)組,并且對(duì)數(shù)組中每個(gè)元素做一次內(nèi)存轉(zhuǎn)換挟纱。如圖3-21羞酗。

圖3-18 托管代碼向非托管代碼傳入整形數(shù)組
圖3-19 il2cpp由3-16生成的C++代碼
圖3-20 托管代碼向非托管代碼傳遞自定義結(jié)構(gòu)
圖3-21 il2cpp由3-18生成的C++代碼

4、使用IL2CPP代碼優(yōu)化:

(1)Devirtualization

通過(guò)對(duì)IL2CPP生成的C++代碼分析紊服,當(dāng)調(diào)用一個(gè)抽象方法時(shí)檀轨,il2cpp生成的C++代碼都會(huì)執(zhí)行一次虛方法調(diào)用(VirtFuncInvoker),虛方法調(diào)用就會(huì)查詢虛函數(shù)表vtable找到合適的方法進(jìn)行調(diào)用,所以虛方法調(diào)用比直接函數(shù)調(diào)用開(kāi)銷大欺嗤。

所以我們應(yīng)盡量避免虛方法調(diào)用参萄,明確直接方法調(diào)用。另外剂府,能明確不需要子類繼承的類或方法使用sealed關(guān)鍵字標(biāo)記拧揽,這樣il2cpp生成C++代碼時(shí)可以明確的知道使用哪個(gè)方法,就可以直接調(diào)用對(duì)應(yīng)的方法取代虛方法調(diào)用腺占。


以上內(nèi)容翻譯自Unity Blog中來(lái)自Josh Peterson的博客(https://blog.unity.com/author/cap-josh)以及Unity官方手冊(cè)淤袜。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市衰伯,隨后出現(xiàn)的幾起案子铡羡,更是在濱河造成了極大的恐慌,老刑警劉巖意鲸,帶你破解...
    沈念sama閱讀 217,907評(píng)論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件烦周,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡怎顾,警方通過(guò)查閱死者的電腦和手機(jī)读慎,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,987評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)槐雾,“玉大人夭委,你說(shuō)我怎么就攤上這事∧记浚” “怎么了株灸?”我有些...
    開(kāi)封第一講書人閱讀 164,298評(píng)論 0 354
  • 文/不壞的土叔 我叫張陵崇摄,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我慌烧,道長(zhǎng)逐抑,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書人閱讀 58,586評(píng)論 1 293
  • 正文 為了忘掉前任屹蚊,我火速辦了婚禮厕氨,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘淑翼。我一直安慰自己腐巢,他們只是感情好品追,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,633評(píng)論 6 392
  • 文/花漫 我一把揭開(kāi)白布玄括。 她就那樣靜靜地躺著,像睡著了一般肉瓦。 火紅的嫁衣襯著肌膚如雪遭京。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書人閱讀 51,488評(píng)論 1 302
  • 那天泞莉,我揣著相機(jī)與錄音哪雕,去河邊找鬼。 笑死鲫趁,一個(gè)胖子當(dāng)著我的面吹牛斯嚎,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播挨厚,決...
    沈念sama閱讀 40,275評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼堡僻,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了疫剃?” 一聲冷哼從身側(cè)響起钉疫,我...
    開(kāi)封第一講書人閱讀 39,176評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎巢价,沒(méi)想到半個(gè)月后牲阁,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,619評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡壤躲,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,819評(píng)論 3 336
  • 正文 我和宋清朗相戀三年城菊,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片碉克。...
    茶點(diǎn)故事閱讀 39,932評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡凌唬,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出棉胀,到底是詐尸還是另有隱情法瑟,我是刑警寧澤冀膝,帶...
    沈念sama閱讀 35,655評(píng)論 5 346
  • 正文 年R本政府宣布,位于F島的核電站霎挟,受9級(jí)特大地震影響窝剖,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜酥夭,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,265評(píng)論 3 329
  • 文/蒙蒙 一赐纱、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧熬北,春花似錦疙描、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書人閱讀 31,871評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至巫延,卻和暖如春效五,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背炉峰。 一陣腳步聲響...
    開(kāi)封第一講書人閱讀 32,994評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工畏妖, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人疼阔。 一個(gè)月前我還...
    沈念sama閱讀 48,095評(píng)論 3 370
  • 正文 我出身青樓戒劫,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親婆廊。 傳聞我的和親對(duì)象是個(gè)殘疾皇子迅细,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,884評(píng)論 2 354

推薦閱讀更多精彩內(nèi)容