Huatuo 劃時(shí)代的Unity原生C#熱更新技術(shù)
huatuo介紹
huatuo是一個(gè)特性完整、零成本、高性能、低內(nèi)存的近乎完美的Unity全平臺(tái)原生c#熱更方案。
huatuo擴(kuò)充了il2cpp的代碼,使它由純AOT runtime變成‘AOT+Interpreter’ 混合runtime,進(jìn)而原生支持動(dòng)態(tài)加載assembly,使得基于il2cpp backend打包的游戲不僅能在Android平臺(tái),也能在IOS、Consoles等限制了JIT的平臺(tái)上高效地以AOT+interpreter混合模式執(zhí)行。
基礎(chǔ)概念
CLR
CLR即 Common Language Runtime,中文叫公共語言運(yùn)行時(shí),是讓 .NET 程序執(zhí)行所需的外部服務(wù)的集合,.NET 平臺(tái)的核心和最重要的組件,類似于 Java 的 JVM。更詳細(xì)介紹請(qǐng)看?公共語言運(yùn)行時(shí) (CLR) 概述
il2cpp
il2cpp是Unity開發(fā)的跨平臺(tái)CLR解決方案。誕生它的一個(gè)關(guān)鍵原因是Unity需要跨平臺(tái)運(yùn)行,但一些平臺(tái)如iOS這種禁止了JIT,導(dǎo)致依賴了JIT的官方CLR虛擬機(jī)無法運(yùn)行,必須使用AOT技術(shù)將mananged程序提前轉(zhuǎn)化為目標(biāo)平臺(tái)的靜態(tài)原生程序后再運(yùn)行。而mono雖然也支持AOT,但性能較差以及跨平臺(tái)支持不佳。
il2cpp方案包含一套AOT運(yùn)行時(shí)以及一套dll到C++代碼及元數(shù)據(jù)的轉(zhuǎn)換工具,使得原始的c#開發(fā)的代碼最終能在iOS這樣的平臺(tái)運(yùn)行起來。
il2cpp與熱更新
很不幸,不像mono有Hybrid mode execution,支持動(dòng)態(tài)加載dll,il2cpp是一個(gè)純靜態(tài)的AOT運(yùn)行時(shí),不支持運(yùn)行時(shí)加載dll,因此不支持熱更新。
目前unity平臺(tái)的主流熱更新方案xlua、ILRuntime之類都是引入一個(gè)第三方vm(virtual machine),在vm中解釋執(zhí)行代碼,來實(shí)現(xiàn)熱更新。限于篇幅我們只分析使用c#為開發(fā)語言的熱更新方案。這些熱更新方案的vm與il2cpp是獨(dú)立的,意味著它們的元數(shù)據(jù)系統(tǒng)是不相通的,在熱更新里新增一個(gè)類型是無法被il2cpp所識(shí)別的(例如通過System.Activator.CreateInstance是不可能創(chuàng)建出這個(gè)熱更新類型的實(shí)例),這種看起來像、實(shí)際上卻又不是的偽CLR虛擬機(jī),在與il2cpp這種復(fù)雜的CLR運(yùn)行時(shí)交互時(shí),產(chǎn)生極大量的兼容性問題,另外還有嚴(yán)重的性能問題。
一個(gè)大膽的想法是,是否有可能對(duì)il2cpp運(yùn)行時(shí)進(jìn)行擴(kuò)充,添加interpreter模塊,進(jìn)而實(shí)現(xiàn)mono?hybrid mode execution
?這樣機(jī)制?這樣一來就能徹底支持熱更新了,并且兼容性極佳。對(duì)開發(fā)者來說,除了以解釋模式運(yùn)行的部分執(zhí)行得比較慢,其他方面跟標(biāo)準(zhǔn)的運(yùn)行時(shí)沒有區(qū)別。
對(duì)il2cpp加以了解并且深思熟慮后的答案是——確實(shí)是可行的!具體分析參見?關(guān)于huatuo可行性的思維實(shí)驗(yàn)?。這個(gè)想法誕生了huatuo,unity平臺(tái)第一個(gè)支持ios的跨平臺(tái)原生c#熱更新方案!
原理
huatuo擴(kuò)充了il2cpp運(yùn)行時(shí),將它由AOT運(yùn)行時(shí)改造為'AOT + interpreter'雙引擎的混合運(yùn)行時(shí),進(jìn)而完美支持在iOS這種禁止JIT的平臺(tái)上以解釋模式無縫地運(yùn)行動(dòng)態(tài)加載的dll。如下圖所示:




實(shí)際使用體驗(yàn)或者特性比較
huatuo學(xué)習(xí)和使用成本幾乎為零。huatuo讓il2cpp變成全功能的runtime,學(xué)習(xí)和使用成本幾乎為零,幾乎零侵入性。而其他方案則有大量的坑和需要規(guī)避的規(guī)則,學(xué)習(xí)和使用成本,需要對(duì)原項(xiàng)目作大量改造。
huatuo可以使用所有c#的特性。而其他方案往往有大量的限制。
huatuo中可以直接支持使用和繼承主工程中的類型。其他方案要寫適配器或者生成代碼。
huatuo中熱更新部分元數(shù)據(jù)與AOT元數(shù)據(jù)無縫統(tǒng)一。像反射代碼能夠正常工作的,AOT部分也可以通過標(biāo)準(zhǔn)Reflection接口創(chuàng)建出熱更新對(duì)象。其他方案做不到。
huatuo對(duì)多線程支持良好。像多線程、ThreadStatic、async等等特性都是huatuo直接支持,其他方案除了async特性外均難以支持。
huatuo中Unity工作流與原生幾乎完全相同。huatuo中熱更新MonoBehaviour可以直接掛載在熱更新資源上,并且正確工作。其他方案不行。
huatuo兼容性極高。各種第三方庫只要在il2cpp下能工作,在huatuo下也能正常工作。其他方案往往要大量魔改源碼。
huatuo內(nèi)存效率極高。huatuo中熱更新類型與主工程的AOT類型完全等價(jià),占用一樣多的空間。其他方案的同等類型則是假類型,不僅不能被runtime識(shí)別,還多占了數(shù)倍空間。
huatuo執(zhí)行效率高。huatuo中熱更新部分與主工程AOT部分交互屬于il2cpp內(nèi)部交互,效率極高。而其他方案則是獨(dú)立虛擬機(jī)與il2cpp之間的效率,不僅交互麻煩還效率低下。
運(yùn)行性能
實(shí)際性能如理論估計(jì),全面并且大幅勝出當(dāng)前主流的xlua、puerts、ILRuntime之類的熱更新方案。
基礎(chǔ)指令(數(shù)值計(jì)算及條件跳轉(zhuǎn)等指令),由于各個(gè)語言之間差距不大,因此勝出不明顯
對(duì)象模型指令。由于沒有跨語言交互的成本,幾乎是數(shù)倍到數(shù)十倍的提升(如果指令自身消耗特別大,則差距不那么明顯)
性能測(cè)試用例來自ILRuntime提供的標(biāo)準(zhǔn)測(cè)試用例,測(cè)試項(xiàng)目來自Don't worry的github倉(cāng)庫。
測(cè)試結(jié)果顯示,絕大多數(shù)測(cè)試用例都有數(shù)倍到數(shù)十倍的性能差距,差距極其夸張。唯獨(dú)數(shù)值計(jì)算跟xlua有少量劣勢(shì),這是因?yàn)楫?dāng)前huatuo?未對(duì)指令作任何優(yōu)化?,后續(xù)優(yōu)化版本大多數(shù)基礎(chǔ)指令都將有100-300%的性能提升。





