新增了Extreme 级别的托管代码剪裁策略,基于对MonoBehaviour和ScriptableObject类细致地静态分析,尽可能多的剔除代码。 托管代码精简可以缩减构建后的托管程序集的大小,进而减少wasm包的大小,因此对于il2cpp backend优化效果较明显。 对于.net 8 backend 则作用不大,因为托管程序集本身体积就不大,缩减的收益不大。
Managed Code剔除会对代码进行静态分析。首先依据(root marking rules)标记root类型、方法、属性等,然后再依据(dependency marking rules)去标记这些root的依赖。接下来未被标记的代码会被剔除。新增的Extreme Level,使用了更加激进的标记规则,与High Level对比如下:
High及更低级别的裁剪都会保留用户代码中所有的MonoBehaviour和ScriptableObject类。Extreme只保留被实际用到(通过Scene或者AssetBundle引用)的MonoBehaviour和ScriptableObject类。
在依赖标注上,High及更低级别的裁剪会保留MonoBehaviour和ScriptableObject类的所有成员。Extreme模式则只会保留Unity Event Functions和实际被使用到的方法
在4个不同规模的项目上实测的剔除效果对比如下:
与High Level一样,基于静态分析的Extreme模式也存在误剔除的风险。为此我们新增了Dryrun模式,可帮助开发者快速定位与收集误剔除的方法。
新增Log Stripped Method for Debug模式,该模式下托管代码不会真的被剔除。待剔除的方法将被插入警告打印代码。在运行时,当这些待剔除的方法被调用时,Console中会打印出警告信息,这可以帮助开发者快速定位与收集误剔除的方法。
例如微信SDK中WXProfileStatsScript类型是通过AddComponent(Type)挂载的,输入的Type类型变量是在它处赋值的,无法在编译时确定,所以导致该类型无法被保留。下图为对应的警告:
补充信息:AddComponent<Type >() 和 AddComponent(typeof(XXX)) 模式的调用则可以通过代码上下文来识别,因此可以被保留。
另一个案例使用了LitJson这个json解析库,该库通过反射查找类型的public属性。在做Managed Code剔除操作时,通过静态分析无法得知类型的哪些public属性将被反射调用,导致这些属性的get/set方法都被误剔除了。
注意,只有il2cpp backend才支持此模式。.net 8 backend 下此模式不显示。