DHAO 文件
dhao文件是DHE技术的核心概念。dhao文件中包含了离线计算好的最新的热更新dll中变化的类型和函数的信息,运行时直接根据dhao文件中信息决定执行某个热更新函数时,应该使用最新的解释版本还是直接调用原始的AOT函数。 离线计算好的dhao文件对于DHE技术极为关键,如果没有dhao文件,需要额外携带原始AOT dll,并且计算函数变化的代价极其高昂。
通过对比最新的热更新dll与打包时生成的AOT dll,离线计算出变化的类型与函数,保存成dhao文件。因此DHE机制要正常工作,必须依赖于dhao文件的正确性,而dhao文件的正确性 则依赖精确提供最新的热更新dll和打包时生成的AOT dll。
HybridCLR.Editor.DHE.BuildUtils
提供了多个生成dhao文件相关的函数。
函数名 | 描述 |
---|---|
GenerateDHAODatas | 生成热更新包(即有代码发生改变时)的dhao文件 |
EncryptDllAndGenerateDHAODatas | 当开启初级代码加固时,生成热更新包(即有代码发生改变时)加密后的dll和dhao文件 |
标记变化的函数信息
目前已经可以自动通过对比最新的热更新dll和打包时生成的aot dll,计算出变化的函数,绝大多数情况下不需要手动操作。但事实上并不存在完美的能够判断逻辑等价性的代码,
工具只是简单地一一对比IL来判断等价性。有时候可能发生函数是等价的但IL发生变化的情况(如调换了两行不相关代码的顺序),则会被判定为函数发生变换而切到解释执行。
如果发生了这种情况,并且对该函数有极其严苛的性能要求,开发者可以手动使用UnchangedAttribute特性标注函数的变化性。
[Unchanged]
和[Unchanged(true)]
表示未变化,[Unchanged(false)]
表示变化,未标记特性则由工具自动计算。
错误地将未变化函数标记为已变化,不会影响运行的正确性,只会影响性能。就算将所有热更新函数都标记为变化,也能正常运行。但错误地将变化函数标记为未变化,不仅会导致运行逻辑出错, 严重情况下还会导致崩溃!
除非特殊情况以及你是有经验的专家,不要手动标记。因为编译器经常生成一些隐藏类或字段,这些类名并不是稳定的。表面看起来一样的C#代码,实际生成的代码未必一样,强行标注为[Unchanged]
会导致不正确的运行逻辑,甚至崩溃。
合并dhao文件
基于相同或者相似源码发布的游戏包,它们的原始dhe程序集在不同平台之间仅有微小区分,热更新时生成的dhao文件也只有微小区别。 导致需要为每个平台都计算单独的dhao文件(即使是相同平台,由于代码编译的不稳定性,生成的原始dll也可能有微小区别),这导致维护dhao工作变得复杂易错。当多个新旧游戏包同时存在时,这个问题尤为严重。 可以考虑将同一个dhe程序集对应的多个平台的dhao文件合并,不影响运行正确性的同时对性能的影响也很小。
我们提供了HybridCLR.Editor.DHE.BuildUtil::MergeDHAOFiles
函数实现合并dhao文件目标。
注意,带校验的工作流无法使用合并dhao文件的方式,因为带校验的工作流会检查原始dll的md5码,这个肯定是不匹配的。
注意事项
外部dll引发的计算dhao的结果有巨量差异
如果有外部dll被标记为DHE程序集,由于外部dll打包时会被裁剪,而计算dhao文件时,取的是原始的外部dll,导致产生巨量的差异,这不是所期望的。解决办法有几个:
- 在link.xml里
<assembly fullname="YourExternDll" preserve="all"/>
完全保留外部dll - 不用最新的热更新dll去计算差异,而是使用最新代码重新打包时生成的aot dll去计算差异