iOS耗电量和性能优化的全新框架

Session 417, Improving Battery Life and Performance (MetricsKit)

App 的耗电量和性能表现是用户体验的一个重要部分,耗电量过大或是性能很差的 App 会导致糟糕的用户体验。为了改善用户体验以及延长电池寿命,在 Session 417 中,苹果推出了三项新的电量和性能监测工具,分别用于开发阶段、内测阶段、以及线上阶段。相信通过本文,你会对你的 App 接下去的耗电量和性能优化的方向,有更好的计划。

本次苹果推出的三项工具分别是:

  • XCTest Metrics (开发和测试阶段)
  • MetricsKit (内测阶段和线上阶段)
  • Xcode Metrics Organizer (线上阶段)

Session 417 中,苹果的工程师首先为我们介绍了 Metrics 能够监测的指标,以及各项指标的意义。然后,由三项新工具的负责人,依次为我们介绍了这三项新工具。下面我们进入正文。

概览

今年,苹果推出的性能指标监测工具可以分为两大类,分别是耗电量统计和性能监测。之所以要把耗电量统计单独作为一大类,是因为电量对于 iOS 而言真的十分重要。iOS 以其普遍落后的电池容量,做到超越大部分安卓设备的续航表现,很大一部分原因都归功于 iOS 优秀的电池管理和严格的后台任务管理,。

下面我们从这两大类分别介绍下它们的主要监测指标。

1.Battery Metrics

battery

耗电量的指标可以分为几种,Metrics 可以分别进行统计,可以统计的几个耗电量大户分别是:

  • Processing
  • Location
  • Display
  • Networking
  • Accessories (蓝牙)
  • Multimedia
  • Camera

下面我们大致介绍下前四个:

Processing

CPU time, GPU time, etc.

这些指标可以来度量和理解 App 的复杂度,它们可以用来比对各个功能的算法效率,发现无效的渲染等。

Location Metrics

Cumulative usage time, bakcground time, etc.

可以用来了解定位的使用情况。

例如:

  • 定位多余的后台定位
  • 过高的定位精度

Display Metrics

Average Pixel Luminance (简称 APL,平均像素亮度,即每个像素的平均亮度)

在 X/XS 手机上,OLED 屏幕显示的 UI 的颜色直接影响到能耗。

  • 越亮的颜色 = 越高的能耗(高 APL)
  • 越暗的颜色 = 越低的能耗(低 APL)

Network Metrics

Upload and download bytes, connectivity, etc

尽可能优化网络使用,因为它是一项高能耗的任务。

例如:

  • 检查各个预期的上传/下载,看看能否推迟
  • 了解弱网络环境下的能耗情况(高耗能)

2.Performance Metrics

performance

性能指标包括以下几项:

  • Hangs
  • Disk
  • Application Launch
  • Memory
  • Custom Intervals

苹果着重介绍了前四个。

Hang Metrics

Hang Metrics 就是我们常说的卡顿监测 ANR,它可以用来:

  • 查找哪些地方的任务可以移到后台线程
  • 利用各种 dispatches 和 queues 来减少卡顿的概率

Disk Metrics

这个指标记录的是磁盘逻辑写入,它用来度量磁盘使用情况,可以用来:

  • 定位多余的磁盘写入
  • 检查合并策略

Application Launch Metrics

可以用来度量 App 启动或恢复所消耗的时间。

  • 了解启动时的耗时因素,例如数据库加载对启动耗时的影响有多大
  • 查看启动和恢复这两种路径下的不同耗时

Memory Metrics

内存的管理很重要,它也可以影响到应用的启动速度,以及在后台时,被终止的可能性。
Memory Metrics 用于查看平均内存占用,以及峰值内存占用。它可以用来检测内存的使用情况,例如:

  • 定位难以复现的内存泄漏
  • 减少应用挂起后的平均内存占用(有助于推迟应用被终止的时间)

了解完主要的一些指标后,让我们进入正题。

1、在 XCTest 中监测性能(XCTest Metrics)

单元测试支持检测更多的性能数据。这是本 Session 推出的第一项新功能。

