HybridCLR是一个个性残缺、零老本、高性能、低内存的近乎完满的Unity全平台原生C#热更计划。

HybridCLR裁减了IL2CPP的代码,使它由纯AOT Runtime变成“AOT+Interpreter“混合Runtime,进而原生反对动静加载Assembly,使得基于IL2CPP Backend打包的游戏不仅能在Android平台,也能在iOS、Consoles等限度了JIT的平台上高效地以AOT+interpreter混合模式执行。

HybridCLR开创性地实现了“Differential Hybrid Dll“技术。即能够对AOT Dll任意增删改,会智能地让变动或者新增的类和函数以Interpreter模式运行,但未改变的类和函数以AOT形式运行,让热更新的游戏逻辑的运行性能根本达到原生AOT的程度。

HybridCLR(wolong)原作者walon(focus-creative-games创始人)将该系列课程放入UWA学堂连载更新,现已【收费上线UWA学堂】,本文先带大家对HybridCLR——划时代的Unity原生C#热更新技术有个简略的理解,更多内容可返回UWA学堂查看。


1. 根底概念

1.1 CLR
CLR即Common Language Runtime,中文叫公共语言运行时,是让.NET程序执行所需的内部服务的汇合,是.NET平台的外围和最重要的组件,相似于Java的JVM。更具体介绍请看《公共语言运行时 (CLR) 概述》。

1.2 IL2CPP
IL2CPP是Unity开发的跨平台CLR解决方案,诞生它的一个要害起因是Unity须要跨平台运行。但一些平台如iOS,这种禁止JIT并导致依赖JIT的官网CLR虚拟机无奈运行,而是必须应用AOT技术将Mananged程序提前转化为指标平台的动态原生程序后再运行。而Mono尽管也反对AOT,但性能较差以及跨平台反对不佳。

IL2CPP计划蕴含一套AOT运行时以及一套DLL到C++代码及元数据的转换工具,使得原始的C#开发的代码最终能在iOS这样的平台运行起来。

1.3 IL2CPP与热更新
很可怜,不像Mono有Hybrid mode execution,可反对动静加载DLL。IL2CPP是一个纯动态的AOT运行时,不反对运行时加载DLL,因而不反对热更新。

目前Unity平台的支流热更新计划xLua、ILRuntime之类都是引入一个第三方VM(Virtual Machine),在VM中解释执行代码,来实现热更新。这里咱们只剖析应用C#为开发语言的热更新计划。这些热更新计划的VM与IL2CPP是独立的,意味着它们的元数据系统是不相通的,在热更新里新增一个类型是无奈被IL2CPP所辨认的(例如,通过System.Activator.CreateInstance是不可能创立出这个热更新类型的实例),这种看起来像,但实际上又不是的伪CLR虚拟机,在与IL2CPP这种简单的CLR运行时交互时,会产生极大量的兼容性问题,另外还有重大的性能问题。

一个大胆的想法是,是否有可能对IL2CPP运行时进行裁减,增加Interpreter模块,进而实现Mono hybrid mode execution这样机制?这样一来就能彻底反对热更新,并且兼容性极佳。对开发者来说,除了解释模式运行的局部执行得比较慢,其余方面跟规范的运行时没有区别。

对IL2CPP加以理解并且三思而行后的答案是——的确是可行的!具体分析参见第二节《对于HybridCLR可行性的思维试验》 。这个想法诞生了HybridCLR,Unity平台第一个反对iOS的跨平台原生C#热更新计划!


2. 原理

HybridCLR裁减了IL2CPP运行时,将它由AOT运行时革新为“AOT + interpreter”双引擎的混合运行时,进而完满反对在iOS这种禁止JIT的平台上以解释模式无缝地运行动静加载的DLL。如下图所示:

更具体一些,至多须要实现以下性能:

  • 加载和解析DLL元数据
  • 动静注册元数据,其中要害为Hook动静函数的执行流到解释器函数
  • 实现一个高效正确的解释器
  • 正确处理GC及多线程等运行时机制

3. 个性

3.1 规范运行时个性
近乎残缺实现了ECMA-335标准,不反对的个性仅包含:

  • 不反对Delegate的BeginInvoke,EndInvoke。纯正是感觉没必要实现。
  • 不反对MonoPInvokeCallbackAttribute。意味着你如果同时还接了Lua,你没法间接将热更新的C#函数注册到Lua中,但有一个不简单的方法能做到这点。

因为HybridCLR极其残缺的实现,让应用HybridCLR后的C#开发体验跟Editor下Mono开发简直完全相同(除了调用一些IL2CPP没实现的.net framework函数,非专家级别的开发者难以结构出HybridCLR不反对的用例)。

另外,因为是运行时级别的实现,HybridCLR反对这些个性的同时,不须要你额定生成或者调整任何代码。对于开发者来说,相比Unity下原生C#开发,零额定的学习和开发成本。

3.2 AOT相干个性
因为IL2CPP AOT模块的存在,IL2CPP比规范运行时多了一些不存在的机制,因而HybridCLR也有一些额定的个性:

  • 反对应用Interpreter Assembly替换AOT Assembly(限度:必须不存在其余AOT Assembly对它的间接援用)。
  • 反对补充元数据机制,彻底反对AOT泛型,参见《AOT泛型限度及原理介绍》。
  • 反对AOT Hotfix,能够修复AOT模块的Bug。
  • 反对任意C#函数注册到Lua之类的虚拟机,不限于Static函数,并且也不须要MonoPInvokeCallbackAttribute。条件是注册和回调形式须要稍微调整。

