Unity將來時:IL2CPP是什麼?

http://zhuanlan.zhihu.com/indieace/19972689


作者:@Xu Bowie

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上官方給出的性能測試截圖(數字越小表示運行得越快):

有了IL2CPP,程序尺寸可以相對縮小,運行速度可以提高!看了興奮嗎?其實現有的Unity版本中已經引入了IL2CPP技術。本文下篇就通過一個實際的例子,看看IL2CPP都爲我們做了哪些,以及我們需要注意些什麼。


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章