使用 XCTest 监测性能指标

XCTest Metrics 是开发和测试过程中用来衡量应用性能的工具。

在 Xcode 11 以前,XCTest 就支持跑性能测试,但是,我们只能通过设定一些性能指标的基准线,来进行性能方面的监测。然而,性能指标其实包含多个维度,因此今年 XCTest 增加了一些新的指标。包括:

  • CPU
  • memory
  • storage
  • clock and OSSignpost
  • custom Metrics

另外,在应用运行的过程中 Xcode 中可以查看到应用在 CPU、内存等各个子系统上的整体性能消耗。但这些信息比较粗略,如果你想深入挖掘更多性能信息,或者诊断一些复杂问题,就可以使用 Instrument。Instrument 中提供了一些性能检测的模板,用来诊断内存、响应速度、数据读写、耗电量等问题,可以更详细、更精确地展示性能数据。

XCTest 示例

以往测量一段代码的性能表现只需要在 measure 代码块中编写需要检测性能的代码:

xctestbefore

而现在,我们只需在调用 measure 时增加想要监测的指标作为参数,就可以从多个维度监测代码块的性能表现,十分简单:

而检测应用启动耗时,更是容易。不需要任何代码,创建 XCTest 的时候就已经自动生成:

同样地,如果我们设置基准线(baseline),那么每次运行测试时,Xcode 都会自动对比 我们设定的基准线,如果启动耗时高于基准线,那么测试就不通过。

需要注意的是,在运行 XCTest 来测试 App 的性能时,不要启用 Xcode 的 debugger(去掉下图中勾选的选项),也不要开启 Xcode 的一些诊断选项,例如僵尸对象检测、内存分配记录等,以避免它们影响到应用的性能表现。可以在项目中创建一个新的 scheme 来关闭这些干扰项。

XCTest 新增的各种 metrics 除了在应用的 UITest 中检测性能,还可以在 Unit Test 中检测应用性能。除了官方提供的 CPU、内存、存储、时钟和 OSSignpost 之外,我们也可以自定义性能指标,利用 XCTest 的 baseline,来监控各个功能的性能是否有变差。

XCTest 也可以用于 A/B 测试,它可以低成本地对不同算法进行 A/B 测试,从而我们可以选取性能更优的算法。

此外,因为 XCTest 可以和 Xcode 以及 Xcode Server 配合得很好,因此这些性能测试还可以在日常开发和持续集成中使用,让我们随时了解 App 的性能表现。

2、跟踪线上数据(Field Metrics)

我们可以主动搜集用户的性能数据。

跟踪线上数据的好处

  • 利用内测用户和线上用户的数据量
  • 发现测试时遗漏的问题
  • 追踪各个版本迭代过程中性能指标的变化
  • 了解新功能和 A/B 测试的影响

MetricsKit

MetricsKit 用于收集电池用量信息和各项性能指标。它能够帮助我们记录下我们指定的关键代码块执行的时候,App 的各项性能指标。线上性能数据上报的重要性无需多言,以往我们绞尽脑汁也难以做到的性能数据上报,现在通过 MatricesKit 即可完成。

MetricsKit 使用起来很简单,它会在一天结束后,将过去24小时搜集的性能数据归集在一起,然后在下一次启动 App 后,在 delegate 的回调中提供给我们。关于这个代理回调的频率,苹果的官方文档中是这么说的:

The system then sends a report as an array of MXMetricPayload objects at most once per day. The array contains the metrics from the past 24 hours and any previously undelivered daily reports.

也就是说,每天我们的应用最多只会收到一次回调,该次回调会把上一段 24 小时收集到的数据返回给我们。同时,如果在上一个 24 小时之前,存在老数据没有返回给我们的,也会在该次回调中一并返回。返回的数据会存储成数组的形式,每个数组的元素表示一天的数据。

这个数据返回的频率,与 MetricsKit 的底层实现有关。在之前的 iOS 版本中,需要用户安装电量分析的 profile 之后,系统才会生成这种每 24 小时一份的数据库。Power Log 底层会以高效的方式收集各项性能数据,猜测可能是硬件实现的,然后将各项数据存储于 PL/SQL 格式的数据库中,每 24 小时生成一份数据库文件。因此,MetricsKit 应该是直接解析了这份数据库文件,然后将数据返回给 App。

