1. 程式人生 > >Unity將來時:IL2CPP是什麼?有了Mono為什麼還需要IL2CPP?

Unity將來時:IL2CPP是什麼?有了Mono為什麼還需要IL2CPP?

作者:小玉
連結:https://zhuanlan.zhihu.com/p/19972689
來源:知乎
著作權歸作者所有。商業轉載請聯絡作者獲得授權,非商業轉載請註明出處。

Unity3D想必大家都不陌生,獨立遊戲製作者們很多人都在用它,甚至一些大公司也用在很商業的遊戲製作上。Unity3D最大的一個特點是一次製作,多平臺部署,而這一核心功能是靠Mono實現的。可以說Mono是Unity3D核心的核心,是Unity3D跨平臺的根本。但是在2014年年中的時候,Unity3D官方部落格上卻發了一篇“The future of scripting in unity”的文章,引出了IL2CPP的概念,感覺有取代Mono之勢。那什麼是IL2CPP,它能為Unity3D和作為使用Unity3D的我們帶來哪些好處和改變?這就是本文嘗試說明的。

C#,.Net Framework


我們先說說IL2CPP試圖取代的Mono。在說Mono之前,不得不提C#語言和背後的.Net Framework。C#是微軟推出的一種基於.NET框架的、面向物件的高階程式語言。C#的發音為“see sharp”,模仿音樂上的音名“C♯”(C調升),是C語言的升級的意思。其正確寫法應和音名一樣為“C♯”。C#由C語言和C++派生而來,繼承了其強大的效能,同時又以.NET框架類庫作為基礎,擁有類似Visual Basic的快速開發能力。C#由安德斯·海爾斯伯格主持開發,微軟在2000年釋出了這種語言。說到安德斯·海爾斯伯格這裡要多說一句,當年和VC(注意,是VC,那個時候還沒有Visual Studio)齊名還有另外一家公司的IDE也非常流行,那就是Borland公司的Delphi,也是由安德斯·海爾斯伯格主導開發的。

他後來被微軟挖走,建立了J++,一門類似Java的語言(好吧,以我膚淺的知識認為,那基本就是照著Java做的)。後來由於和Sun公司授權的原因,微軟在2001年停止了J++的開發而推出了C# 1.0。說來要感謝和Sun的這場官司,否則微軟也不會有C#,J++也可能一直會跟隨Java的腳步。相反C#經過不斷的進化,從1.0開始到4.0和最新的5.0,C#已經遠遠甩開Java幾條街了(還是以我個人的使用Java和C#感覺而言,關於兩門語言的比較,無論是效率上,誇平臺上,還是語言易用性上,社群活躍度上,網上的爭論隨處可見,每個人都有自己的看法,這也不是本文的重點)。

Mono,Mono VM

C#雖好,但是隻能在Windows上執行,微軟那時候也沒有將其開源,所以總是會有人說不能跨平臺,光就這點,C#和Java就不能比呀。

微軟公司已經向ECMA申請將C#作為一種標準。在2001年12月,ECMA釋出了ECMA-334 C#語言規範。C#在2003年成為一個ISO標準(ISO/IEC 23270)。這意味著只要你遵守CLI(Common Language Infrastructure),第三方可以將任何一種語言實現到.Net平臺之上。Mono就是在這種環境下誕生的。

Mono是一個由Xamarin公司(先前是Novell,最早為Ximian)所主持的自由開放原始碼專案。該專案的目標是建立一系列符合ECMA標準(Ecma-334和Ecma-335)的.NET工具,包括C#編譯器和通用語言架構。與微軟的.NET Framework(共通語言執行平臺)不同,Mono專案不僅可以運行於Windows系統上,還可以運行於Linux,FreeBSD,Unix,OS X和Solaris,甚至一些遊戲平臺,例如:Playstation 3,Wii或XBox 360之上。Mono使得C#這門語言有了很好的跨平臺能力。相對於微軟的.Net Framework執行時庫Mono使用自己的Mono VM作為執行時庫。 加上C#本身快速友好的開發能力,最終使得Unity團隊在建立之初就決定將Mono,C#作為其核心。(嗯,這是我猜的)

有人也許會說,Unity還支援JavaScript和Boo呢,不光光只有C#一門語言。首先我要糾正的是,在Unity中的JavaScript嚴格意義上說並不是W3C規範中的JavaScript,它正確的名字叫做Unity Script,其實是從Boo演變過來的(這樣大家就能理解為啥在3門語言中,Boo用的人最少,但是卻還一直存在的原因了吧)。我認為是Unity開始為了讓更多的人能夠快速的上手,特別是考慮到很多指令碼程式設計師對JavaScript已經很熟悉了,為了照顧這部分人,發明了Unity Script,它的語法和W3C的JavaScript幾乎一致,使得大家可以直接用其進行開發,降低門檻。但是Unity Script在執行上卻和JavaScript有著本質的不同。這個我會在下一節IL中進行詳細的描述。從三門語言在Unity中的使用情況而言:Boo幾乎就沒人用了,Unity Script和C#兩者中無論是演示程式碼還是Unity Asset Store中的第三方程式碼,C#已經有85%-90%的比例(個人粗略估計,沒有做詳細統計)。可見C#在Unity中深受我等遊戲碼農的愛戴。

IL

