
一、背景
优酷 iPhone 端还有很多低端机用户,为进一步提升用户体验和用户活跃度,优酷主客团队启动了 iPhone 首页频道页性能优化项目,主要包括架构优化和 UI 渲染优化。
本文主要介绍渲染优化部分的技术经验。渲染优化主要采用了异步渲染和对象预缓存方式,整体效果较好。
二、优酷首页性能现状
优酷 iPhone 首页频道页经过架构改造、新视觉改版、业务迭代,积累了较多的性能压力,新视觉改版大量使用了圆角、阴影、渐变等性能消耗较大的 UI 样式,首页滑动性能有所下降,通过 Instrument 测得 iPhone6 plus 上首页帧率平均 44 帧左右,用户体验尚不理想。
性能问题分析
1)耗时方法分析
在首页滑动时,主要调用的是相关 cell 的 query view、reuse view 方法,打印相关方法的主线程耗时可以发现,query view 方法耗时一般不高,但在没有可重用 cell 需要新建时耗时会增高很多,导致明显的卡顿。

iPhone6 plus 优酷首页 query view 方法主线程耗时
reuse view 方法平均每个坑位耗时 3 毫秒左右,而一个组件往往包含多个坑位,总体耗时超过 10ms,造成滑动掉帧。

iPhone6 plus 优酷首页 reuse view 方法主线程耗时
2)UI 卡片代码分析
现有 UI 卡片的代码主要分为三个部分:初始化、绑定数据、事件处理。其中对滑动帧率影响较大的主要是初始化和绑定数据部分,也对应于上面 query view 和 reuse view 方法的耗时。 UI 卡片需要在主线程创建,分配内存、初始化等操作耗时较大,尤其控件较多时新建卡片易造成主线程卡顿;卡片重用池稳定时不再新建卡片,此时性能主要消耗在卡片 reuse 绑定数据时,布局变换、文本 size 计算、图层混合、图片文本渲染等高耗性能操作是造成卡顿的主要原因,从上面 Instrument 测试结果看,CPU 占用时有达到甚至超过 100%的情况,GPU 占用最高 76% 左右,必须降低 CPU、GPU 占用。

优化前卡片复用时序
性能解决方案
对于卡片初始化的耗时可以通过预创建卡片的方式优化,在启动后闲时创建页面主要卡片并缓存备用,以减少滑动过程中创建新卡片。
对于卡片复用时的高耗性能操作,通过简化 UI 代码、文本高度预计算、改用 Layer 渲染、减少图层混合等方式优化后,首页低端机帧率已由 38.4 帧提升到 45 帧左右,提升 15%,但效果还不够理想,滑动卡顿、掉帧还比较明显,CPU 占用峰值在低端机上依然达到 100%,说明复用任务即使优化后也已达到 CPU 性能极限,此时继续在主线程上优化效果不大,可以采用子线程复用结合异步渲染的方式,将复用任务分拆到子线程当中,减少主线程拥塞。为此我们开发了 ModelUI 渲染框架以实现相关性能优化方案。
三、ModelUI 渲染框架
ModelUI 简介
Model 渲染框架主要包含三个部分:
1)线程安全的 UI Model 层
由于系统 UIKit 只能在主线程操作,为了实现子线程拆分等,ModelUI 仿照 UIKit 抽象了线程安全的 UI Model,如 LabelModel 、 ImageModel 等,以封装渲染参数、渲染内容,供 ModelContainer 管理、渲染,UI Model 提供了统一的类似 UIKit 的使用接口,并对齐了优酷在 UIKit 的相关扩展方法,不影响现有开发习惯,也方便迁移现有 UI 卡片,大幅减少了迁移工作。
2)四种渲染方式

iOS 绘制渲染框架图
iOS 系统在底层硬件之上提供了三层渲染 API,分别封装为 CoreGraphics、QuartzCore(CoreAnimation、LayerKit)和 UIKit 三个库,其他常见的第三方 UI 库主要是 YYKit、 AsyncDisplayKit(Texture),YYKit 对 UIKit 部分控件进行了优化,AsyncDisplayKit 是 Facebook 基于 CoreGraphics 开发的异步渲染框架,几种渲染方式对比如下:

