问题背景
1 现有热更方案的开发效率、性能没有到达极限,还有提升的空间
2 ios多平台政策导致热更新受限问题,ios禁止jit。根据我查找的资料,ios的代码段启动的时候就确定了,不能增加新的代码段。IOS封了内存(或者堆)的可执行权限,相当于变相的封锁了JIT这种编译方式,即机器码被禁止映射到内存。所以不能运行时生成机器码执行。
目标
研发一个使用c#的热更新方案,既有开发效率又有运行效率
原理
灵感来源于:mixed mode execution
unity的il2cpp runtime额外提供了interpreter模块,将它们由纯AOT运行时改造为AOT + Interpreter混合运行方式
简明说法:c#代码通过il2cpp转换为c++代码,此时接入c++实现的HybridCLR,完美契合
HybridCLR做了以下几点工作
实现了一个高效的元数据(dll)解析库
改造了元数据管理模块,实现了元数据的动态注册
实现了一个IL指令集到自定义的寄存器指令集的compiler
实现了一个高效的寄存器解释器
额外提供大量的instinct函数,提升解释器性能
概念和术语
Hybrid
是一个热更方案,实现方式为AOT+Interpreter 混合runtime,进而原生支持动态加载assembly
寄存器解释器
解释器就是HybridCLR实现的解释执行自定义指令集的虚拟机。和基于栈的解释器相对,显著区别是有没有大量依赖出栈、入栈来操作完成运算。
使用寄存器式虚拟机没有基于栈的虚拟机在拷贝数据而使用的大量的出入栈(push/pop)指令。同时指令更紧凑更简洁。但是由于显示指定了操作数,所以基于寄存器的代码会比基于栈的代码要大,但是由于指令数量的减少,其实没有大多少。
AOT代码
unity打包时就进包的代码
AOT
AOT 即提前编译,可以生成被直接执行的二进制代码
热更新代码
动态加载执行的代码
instinct函数
build-in function,内置函数,指编译器实现的函数,这里指这些函数对效率帮助很大,喊666吧
JIT
Just-in-time compilation,缩写为JIT;又译及时编译、实时编译。通俗的说是,源代码、中间代码直接编译成机器码执行
Differential Hybrid Execution(DHE) 差分混合执行技术
可以对AOT dll任意增删改,会智能地让变化或者新增的类和函数以interpreter模式运行,但未改动的类和函数以AOT方式运行,让热更新的游戏逻辑的运行性能基本达到原生AOT的水平。
将标记为DHE的程序集也打入主包中,运行后再加载最新的热更新dll。执行过程中,调用某个DHE程序集的函数时,如果函数未发生变化,则直接调用原生的AOT实现,否则以解释方式执行最新的代码。
通俗的说:和打包进主包的代码对比,不一样就用热更代码,因为大部分都不会改,所以整体效率接近原生
注意事项
代码剪裁
Assets/link.xml,这个是不能热更的,未来需要使用的类型一定要预留在配置文件里面
使用HybridCLR.Editor.HotUpdate.MissingMetadataChecker检查是否使用了被剪裁的代码
这块一定要注意!!!如果link.xml里没有预留,后面想用只能换主包了!
问一问
热更新流程
1 按照依赖加载所有的Dll
2 执行热更代码,这里有多种方式,看文档:加载和运行 | HybridCLR
热更新资源和热更新脚本的加载顺序
先加载代码再加载资源
热更新的Dll里面能不能增加新的类和方法?
可以
能增加新的热更程序集吗?
可以
参考资料
MonoBehaviour支持 | HybridCLR
基于栈实现的虚拟机 & 基于寄存器实现虚拟机 - 简书