简介

卡顿的界说有多种,依照卡住的程度排列的话,有这么几种,从ANR到Hitch,本文首要描述Hitch,Render Loop以及怎么发现和修正Hitch。

APM - iOS 卡顿监控 Hitch

Hitch

hitch代表任何时候一帧比预期的晚出现在屏幕上,hitch目标首要用在Instruments和MerticKit

如图Frame4比预期的晚出现在了屏幕上

APM - iOS 卡顿监控 Hitch

场景

APM - iOS 卡顿监控 Hitch

APP中发生画面改变的状况首要有以下3种场景

  • Srcoll
  • Animation
  • Transition

Render Loop

烘托环是一个继续的进程,从用户接触事情到供给信息给操作系统来剖析和展现最终成果

APM - iOS 卡顿监控 Hitch

这个作业与设备容量高度相关,由于这个进程在设备的刷新率下发生

APM - iOS 卡顿监控 Hitch

屏幕依照帧率刷新的时候,每一帧都会由硬件触发一个垂直信号,在这个垂直信号宣布之前每一帧需求显现的内容需求准备好,才能供给流畅的用户体会

APM - iOS 卡顿监控 Hitch

这个进程又这能够分红3个进程

  • APP处理进程
  • GPU烘托进程
  • 屏幕显现进程

APM - iOS 卡顿监控 Hitch

Double Buffering

APM - iOS 卡顿监控 Hitch

Trible Buffering

APM - iOS 卡顿监控 Hitch

这3个进程对应如下5个阶段

  • APP

    • Event Phase
    • Commit Phase
  • Render Server

    • Render Prepare
    • Render Execute
  • Display

APM - iOS 卡顿监控 Hitch

Event Phase

处理事情,修正状况

事情包括

  • Events
  • Touch
  • Networking
  • Keyboard
  • Timers

APM - iOS 卡顿监控 Hitch

事情中设置BackgroudColor,设置Bounds等,在设置好之后,setNeedsLayout会进行状况的修正和传递

Commit Phase

提交阶段布局和制作视图布局和状况

APM - iOS 卡顿监控 Hitch

Render Prepare Phase

处理layers图层,作用,和用于履行的动画

APM - iOS 卡顿监控 Hitch

Render Execute

运用GPU把图层和作用制作出来

APM - iOS 卡顿监控 Hitch

并行处理联系

APM - iOS 卡顿监控 Hitch

Hitch分类

Commit Hitch

APP提交和处理事情的时刻过长

延迟1帧,即16.67ms

APM - iOS 卡顿监控 Hitch

延迟2帧,即33.34ms

APM - iOS 卡顿监控 Hitch

Render Hitch

烘托无法及时完成

延迟1帧,即16.67ms

APM - iOS 卡顿监控 Hitch

Hitch Time Measurement

全体hitch的时刻

  • 不同的进程,设备刷新率和许多正常的帧
  • 不好比较

Hitch Time Ratio

全体hitch的时刻跟全体总计时刻的份额

  • 为不同的设备统一了总共hitch的时刻

以这30帧为例,由于hitch time是0,所以0ms/0.5s = 0 ms/s

APM - iOS 卡顿监控 Hitch

假如其中hitch time的时刻是100.02ms,那么计算下来便是200.04ms/s

APM - iOS 卡顿监控 Hitch

功能目标

  • 严重是大于等于10ms/s
  • 正告是5ms/s与10ms/s之间
  • 很好是小于5ms/s

APM - iOS 卡顿监控 Hitch

修正Hitch

Commit Phase

Commit Transaction

应用的视图结构从等候事情的状况转化到接纳事情的状况

APM - iOS 卡顿监控 Hitch

从接纳事情的状况到提交的状况

APM - iOS 卡顿监控 Hitch

APM - iOS 卡顿监控 Hitch

Layout

每个需求layout的View的layoutSubviews()办法都会被调用

Layout needed的条件

  • 移动位置(frame,bounds,transform)
  • 添加或许移除视图
  • 显式调用setNeedsLayout()
Display

每个需求更新内容的View的draw(rect:)办法都会被调用

Display needed的条件

  • 添加的view覆盖了draw(rect:)
  • 显现调用setNeedsDisplay()