3.3 Unity相干个性

  • 完满反对Unity的Assembly Def模块机制。
  • 完满反对代码中挂载热更新脚本,无应用场景限度
  • 完满反对资源上挂载热更新脚本,但要求打包工作流有少许调整,参见《MonoBehaviour相干工作流》。

4. 与其余风行的C#热更新计划的区别

从原理来说,HybridCLR简直将热更新技术做到实践上的极限,与以后所有支流热更新计划不在一个档次。

4.1 实质比拟
HybridCLR是原生的C#热更新计划。艰深地说,IL2CPP相当于Mono的AOT模块,HybridCLR相当于Mono的Interpreter模块,两者合一成为残缺Mono。HybridCLR使得IL2CPP变成一个全功能的Runtime,原生(即通过System.Reflection.Assembly.Load)反对动静加载DLL,从而反对iOS平台的热更新。

正因为hHybridCLR是原生Runtime级别实现,热更新局部的类型与主工程AOT局部类型是齐全等价并且无缝对立的。能够随便调用、继承、反射或多线程,不须要生成代码或者写适配器。

其余热更新计划则是独立VM,与IL2CPP的关系实质上相当于Mono中嵌入Lua的关系。因而类型零碎不对立,为了让热更新类型可能继承AOT局部类型,须要写适配器,并且解释器中的类型不能为主工程的类型零碎所辨认。个性不残缺、开发麻烦、运行效率低下。

4.2 理论应用体验或者个性比拟

  • HybridCLR学习和应用老本简直为零。HybridCLR让IL2CPP变成全功能的Runtime,学习和应用老本简直为零,简直零侵入性。而其余计划则有大量的坑和须要躲避的规定,学习和应用老本较高,须要对原我的项目作大量革新。
  • HybridCLR能够应用所有C#的个性,而其余计划往往有大量的限度。
  • HybridCLR中能够间接反对应用和继承主工程中的类型,其余计划要写适配器或者生成代码。
  • HybridCLR中热更新局部元数据与AOT元数据无缝对立,像反射代码可能失常工作的,AOT局部也能够通过规范Reflection接口创立出热更新对象。其余计划做不到。
  • HybridCLR对多线程反对良好。像多线程、ThreadStatic、Async等等个性都是HybridCLR间接反对,其余计划除了Async个性外均难以反对。
  • HybridCLR中Unity工作流与原生简直完全相同。HybridCLR中热更新MonoBehaviour能够间接挂载在热更新资源上,并且正确工作。其余计划不行。
  • HybridCLR兼容性极高。各种第三方库只有在IL2CPP下能工作,在HybridCLR下也能失常工作。其余计划往往要大量魔改源码。
  • HybridCLR内存效率极高。HybridCLR中热更新类型与主工程的AOT类型齐全等价,占用一样多的空间。其余计划的等同类型则是假类型,不仅不能被Runtime辨认,还多占了数倍空间。
  • HybridCLR执行效率高。HybridCLR中热更新局部与主工程AOT局部交互属于IL2CPP外部交互,效率极高。而其余计划则是独立虚拟机与IL2CPP之间的效率,不仅交互麻烦还效率低下。

5. 运行性能

理论性能如实践预计,全面并且大幅胜出以后支流的xLua、Puerts、ILRuntime之类的热更新计划。

  • 根底指令(数值计算及条件跳转等指令),因为各个语言之间差距不大,因而胜出不显著。
  • 对象模型指令。因为没有跨语言交互的老本,简直是数倍到数十倍的晋升(如果指令本身耗费特地大,则差距不那么显著)。

性能测试用例来自ILRuntime提供的规范测试用例,测试项目来自Don't worry的github仓库。

测试结果显示,绝大多数测试用例都有数倍到数十倍的性能差距,差距极其夸大。唯独数值计算跟xLua有大量劣势,这是因为以后HybridCLR未对指令作任何优化,后续优化版本大多数根底指令都将有100~300%的性能晋升。


6. 内存

HybridCLR是运行时级别的实现,因为热更新的脚本,除了执行代码是以解释模式执行,其余形式跟AOT局部的类型是完全相同的,包含占用内存。

6.1 对象内存大小比照
Lua的计算规定略简单,参见《Lua数据结构和内存占用剖析》。空Table占56字节,每多一个字段至多多占32字节。

ILRuntime的类型除了Enum外对立以IlTypeInstance表白,空类型占72字节,每多一个字段至多多用16字节。如果对象中蕴含援用类型数据,则整体又至多多24字节,并且每多一个Object字段多8字节。


7. 以后稳定性情况

技术评估上目前稳定性处于Beta版本。因为HybridCLR技术原理的先进性,Bug实质上不多,稳固得十分快。曾经有一段时间未收到2020.3.33版本的Bug反馈。

  • 目前PC、Android、iOS已跑通所有单元测试,可稳固体验应用。
  • 2022.6.7第一款应用HybridCLR的Android和iOS双端休闲游戏正式上线。
  • 2022.7将有至多2款重度我的项目和2款中度游戏上线。
  • 2022年预计有几十款中重度我的项目及超过一百款轻度或者独立游戏上线。

曾经有几十个大中型游戏我的项目较完整地接入HybridCLR,并且其中一些在紧锣密鼓作上线前测试。具体参见收集的一些残缺接入的商业我的项目列表。


8. 总结

HybridCLR是一个划时代的Unity平台C#原生热更新技术,它将国内Unity开发的技术框架程度进步到新的高度,并粗浅地扭转Unity平台的开发生态。


本文内容就介绍到这里啦,更多内容能够返回UWA学堂进行浏览。