囉嗦完了C#,.Net Framework和Mono,引出了我們很重要的一個概念”IL“。IL的全稱是 Intermediate Language,很多時候還會看到CIL(Common Intermediate Language,特指在.Net平臺下的IL標準)。在Unity部落格和本文中,IL和CIL表示的是同一個東西:翻譯過來就是中間語言。它是一種屬於通用語言架構和.NET框架的低階(lowest-level)的人類可讀的程式語言。目標為.NET框架的語言被編譯成CIL,然後彙編成位元組碼。CIL類似一個面向物件的組合語言,並且它是完全基於堆疊的,它執行在虛擬機器上(.Net Framework, Mono VM)的語言。
具體過程是:C#或者VB這樣遵循CLI規範的高階語言,被先被各自的編譯器編譯成中間語言:IL(CIL),等到需要真正執行的時候,這些IL會被載入到執行時庫,也就是VM中,由VM動態的編譯成彙編程式碼(JIT)然後在執行。


正是由於引入了VM,才使得很多動態程式碼特性得以實現。通過VM我們甚至可以由程式碼在執行時生成新程式碼並執行。這個是靜態編譯語言所無法做到的。回到上一節我說的Boo和Unity Script,有了IL和VM的概念我們就不難發現,這兩者並沒有對應的VM虛擬機器,Unity中VM只有一個:Mono VM,也就是說Boo和Unity Script是被各自的編譯器編譯成遵循CLI規範的IL,然後再由Mono VM解釋執行的。這也是Unity Script和JavaScript的根本區別。JavaScript是最終在瀏覽器的JS解析器中執行的(例如大名鼎鼎的Google Chrome V8引擎),而Unity Script是在Mono VM中執行的。本質上說,到了IL這一層級,它是由哪門高階語言建立的也不是那麼重要了,你可以用C#,VB,Boo,Unity Script甚至C++,只要有相應的編譯器能夠將其編譯成IL都行!


IL2CPP, IL2CPP VM

本文的主角終於出來了:IL2CPP。有了上面的知識,大家很容易就理解其意義了:把IL中間語言轉換成CPP檔案。大家如果看明白了上面動態語言的CLI, IL以及VM,再看到IL2CPP一定心中充滿了疑惑。現在的大趨勢都是把語言加上動態特性,哪怕是c++這樣的靜態語言,也出現了適合IL的c++編譯器,為啥Unity要反其道而行之,把IL再弄回靜態的CPP呢?這不是吃飽了撐著嘛。根據本文最前面給出的Unity官方部落格所解釋的,原因有以下幾個:
1.Mono VM在各個平臺移植,維護非常耗時,有時甚至不可能完成
Mono的跨平臺是通過Mono VM實現的,有幾個平臺,就要實現幾個VM,像Unity這樣支援多平臺的引擎,Mono官方的VM肯定是不能滿足需求的。所以針對不同的新平臺,Unity的專案組就要把VM給移植一遍,同時解決VM裡面發現的bug。這非常耗時耗力。這些能移植的平臺還好說,還有比如WebGL這樣基於瀏覽器的平臺。要讓WebGL支援Mono的VM幾乎是不可能的。
2.Mono版本授權受限
大家有沒有意識到Mono的版本已經更新到3.X了,但是在Unity中,C#的執行時版本一直停留在2.8,這也是Unity社群開發者抱怨的最多一條:很多C#的新特性無法使用。這是因為Mono 授權受限,導致Unity無法升級Mono。如果換做是IL2CPP,IL2CPP VM這套完全自己開發的元件,就解決了這個問題。
3.提高執行效率
根據官方的實驗資料,換成IL2CPP以後,程式的執行效率有了1.5-2.0倍的提升。

使用Mono的時候,指令碼的編譯執行如下圖所示:


簡單的來說,3大指令碼被編譯成IL,在遊戲執行的時候,IL和專案裡其他第三方相容的DLL一起,放入Mono VM虛擬機器,由虛擬機器解析成機器碼,並且執行
IL2CPP做的改變由下圖紅色部分標明:


在得到中間語言IL後,使用IL2CPP將他們重新變回C++程式碼,然後再由各個平臺的C++編譯器直接編譯成能執行的原生彙編程式碼。

幾點注意:
1.將IL變回CPP的目的除了CPP的執行效率快以外,另一個很重要的原因是可以利用現成的在各個平臺的C++編譯器對程式碼執行編譯期優化,這樣可以進一步減小最終遊戲的尺寸並提高遊戲執行速度。

2.由於動態語言的特性,他們多半無需程式設計師太多關心記憶體管理,所有的記憶體分配和回收都由一個叫做GC(Garbage Collector)的元件完成。雖然通過IL2CPP以後程式碼變成了靜態的C++,但是記憶體管理這塊還是遵循C#的方式,這也是為什麼最後還要有一個IL2CPP VM的原因:它負責提供諸如GC管理,執行緒建立這類的服務性工作。但是由於去除了IL載入和動態解析的工作,使得IL2CPP VM可以做的很小,並且使得遊戲載入時間縮短。

3.由於C++是一門靜態語言,這就意味著我們不能使用動態語言的那些酷炫特性。執行時生成程式碼並執行肯定是不可能了。這就是Unity裡面提到的所謂AOT(Ahead Of Time)編譯而非JIT(Just In Time)編譯。其實很多平臺出於安全的考慮是不允許JIT的,大家最熟悉的有iOS平臺,在Console遊戲機上,不管是微軟的Xbox360, XboxOne,還是Sony的PS3,PS4,PSV,沒有一個是允許JIT的。使用了IL2CPP,就完全是AOT方式了,如果原來使用了動態特性的程式碼肯定會編譯失敗。這些程式碼在編譯iOS平臺的時候天生也會失敗,所以如果你是為iOS開發的遊戲程式碼,就不用擔心了。因此就這點而言,我們開發上幾乎不會感到什麼問題。

最後給出Unite 2014上官方給出的效能測試截圖(數字越小表示執行得越快):