Prepare
  • 假如有必要的图片解码
  • 假如有必要的图片转化
Commit

提交会做

  • 递归打包视图树
  • 发送到烘托服务者

Find hitches with Instruments

在Instruments的东西中能够找到Animation Hitches东西

APM - iOS 卡顿监控 Hitch

左侧列出了Events,Commits,Renders,GPU,Frame Lifetime和Display信息

APM - iOS 卡顿监控 Hitch

正常的状况

APM - iOS 卡顿监控 Hitch

Acceptable Latency

APM - iOS 卡顿监控 Hitch

Hitch Duration

APM - iOS 卡顿监控 Hitch

在下边的数据追踪详情中能够看到Acceptable Latency数据,Hitch Duration数据和Hitch Type类型数据

APM - iOS 卡顿监控 Hitch

能够挑选和筛选到对应Commit Phase,经过检查Thread中的调用栈来找到导致Hitch发生的原因

APM - iOS 卡顿监控 Hitch

实例中是在复用函数事情prepareForReuse()中遍历调用了addSubview()removeFromSuperview()的办法导致Hitch

APM - iOS 卡顿监控 Hitch

Recommendations

坚持视图轻量级
  • 运用CALayer的特点好于自界说draw(rect:)代码
  • 没必要的状况下,不要覆写draw(rect:)
  • 复用视图防止添加和移除的操作
  • 能够运用hidden特点
削减大开支或许重复的布局
  • 尽量运用setNeedsLayout(), 不必layoutIfNeeded()
  • 尽量运用简明的束缚
  • 递归布局的开支很大

Render Phase

Render Phase

Render Server

APM - iOS 卡顿监控 Hitch

Render Phase分为两个细分阶段

  • Render Prepare
  • Render Execute

APM - iOS 卡顿监控 Hitch

Render Prepare

把视图层级和作用拆解成一步一步的简单操作

APM - iOS 卡顿监控 Hitch

Render Execute

一步一步进行纹理叠加

APM - iOS 卡顿监控 Hitch

遇到Shadow的时候,需求拓荒新的烘托空间,离屏烘托发生了

APM - iOS 卡顿监控 Hitch

另一块空间,离屏空间烘托完成后,再复制回原烘托空间中,进行纹理叠加

APM - iOS 卡顿监控 Hitch

离屏烘托

任何时候当GPU必须拓荒另外一个区域烘托视图,然后再复制回来

几个首要原因

  • Shadowing
  • Masking
  • Rounded Rectangles
  • Visual Effects

Shadowing

APM - iOS 卡顿监控 Hitch

Masking

APM - iOS 卡顿监控 Hitch

Rounded Rectangles

APM - iOS 卡顿监控 Hitch

Visual Effects

APM - iOS 卡顿监控 Hitch

Find hitches with Instruments

能够观察到GPU Phase超时

APM - iOS 卡顿监控 Hitch

经过Xcode中Debug Navigator检查视图层级

APM - iOS 卡顿监控 Hitch

Layer的描述中标记了离屏烘托的状况

APM - iOS 卡顿监控 Hitch

Editor中Show Optimization Opportunities

APM - iOS 卡顿监控 Hitch

Xcode中的Issue Navigator会显现出视图对应的问题

APM - iOS 卡顿监控 Hitch

Recommendations

尽量运用供给的API
  • 设置shadowPath特点界说暗影形状
  • 运用cornerRadiuscornerCurve来画圆角

APM - iOS 卡顿监控 Hitch

优化mask讳饰层
  • 尽量运用maskToBounds而不是自界说mask
  • 假如内容不超过边界不要敞开maskToBounds

APM - iOS 卡顿监控 Hitch

结合XCTest

iOS14开始,在开发,Beta和出产阶段都能够运用东西套件来追踪hitch,本节结合XCTest从开发阶段排查hitch问题

  • XCTest framework供给了在单元测试和UI测试中直接纳集hitch和animation数据
  • MetricKit和Xcode Organizer能够从用户侧搜集功能数据

APM - iOS 卡顿监控 Hitch

Xcode11开始,引荐运用XCTMetrics

XCTApplicationLaunchMetric
XCTCPUMetric
XCTClockMetric
XCTMemoryMetric
XCTOSSignpostMetric
XCTStorageMetric 

