在 ejoy2d 里,我将 sprite 的结构信息储存在一组叫 sprite pack 的结构中。其中包括动画的 frame 数据,sprite 由若干部分组成,每个部分的变换矩阵,对应贴图的编码和坐标等等。
通常这些数据不会太大,所以我建议一次加载到内存就不再删除。而动态生成的 sprite 对象则直接引用这些数据,不必做引用计数。这些数据之间的交叉引用(可以像搭积木一样用很多部件构成复杂的 sprite )也不需要额外记录。但如果保存了大量的动画信息,或 sprite 是由非常多的小部件构成,数据量也可能非常可观。
在我们的 心动庄园 里,达到了数十 M 内存之多。前几天同事提到这个问题,我便动手做了一点简单的优化,居然省出了几十兆内存。
其实方法很简单。在 64bit 平台上,我将 sprite pack 结构中的指针都改成了一个 32bit 的相对 pack 头的偏移量。
在设计之初,我就是把整个 sprite pack 存放在连续单个内存块上的。为之做了一个非常简单的 bump allocator :资源打包的时候,统计整个要用的内存数量,然后在包加载时就分配出来,然后每个小对象都紧贴在一起保存。这样可以节省下大量小内存块的头,并将常规内存管理中可能出现的内存碎片率减少到零。
由于原本就是连续内存块, 把指针更替成偏移量不需要修改太多代码 。
把指针体积减少一半后,数据结构对齐也会变得更紧凑,在我们公司三个项目实际比对下来后,发现 sprite pack 占内存量大约可以减少 40% 。
做次修改后,还获得了一个额外的好处。sprite pack 在内存中已经完全不包含指针,也就是说可以随意在内存中移动了,变得和地址无关。
我们可以在打包时直接把 sprite pack 的内存块直接 dump 到文件,加载时可以绕过 import 过程。想来可以快上不少(还没有测试)。即使不能提高太多性能,我们的资源加载过程会变得和资源文件大小严格相关,也就是说,可以通过资源文件体积来准确预测加载时间,可以实现一个非常匀速准确的 loading 条了。我想这对用户体验来说,是个不错的改善。