iOS 底层原理39:Instruments系列(三)Anima

2022-08-10  本文已影响0人  Style_月月

iOS 底层原理 文章汇总

引言

从Xcode12开始,Instrument更新了UI,新增了一个模板 Animation Hitches 用来分析用户的 app 中的卡顿,并去除了 Core Animation 检测方式。在 iPhone13Pro 之前 iPhone 屏幕最高刷新频率仍为 60 HZ,而在支持 PromotionDisplay 的设备上帧率可调整至 120 帧,并且会根据当前用户手势和设备状态进行动态调整。此时再继续使用帧率来判断性能的好坏及流畅度将会是一个错误的选择。所以 Animation Hitches 主要用于代替帧率检测,并且提出 卡顿时间比(Hitch Time Ratio) 的概念用于替代 FPS。

在 Hitch 提出之前,都是借助FPS (Frames Per Second 帧率),即每秒绘制帧的数量来衡量页面是否卡顿

苹果于 20 年的 Session 中提出了 Hitch 的概念,用以衡量滑动时的卡顿情况。Hitch 指的是 卡顿时间(一帧延后出现的时间,ms)/ 总时间(一般是 1 秒),简单来说 卡顿时间比就是一个区间内的总卡顿时间除以它的持续时间。

卡顿

如上图所示,当手指在屏幕上滑动时,滚动视图会随着手势做出响应,如果一帧一帧来看,就是每一帧都对应手指位置的变化。当卡顿发生时,某一帧没有跟随手指变化,导致到下一帧时,产生跳跃,打破了用户和屏幕内容的视觉连接感。图中卡顿产生的原因就是第三帧重复了,主要是因为第四帧的延迟导致了第三帧占用了两帧的时间,给用户看到的就是卡顿掉帧的现象。

RenderLoop

视图渲染流程

在每一帧显示的过程中,大概可以分为3个阶段,如下所示


渲染流程


1、AppRender server 阶段需要在下一个 VSYNC 到来之前完成
2、这里运用了 双缓存区 + 垂直同步机制,主要是用于解决 屏幕撕裂现象
3、整个渲染阶段可以分为5个阶段:

渲染阶段细分
  • 阶段 1 + 2:App(Event + Commit)
  • 阶段 3 + 4:Render server(Prepare + Excute)
  • 阶段 5:Display

4、整体渲染流程如下

  • Event(事件阶段)通过 touch、timer 等事件决定用户界面是否需要改变
  • Commit(提交阶段),App会向 Render server(渲染服务器)提交渲染命令
  • Prepare (准备阶段)会为 GPU 的绘制组好准备
  • Excute(执行阶段)会由 GPU 将用户界面的图像绘制出来
  • Display(显示阶段)会将缓冲区的帧交换到屏幕上显示

下面以一个带有阴影的渲染图形为例,通过观察 RenderLoop 中每一帧所做的工作,来分别介绍不同阶段

App 阶段

App 阶段包含 2 个阶段,分别是 Event 、Commit。其中Commit 又分为 4 个子阶段,分别是

阶段 1:Event 事件阶段

阶段 2: Commit 提交阶段

提交阶段还可以细分,主要分为4个子阶段


提交阶段-Commit
Layout - 布局阶段

在布局阶段,layoutSubviews 会被所有需要布局的 View 调用。例如视图布局(frame、bounds、tranform等)、增加/移除视图、直接调用 setNeedsLayout/layoutIfNeesed 等

注:这些操作并不是立即执行,系统会合并布局请求,在 Runloop 休眠前进行统一处理

Display - 显示阶段
drawRect
Prepare - 准备阶段

在准备阶段,主要是将还未解码的图像进行进一步解码,这也是我们需要优化的点(即优化图片主线程解码操作)。

因为对于每个解码的图像,App可能会持续存在大量的内存分配(与图像大小成正比),当App占用内存越来越多时,操作系统就会开始压缩物理内存(physical memory),这整个过程都需要CPU参与,所以除了App会使用CPU,还增加了无法控制的全局 CPU 使用率,导致App消耗更多的物理内存,此时操作系统会终止低优先级的后台进程,从而释放更多的物理内存。但设备的物理内存始终是有限的,当App对内存的消耗达到了临界值时,该App进程就会被操作系统终止,这就是常说的大图导致的OOM

若某个图像的颜色格式 GPU 无法直接使用,也会在这一步进行格式转换。这就要求对该图像进行 copy 操作,而不是直接使用指针,这样会耗时更长及占用更多的内存。

Commit - 提交阶段

在提交阶段,视图树会被递归打包,并发送到 Render Server中,所以当视图图层较复杂时,这个过程的耗时也会相对较长,这也是我们经常提及的优化点(即尽量减轻视图层级结构,不要跟套娃似的,无穷无尽)。

Render Server 阶段

Render Server(渲染服务器)主要负责将图层树转换为真正显示的图像,分为两个子阶段


Render Server 阶段

阶段 3:Prepare 准备阶段

阶段 4:Excute 执行阶段

在执行阶段,主要是由 GPU 根据前面 prepare 阶段准备好的图层树进行顶点着色、形状装配、几何着色、光栅化、片段着色与图层混合。一旦 GPU 执行完会将渲染好的图像放入帧缓存区中等待下一个 VSYNC 的到来并交换到屏幕上进行显示。

执行阶段-Excute

Display 阶段

阶段 5:Display 显示阶段

在显示阶段,主要是将帧缓存区中的内容交换到显示器上进行最终显示

视图渲染流程总结

想了解离屏渲染的同学请阅读# 屏幕卡顿 及 iOS中的渲染流程解析

卡顿类型

通过了解了视图渲染的工作流程,其主要工作是在App 和 Render Server 中进行的,所以总共涉及两种卡顿类型

提交卡顿

如何避免提交卡顿?

主要有以下几种方式

下面进行详细说明

保持视图的轻量

针对于文本、图片等原本就在 CPU 上进行绘制的系统控件,我们可以尝试使用其更底层线程安全的 CoreGraphics 能力,比如 TextKit、CoreText 等搭配多线程异步绘制减轻主线程压力。

避免复杂布局

layoutIfNeeded 会消耗当前事务的生命周期也会造成卡顿,大多数时候你可以等到下一次 Runloop 执行时再更新你的布局。

合理运用多线程能力

渲染卡顿

如何避免渲染卡顿?

Prepare 阶段对卡顿的影响较少,主要还是在 Excute 阶段的离屏渲染。针对离屏渲染的优化,请阅读
# iOS 常见触发离屏渲染场景及优化方案总结

使用 masksToBounds 遮蔽为矩形圆角矩形或椭圆形的性能比自定义蒙版图层好得多

它会对一块图层进行光栅化操作并进行缓存。若针对于需要频繁刷新的图层使用该属性反而对性能有着负面影响。

下面就主要介绍 Instrument 中 Animation Hitches 的使用

使用

参考文章

Tech Talk - Hitches 与 渲染循环
iOS 性能检测新方式——AnimationHitches
Animation Hitches in iOS Development
Explore UI animation hitches and the render loop - Tech Talks - Videos - Apple Developer
Find and fix hitches in the commit phase - Tech Talks - Videos
iOS 性能分析-阿里
iOS 高刷屏监控 + 优化:从理论到实践全面解析 -字节
WWDC20 10077 - 使用 XCTest 消除动画卡顿
# APP 性能优化终极求生指南
# iOS性能优化之界面卡顿监测
# iOS 高刷屏监控 + 优化:从理论到实践全面解析
## 精确定位页面滑动帧率瓶颈及优化参考

上一篇 下一篇

猜你喜欢

热点阅读