四种渲染方式各有短长,为满足不同业务需求,ModelUI 提供了四种渲染方式:已被广泛使用的 UIKit 渲染,能力完善且性能有所改善的 YYKit 渲染,支持动画性能较好的 Layer 渲染,以及性能最优的异步渲染,其中由于 AsyncDisplayKit 过重,侵入比较大,ModelUI 开发了自己的轻量级异步渲染,以更好的配合 UI Model 使用。
每个 UI Model 默认使用异步渲染,但异步渲染无法添加交互动画,不能响应点击事件,对于需要交互动画但不需响应事件的 UI Model 可使用 Layer 渲染,对于两者都需要的 UI Model 可使用 YYKit 或 UIKit 渲染。ModelUI 支持不同渲染方式混合使用,不同 UI 元素可根据需要标记为不同的渲染方式,以在满足业务需求、保证使用体验的情况下尽量提高渲染性能。
ModelUI 渲染方式简介:
UIKit 渲染通过系统原生 UIKit 控件渲染卡片,使用 UIKit 渲染时 ModelUI 相当于提供了一个线程安全的 UIKit 接口,在子线程完成数据绑定、布局计算后 ModelUI 会在主线程完成渲染,使用 UIKit 渲染时只减少了部分主线程耗时,性能提升有限。
YYKit 渲染使用开源项目 YYKit 控件渲染 UI,YYKit 较 UIKit 性能有所提升,结合子线程数据绑定、布局计算,YYKit 渲染较 UIKit 渲染性能略有提升但整体不大。
Layer 渲染通过系统 QuartzCore 库中的 CALayer、CATextLayer 等渲染 UI,Layer 渲染不响应点击事件,较 UIKit 渲染、YYKit 渲染更为轻量,显著减少图层混合等 GPU 渲染负担,性能较 UIKit、YYKit 进一步提升,首页使用 Layer 渲染后卡顿明显减少,但还有滑动掉帧、抖动等,性能尚不理想。
异步渲染使用系统 CoreGraphics 中的 C 语言绘制命令,在子线程将各 UI Model 绘制为一张图片,最后由 GPU 直接渲染结果图片到屏幕上,不需要 GPU 渲染每个控件,渲染结束后 ModelUI 使用 LRU 策略缓存结果图片,减少重复渲染,UI 卡片可以指定缓存 Id,在绑定重复数据时可直接复用上次渲染结果,进一步减少渲染耗时,同时解决了刷新闪烁问题。异步渲染极大减少了 GPU 渲染负担,充分发挥了硬件性能,性能提升较大,但异步渲染不能响应点击事件且只适用于静态 UI 卡片。

3)布局渲染线程池
ModelUI 维护了一个线程池用以布局和渲染,减少新线程开辟耗时,不同卡片可在多个线程同时布局、渲染,进一步挖掘多核硬件性能。
经过任务拆分、渲染方式优化后,主线程拥塞基本解决, GPU 负担大幅减少,从而降低 CPU、GPU 占用峰值,减少滑动卡顿,提高用户体验。

优化后卡片复用时序
不足
1)ModelUI 采用子线程渲染,在快速滑动需要大量渲染时限于 CPU 性能部分卡片渲染需要等待,导致卡片延时显示,但相比于卡顿,延时较小可以接受;
2)ModelUI 异步渲染方式只能展示静态 UI,对于 Gif 等场景需要结合原生控件,而切换逻辑需要业务层处理,对业务层有少量侵入;
3)ModelUI 异步渲染方式页面刷新时会有闪烁问题,主要由于数据更新时 UI 没有同步更新,需要在数据更新时清除上次渲染结果,结合渲染复用可以解决此问题,需要业务层处理相关逻辑。
四、优化效果
首页卡片使用 ModelUI 渲染后体验有较大提升,滑动时已无明显卡顿或掉帧,帧率提升 18% 左右,reuse 方法主线程耗时降低一半左右,图层混合、离屏渲染、像素对齐等 GPU 耗时问题也基本解决,GPU 占用降低 36%左右,整体效果较好。
GPU 渲染负担大幅减少

iPhone6 plus 首页接入 ModelUI 后图层混合、离屏渲染等问题大部分得到解决
Instrument 测试帧率显著提升
通过 Instrument 可以看到,帧率提升 18%左右,平均 GPU 占用降低近一半,虽然最低帧数据还不够理想,但结合架构层、网络层、数据中心优化后整体效果比较满意。

iPhone6 plus 优酷首页接入 ModelUI 后从轮播图滑动到单 Feed 区帧率统计
性能测试平台测试整体效果较好
通过优酷自动化测试平台,测试 iPhone6 上综合架构优化版首页帧率从 55 帧提高到 58 帧,最低帧由 26 帧提升到 48 帧。由于测试帧率原理、设备性能不同测试数据有所差异,整体可以作为改善参考。

优酷自动化测试平台 iPhone6 上优酷首页优化前后帧率曲线对比
五、结语
性能是 APP 核心指标之一,而随着业务迭代的增加,UI 越来越复杂,难免要使用一些高耗时的 UI 效果,如文本 size 计算、圆角阴影渐变等,导致 APP 性能逐渐下降,而开发同学编码习惯也不同,难以形成有效的性能机制,而 ModelUI 提供了一种性能卡口,将耗时任务拆分到子线程,减少主线程拥塞,从而确保了 APP 的渲染性能,最终充分发挥现有硬件的性能,以提供最好的用户体验。
作者 | 阿里文娱高级无线开发工程师 氚雨
评论