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ī)平臺編譯器編譯生成的 C++ 代碼和 IL2CPP 的運(yùn)行時(shí)部分。

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

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

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

il2cpp頭文件和源碼位置:

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

2、IL2CPP特點(diǎn):

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

(2)只支持AOT編譯

(3)相對于Mono,構(gòu)建時(shí)間更長,生成代碼量更大

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)用對應(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”類型,這樣一來,在需要的時(shí)候就可以騙過C++編譯器讓其認(rèn)為所有這些函數(shù)都是一個(gè)類型。

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

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

(4)異常處理

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

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

(5)goto

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

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

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

(7)泛型共享

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

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

而對于值類型,泛型T的大小不同,只能針對不同的值類型生成特有的代碼。

如圖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í),針對各自類型生成了特有的構(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針對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)換相對于blittable類型的轉(zhuǎn)換是個(gè)耗時(shí)又要有新內(nè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ù)組,并且對數(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

通過對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)用開銷大。

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


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

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲(chǔ)服務(wù)。
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,197評論 6 531
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 98,415評論 3 415
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 176,104評論 0 373
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經(jīng)常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,884評論 1 309
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 71,647評論 6 408
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 55,130評論 1 323
  • 那天,我揣著相機(jī)與錄音,去河邊找鬼。 笑死,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,208評論 3 441
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 42,366評論 0 288
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 48,887評論 1 334
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 40,737評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 42,939評論 1 369
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,478評論 5 358
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 44,174評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,586評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,827評論 1 283
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 51,608評論 3 390
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 47,914評論 2 372

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