Power Log 导出的原始数据

顺便一提,苹果做了这个解析工作之后,之前美图的测试同学做的许多自动化解析 Power Log 数据的工具差不多都可以退休了,这下总算省事了很多。不过老的 iOS 版本可能还是需要用自己的工具去解析,也还是有用的。

如何获取 MetricsKit 的数据

metri

如上图所示,要获得 MetricsKit 收集的数据,首先我们要实现一个 Subscriber 的类,并将 Subscriber 实例注册到 MXMetricManager 单例中,以便接受数据。当 MetricsKit 收集完 24 小时的数据后,就会将数据发送给它的所有 Subscriber。这时我们需要实现 MXMetricManager 的代理方法,来接收数据。

收到数据后,我们就可以对数据进行处理,或是将数据上报到服务端。

对于开发者而言,等待 24 小时再获取数据显然是不科学的,苹果也考虑得比较周到,我们在 Xcode Debug 菜单中就可以模拟触发一次数据上报的回调。

MXSignpost 打点

MetricsKit 还有一个特别有意思的,对我们也特别重要的接口,那就是 MXSignpost。我们利用 MXSignpost 就可以针对关键代码块打点,记录性能数据。

例如,我们可以对视频合成、视频播放等关键业务场景的性能损耗进行记录,了解 App 的性能热点在哪个流程上,然后针对性地进行优化。

这个接口初看起来和去年 Instrument 中增加的 os_signpost 的打点功能有点相似。用法也很像,只需要在目标代码前后打上点,系统就会自动记录信息。

而追根溯源,我们可以发现,其实 MXSignpost 就是对 os_signpost 的封装,因此它才能够获取到线程级的性能数据。它们的原理和行为也是一致的。os_signpost 的性能非常好,因此一开始我非常开心,心想,那 MXSignpost 的性能想必也非常优秀。

然而,MXSignpost 的头文件给我泼了盆冷水。苹果在 MXSignpost 的头文件上特别注明,切莫直接将 MXSignpost 替换成 os_signpost,否则,如果原本 os_signpost 打点的量比较多的话,将会导致 App 的性能显著下降

此外,自定义的打点数量也受到系统的限制,不能无限制地打点。因为过多的打点,也会输出大量日志,导致磁盘占用过大。如下图所示。但没有看到苹果有说明具体的数量限制,我们只能先自己保持克制了。

os_signpost 感兴趣的同学可以参考王乐之前的文章:WWDC-2018-Signpost .

同样的,执行关键代码块时记录下来的各项数据,也会在上述 24 小时的数据生成后,一并返回给我们的 Subscriber。返回的数据中会给出各个 MXSignpost 执行的时候的累计 CPU 耗时、内存占用、卡顿时间等信息。我们同样可以将这些数据上传到服务器,由后台进行统计分析。这样,我们就能从大量线上用户那里搜集的真实数据中,及早发现性能消耗的热点

mxSignpost

3、Xcode Metrics Organizer

简单来说,这是苹果为我们自动搜集用户性能数据的工具。

什么是 Xcode Metrics Organizer

  • 开箱即用的电量和性能分析工具
  • App 无需做出任何代码的修改
  • 从设备上的数据搜集到服务器上的数据归集,全流程都做到隐私保护

目前,它能提供电量、启动耗时、卡顿监测、内存监测、磁盘逻辑写入的数据上报。

这应该就是苹果之前预埋在 iOS 上的 Power Log 等工具获取到的数据。因此,从现在起,开发者升级 Xcode 后就能尝试使用这个工具了。

Xcode Metrics Organizer 如何运行的

当用户使用 App 时,iOS 会自动记录应用的各项性能指标,而后发送到苹果的服务器上。服务器搜集用户的这些性能数据,并且自动进行分析,生成报告。当我们开发者查看 Xcode Metrics Organizer 的时候,看到的就是性能分析的结果。但是如果用户量不够多的情况下,Xcode Metrics Organizer 上有可能看不到数据。

