作者|徐霜晴

引言

在启动优化时,我们常常通过增加并发的方式来减轻主线程的耗时。而在 iOS 中,GCD 是并发编程最常用的框架。增加并发是否是启动优化的良策?开发者适合选用哪个优先级的 GCD 队列?本文将结合飞书启动优化,给出选取 GCD 队列的最佳实践,也提供针对低端机的启动优化思路。

应用此思路,我们在未修改飞书业务逻辑的情况下,在飞书低端机上,取得了不错的用户体验收益:首屏展示时间优化 100ms,消息列表首刷时间优化 1500ms。

低端机的特性

通过 Instruments 的 App Launch 功能,我们能看到 App 启动时的线程状态、Time Profiler 等信息。其中,我们发现不同设备在启动时的表现有很大差异。​​

以 iPhone 7p(低端)和 iPhone 12(高端)举例,它们的设备参数分别为:

设备

CPU 参数

实际核数

ProcessInfo.processInfo.activeProcessorCount

跑满的 CPU 占比(Xcode 测试)

iPhone 7p

A10 芯片[1],2 高性能 + 2 低功耗,但是只有 2 核能同时工作

2

200%

iPhone 12

A14 芯片[2],2 高性能 + 4 低功耗

6

600%

启动飞书时,我们通过 Instruments 观察两个设备的线程状态,经过统计发现,iPhone 7p 上,主线程 Preempted 和 Runnable 状态的占比高达 21%。Instruments 的图中能看到主线程大片被抢占。

图片

一个典型的局部,能看到主线程是 preempted 状态,CPU0 在执行其他进程,CPU1 在执行 GCD 线程。

而 iPhone 12,主线程 Preempted 和 Runnable 状态占比则只占 1%从这里我们能发现:对低端机来说,CPU 已经成为了启动的瓶颈,“增大并发”已不是一个万能的启动优化措施,而想办法减少其他线程对主线程的抢占,可能会是优化思路。

GCD queue 对主线程的抢占评测

为了评估“减少其他线程对主线程的抢占”是否是一个可行的优化思路,我们首先需要弄明白,主线程被抢占的程度会有多大?

我们可以使用 Demo 制造一些极端场景,了解极端场景下,主线程有多少比例会被其他线程抢占,因此有了如下 Demo 实验:

实验组1:

  • 异步线程 QoS:DispatchQoS.userInteractive

代码:

for _ in 1...100 {
let queue = DispatchQueue.init(label: "serialQueue", qos: .userInteractive)
queue.async {
while true {
}
}
}
while true {
}