Ced682a5b89c99566b836b309e79ece4
WWDC 2018:App 通往高性能之路的有效实践

Session 407 - Practical Approaches to Great App Performance

简介

在本 Session 中,苹果的工程师 Jon Hess 和 Matthieu Lucas 分享了如何针对 App 性能进行有效的优化,以及如何在设计上针对性能进行优化的理论与实践。其中 Xcode 团队工程师 Jon Hess 现场演示了如何在 Instrument 工具的帮助下进行性能优化,而另一位苹果的工程师 Matthieu Lucas 则分享了官方的 Photos 应用在性能优化方面相关的经验。,


第一部分:性能优化理论基础

性能检测 (Measure) 是性能优化的基础。想要解决任何的性能问题,都离不开性能检测。如同我们在解决一个 BUG 的时候一样 —— 解决一个 BUG 需要经过重现、调试(Debug)、修改、尝试重现……

如何解决一个 BUG

如何解决一个 BUG

解决一个性能问题同样需要 4 个步骤,唯一不同的地方在于解决性能问题我们会以性能检测代替调试

如何解决一个性能问题

如何解决一个性能问题

性能问题的类型

我们关于性能相关的工作大多都可以以不同的场景来划分。不同的场景会影响解决性能问题的工作方式。这里 Jon 介绍了三种不同的场景,分别为性能大幅衰退(Major Regression)、性能偏离目标(Off Target)以及拙劣设计(Poor Design)导致的性能问题

1. 性能大幅衰退

这种场景通常发生在某次回归测试中突然出现的性能大幅衰退。遇到这种问题时,我们必须回过头去仔细排查所出现的性能问题。Jon 在这里提到了关于自动化性能测试的问题:我们在与性能问题的斗争中总会处于输的一方,因为性能总会在不经意间一点点衰退。因此为你的应用增加一个自动化的性能测试是非常必要的。

2. 性能偏离目标

性能偏离预期目标是另一种我们经常遇到的性能问题场景。我们的应用可能在一段时间内的性能都保持在一个稳定的区间内 —— 例如在某个性能测试中达到每秒 45 帧的数据。但这可能与我们的预期并不符合(例如我们希望能达到 60 帧每秒)。通常来说,在这种场景下我们能通过专注于修复几个出现问题的地方来解决此性能问题。

3. 拙劣设计导致的性能问题

在这种场景下,我们的性能通常会表现的极为差劲,远远低于预期。此时,我们很难想上面的场景一样专注于修复几个问题来解决。因此这种场景下的性能问题,应该通过重新设计某些关键的特性或者算法来解决。另外需要注意,自动化性能测试在此场景下也尤为重要 —— 你必须通过它们(自动化测试)来检验你的工作的方向是否正确。

关于性能的测试

无论我们需要解决的性能问题是上述的哪种类型,都离不开测试的步骤。性能的测试大概可以分为两种:单元测试(Unit Test)集成测试(Integration Test)

单元测试

关注性能的单元测试

关注性能的单元测试

在单元测试中,我们的目的是把应用的某些特性孤立出来进行测试。举个例子,假设我们要对 Xcode 的自动补全功能进行性能的单元测试,大概需要三个测试用例:

  • 测试与编译器通信,并获取原始的补全候选数据的性能。
  • 测试对原始候选数据进行分析并选出最适合的数据的性能
  • 测试把准备好的数据展示到 UI 层的性能

这样以来,通过这一系列的单元测试,我们便能够对 Xcode 代码自定补全功能的进行全面的性能测试。

集成测试

关注性能的集成测试

关注性能的集成测试

与单元测试相比,集成测试要求我们不仅以开发者的角度去测试性能,而要以用户的角度去进行性能测试。同样以 Xcode 的代码补全功能为例,对此功能的集成测试应聚焦于其真正的使用场景上面 —— 用 Xcode 打开一个源文件,输入一些代码并显示补全信息,然后不断重复此工作。在这一过程中,我们可以利用如 Instrument 等分析工具进行性能分析。

实战:利用 Instrument 进行性能分析

随后,Jon 在现场利用 Instrument 工具演示了如何对 Xcode 的打开新标签页功能进行性能分析。主要利用了 Instrument 中的 Time Profiler 工具(Jon 强烈推荐了此工具)。

具体的演示可以在 Session 视频11:30 的位置找到。

通用的解决方案

最后,Jon 总结了一系列性能优化的通用方案:

  • 延迟工作(Defer)
  • 批量工作(Batch)
  • 共享计算结果(Share Result):避免进行重复的工作
  • 使用更少视图(Fewer Views)
  • 使用直接观察模式(Direct observartion):避免两个对象通过某个中间者进行通信时,其通信过程不透明造成额外的性能消耗。
  • 使用字典记录(Prefer records to dictionaries)

在随后的部分中,我们可以看到这些解决方案在 Photos 应用的性能优化的实践中是如何应用的。


第二部分:Photos 性能优化之路

在这一部分中,苹果工程师 Matthieu Lucas 分享了原生的 Photos 应用在性能优化实践方面的经验。

概述

Matthieu Lucas 分享的优化实践主要集中于两个部分:

  1. ”时刻“ 页面的加载时长优化
  2. “年度” 页面构建及布局的优化

”时刻“ 的加载

首先,在分析加载性能之前,要确定我们本次需要优化的加载过程属于哪一类型。总的来说,普遍的加载大概有三种不同的类型:“冷” 加载、“热” 加载以及 “温和” 加载。

加载的类型

加载的类型

其次,我们还需要确定如何去 “测量” 加载的时间。这里 Matthieu 给出了一个定义:

Time it takes from the moment you hit the application’s icon until you can start interacting with using the application

目标

  • 即时性
  • 不阻塞交互
  • 无占位符

其中,我们重点关注一下 “即时性”。到底该如何界定加载的即时性,这里给出了以下几个条件:

  • 加载所耗时间与从桌面打开的缩放动画时长一样
  • 具体时间范围为 500 至 600 毫秒
  • 无缝转场

目标加载时间解析

现在,我们需要大概确认一下优化后的目标加载时间大概分为了哪几个部分,以及每个部分所需要进行的具体工作。广义上来说,iOS App 的启动加载都可以分为两个部分(小括号内为此次优化后的目标时间区间):

1. dyld (0ms - 100ms):
- 加载/链接动态库
- 运行静态初始化方法

关于这部分的内容可以关注 WWDC2017 - App Startup Time: Past, Present, and Future

2. main/UIApplicationMain(100ms - 600ms):
- willFinishLaunching
- didFinishLaunching
- 首次布局

至此,我们的 App 已经可以正常使用了,这也符合上面对 “加载时间” 的定义。因此我们的优化工作会集中在对这一部分的时间优化上面。

top Created with Sketch.