organize

Xcode Metrics Organizer 最大的特点就是它开箱即用。开发者们无需对自己的 App 做出任何改动,即日起就能马上体验这项功能。根据我之前调研 Power Log 得到的信息,苹果在记录这些性能数据时,可以做到对 App 的性能毫无影响,并且因为是系统级的记录,准确度非常高。

如何使用 Xcode Metrics Organizer

在 Xcode 的 Window - Organizer 中,可以看到一个新增的 Metrics,这就是 Xcode Metrics Organizer。

它可以查看最新版本的 App 的各项性能指标,同时,也可以查看历史版本的性能指标,并进行对比。

在电池用量统计中,这份报告分别展示了 App 在前后台的耗电量占比。同时,它分门别类地显示了各种耗电的活动的占比,例如定位、网络等。这和之前 iOS 版本中就有的 Power Log 记录的数据一模一样。

内存方面,则是提供了峰值内存和平均内存的数据。

另外,值得一提的是,我们还可以按照 iPhone/iPad 分别查看数据,或是单独查看某个型号的数据,以分析不同设备之间的性能表现,来精细化地对某些设备进行优化。我们有时候会遇到同样的功能,在不同设备上有不同的性能表现,例如某些功能在新机型上特别卡顿,这种时候区分设备查看性能数据就能够发现此类问题。然后再利用上文提到的更加精细的性能检测工具来进行调试,就可以定位到问题。

隐私策略

苹果在发布会上多次强调,数据搜集的全过程都会保护用户隐私,不会导致用户隐私泄露。这也就意味着,用户是可以选择是否与我们共享这些性能数据,因此我们收集到的数据并非是所有用户的。用于控制这项隐私权限的开关位于 iOS 的 设置 - 隐私 - 分析 - 共享 iPhone 分析,如果用户关闭了这个开关,那么他的性能数据就无法被我们获取到,包括 Xcode Metrics Organizer 中苹果自动搜集的数据,以及 MetricsKit 中系统返回给我们的数据。

总结

本 session 先是介绍了 Xcode 11 既有的,在开发调试阶段用于分析电量和性能的工具,然后推出了三个新的工具:

  • XCTest Metrics
  • MetricsKit
  • Xcode Metrics Organizer

summary

这些工具可以覆盖开发、内测、线上全流程的性能数据搜集,囊括了整体数据和精细化的数据。

做过 iOS 性能检测的同学都知道,得益于苹果严格的权限控制,iOS 上的性能检测十分之困难。特别是耗电量的统计,尽管对我们十分重要,但为数不多的几种方法,不仅麻烦,而且只能局限于研发阶段,无法获取线上数据。为了测量 App 的耗电量和性能消耗,不少团队甚至购买了电流计和红外线测温枪(说的就是我们团队),用最原始粗暴的手段来获取粗略的数据。

看到本次 WWDC 推出的 Metrics 其实我并不吃惊,苹果在数据上报方面做出的努力其实早就有迹可循。我们都知道,iOS 系统本身是有对电量的使用情况进行记录和分析的,所以我们才能在系统设置里看到过去一段时间里,各个 App 的前台工作时间和耗电情况。只是以往这些数据只用于 设置 中的耗电量展示,以及 Sysdiagnose 系统诊断中。不过我们可以通过一些方式导出这些数据。之前在调研 iOS 耗电量检测的时候,我曾经写过一篇关于 Power Log 的文章:iOS 最全面的功耗分析之——Power Log . 感兴趣的同学可以看一下。(当然,现在已经不能说 Power Log 是最全面的功耗分析工具了)。

总的来说,本次苹果推出的 Metrics 可以说填补了 iOS 平台上性能监测的空白,使得 iOS 平台上,从开发、测试,到发布,整个流程中 App 的耗电量和性能监测成为可能。并且,这些性能监测反馈给我们的各项指标,能够帮助我们在开发的过程中做出更好的决策,让我们的 App 获得更好的性能,这也是一个优秀 App 的必要条件。