Xcode11中,咱们能够运用XCTOSSignpostMetric来衡量os_signpost的距离

Xcode12中,咱们运用animation os_signpost距离,不只能够获得时长信息,还能够获得3个hitch相关信息,帧率和帧数

APM - iOS 卡顿监控 Hitch

需求搜集这些目标,有3种办法在代码中触发一个os_signpost距离,分红非动画距离和动画距离2种类型

APM - iOS 卡顿监控 Hitch

Xcode11中需求运用begin和end来检测非动画距离,在Xcode12中只需求换成运用animationBegin接口即可

os_signpost(.animationBegin, log: logHandle, name: "performAnimationInterval")
os_signpost(.end, log: logHandle, name: "performAnimationInterval")

除了自界说距离,你也能够运用关于navigation转场和滑动的预界说的UIKit的检测距离

extension XCTOSSignpostMetric {
     open class var navigationTransitionMetric: XCTMetric { get }
     open class var customNavigationTransitionMetric: XCTMetric { get }
     open class var scrollDecelerationMetric: XCTMetric { get }
     open class var scrollDraggingMetric: XCTMetric { get }
}

如示例中,点击cell后进入collectionView并滑动,计划检测scrollDeceleration子目标。丈量代码块,默许会检测5次来搜集功能数据。

// Measure scrolling animation performance using a Performance XCTest
func testScrollingAnimationPerformance() throws {
    app.launch()
    app.staticTexts["Meal Planner"].tap()
    let foodCollection = app.collectionViews.firstMatch
    measure(metrics: [XCTOSSignpostMetric.scrollDecelerationMetric]) {
        foodCollection.swipeUp(velocity: .fast)
    }
}

为了防止上滑5次显现5次不同内容,能够每次在运转前重置应用的状况,咱们能够运用XCTMeasureOptions来让咱们的检测代码块知道咱们会手动中止检测搜集,把参数传递到丈量代码块中

func testScrollingAnimationPerformance() throws {
    app.launch()
    app.staticTexts["Meal Planner"].tap()
    let foodCollection = app.collectionViews.firstMatch
    let measureOptions = XCTMeasureOptions()
    measureOptions.invocationOptions = [.manuallyStop]
    measure(metrics: [XCTOSSignpostMetric.scrollDecelerationMetric],
            options: measureOptions) {
        foodCollection.swipeUp(velocity: .fast)
        stopMeasuring()
        foodCollection.swipeDown(velocity: .fast)
    }
}

但是在运转丈量代码之前,咱们需求在test scheme中修正一些装备消除对功能检测的影响

  • 挑选release build configuration
  • 封闭the debugger
  • 封闭automatic screenshot
  • 封闭code coverage
  • 封闭所有diagnostic options

APM - iOS 卡顿监控 Hitch

APM - iOS 卡顿监控 Hitch

APM - iOS 卡顿监控 Hitch

APM - iOS 卡顿监控 Hitch

跑完测试能够得到以下数据,挑选到Hitch Ratio Rate目标数据

APM - iOS 卡顿监控 Hitch

5次丈量数据

APM - iOS 卡顿监控 Hitch

平均值1.2ms/s

APM - iOS 卡顿监控 Hitch

能够把这个数据设置为基准数据,用于跟后续的功能数据做比照

APM - iOS 卡顿监控 Hitch

检查Number of hitches,承认此刻无hitches

APM - iOS 卡顿监控 Hitch

添加了图片加载等代码后,再检查Number of hitches,发现有hitches发生

APM - iOS 卡顿监控 Hitch

问题出现在scaleAspectFit办法的调用中,主线程在负责烘托UI剩余部分的时候,该办法中又在重新制作图片

APM - iOS 卡顿监控 Hitch

能够经过设置Core Animation供给的setContentMode来处理图片的重绘,运用现有的图片像素,削减主线程作业量

APM - iOS 卡顿监控 Hitch

再启动XCTest发现问题免除

APM - iOS 卡顿监控 Hitch

引证

Explore UI animation hitches and the render loop

Find and fix hitches in the commit phase

Demystify and eliminate hitches in the render phase

Eliminate animation hitches with XCTest

Apple Document XCTMetric