[Yoga 渲染引擎的原始内存占用]
为了优化 WebAssembly 的内存占用,我们调整了编译方式,将 Yoga 编译成独立的 wasm 文件,这种方式相比 asm.js 支持动态内存分配。同时结合聊天窗口的消息卸载策略,经过不断的测试调优,在既要保证初始内存较少又要尽可能避免内存爆发式增长带来的性能损耗的前提下,我们把 WebAssembly 的初始内存分配优化到 2M,再加上对象共享、享元模式等策略,WebWorker 的内存占用有了非常可观的优化。
[QQ 结构化消息渲染引擎优化前后的内存占用对比]
复杂的聊天消息虽然是必不可少的功能,但是实际的消息量还是远少于普通的图文消息,因此在保证用户体验的前提下,在合适的条件下适时销毁 WebWorker 是一个合理的策略,而随着 WebWorker 被销毁这个线程所占用的内存也能被完全释放。
3)性能与体验平衡
- Lottie 及动画方案选型
超级表情采用 Lottie 动画技术方案,有高清高帧率高质量特点,但同时也为我们带来了渲染的高成本。为了保证 Lottie 的高帧率和减少 CPU 占用,我们缓存了 Lottie 渲染器生成的动画帧,内存消耗成为了首要问题。
[QQ Lottie 动画示例]
对其进行定量分析,超级表情 Lottie 资源继承自手机 QQ,尺寸是 512 × 512,动画帧以 int8 数组存储,所以一帧动画为 512 × 512 × 4 / 1024 bit= 1024 Kb = 1Mb,一个普通大小的超级表情,例如庆祝表情,有 160 帧动画,依据缓存 9/10 帧动画的策略,庆祝表情会占用 144Mb 内存,虽然是可回收的,但也无疑是巨大的内存消耗。关注到 Lottie 渲染的内存消耗后,我们主要从以下 2 步入手:
- 缓存的动画帧尺寸:桌面端 lottie 渲染大小为 120 × 120,考虑到需要保持 Lottie 动画的高质量,缓存的动画帧尺寸调整为实际尺寸大小的两倍,即 240 × 240,降低内存消耗 72%。经设计确认,清淅度上也没有明显的差异;
- 缓存策略:缓存 9/10 的动画帧减少到缓存 3/4,降低内存消耗 35%,而且调整之后帧率还能得到保障;
通过以上 2 步,一共降低内存消耗 81.8%,庆祝表情从 144 Mb 降低到 35 Mb。
最后,旧策略对于渲染过且暂时不用的 Lottie 表情,会 buffer 它的第一帧,总共 31 个 Lottie 表情:2.3k * 31 = 7M(最多),经评估之后,我们暂时也拿掉了该策略。
[QQ Lottie 动画缓存首帧对内存的影响]
另外,桌面 QQ 左侧导航栏目,为了与移动端统一体验,使用 Lottie 动画来实现,从 memory 面板来看, 4 个 icon 导航条会占用约 6M 的内存。改用 CSS 实现,不仅效果与 Lottie 的几乎一致,而且这 6M 的内存占用就完全省掉了。