首页 > 电竞游戏 > 详情

定制容器减小 Unity 游戏在 iOS 上二进制文件大小

过年在家闲来无事,写了点对 Unity 游戏在 iOS 上二进制文件大小优化的东西,源码在 github,在这简单写一下思路。

.NET 泛型相关的一些研究赵百万写过很多,包括机制原理、奇技淫巧、性能比较等,感兴趣的可以去读他的博客。与 C++ 模板泛型不同,.NET 的泛型编译成 IL 后不会特化成针对某一特定类型的实现,其类型仍然是待填充的,但这仅限于支持JIT的 .NET 环境。在 iOS 上,Unity (Mono) 会把 IL 通过 AOT 方式编译成汇编指令甩到 XCode 工程里,也就是说类似 Dictionary 这样的泛型容器会提前特化变成汇编指令,进一步编译链接进二进制文件,最糟糕的是即使没被使用到的方法也会编译进去,而一旦类型参数种类多了小小的容器占用的二进制文件磁盘空间也是相当可观的。一个稍大一点的项目,AOT 编译出来的 .a 文件有可能超出 XCode 能处理的大小限度。最容易想到的优化方法是使用 ArrayList、Hashtable 等容器替代 List、Dictionary 等泛型容器,优化效果相当明显,但这样损失了一部分泛型容器使用的便利性,且不方便老代码移植。于是有了我进一步的想法,自己定义泛型容器,实现对 ArrayList、Hashtable 等容器的简单封装,类本身只实现最基本的成员属性和方法,其他方法一律通过扩展方法实现。既保留了一定的泛型容器调用风格,还能达到使用了哪个方法就特化哪个方法的目的,最大限度的省略掉不必要的磁盘空间占用。

可以通过查看 Assembly-CSharp.dll.s 之类的 AOT 编译后的汇编文件检查优化效果。以 Dictionary 为例,优化的结果有一个简单对比,计量单位为汇编代码行:

Dictionary:泛型实现 0,特化实现 3069;

LiteMap:泛型实现 173,特化实现 133。

可见虽然自定义的实现虽然需要消耗一定空间,但“用到什么特化什么”的方式的确能在大多数应用场景缩减占用空间。

关于性能,GC 的影响远比是否使用泛型大,数据结构(及其实现)的选择在这不做过多讨论。目前只写了 LiteList 替代 List,LiteMap 替代 Dictionary,如果使用过程中发现有我没加上的方法(比如LINQ族我就暂时没考虑加入)自行补充到扩展方法里也是很容易的。
来源:扑来树袋熊的博
QR code