没有 Mac,如何为 iPhone 做性能分析?
我接触过的 iOS 团队中,有一半并不在 Mac 上开发。他们编写代码后提交给 CI,由 CI 在一台租用的 Mac 上打包。这套流程一直运转良好,直到有人问:「为什么在我的手机上这么卡?」而能回答这个问题的工具,只能在团队里没人拥有的机器上运行。
本文介绍在这种情况下有哪些可用的方法。
为什么最直接的方案行不通
Instruments 是官方的性能分析工具,功能完善,但有三个容易被忽略的前提:
- 只能在 macOS 上运行。
- 通常需要 Xcode 工程,至少需要一个可以附加的构建。
- 大多数常用的 instrument 要求使用开启调试的包。
第三点即使对有 Mac 的团队也是隐患。Debug 包的优化级别、日志,有时甚至代码路径都与正式包不同。分析它时,您测量的是一个没有用户在运行的程序。
设备本身已经在采集这些数据
讨论工具时常被忽略的一点是:iOS 本身就在测量这些指标。系统会跟踪每个进程的 CPU、内存占用、GPU 利用率、帧呈现、网络字节数和能耗归因。这是系统必须做的,因为调度、内存压力下的进程回收,以及「设置 → 电池」页面都依赖这些数据。
这些计数器通过设备自身的服务,向用户信任过的电脑开放。这与电脑为设备备份、安装 App 使用的是同一条通道。读取这些数据无需越狱,无需在设备上常驻程序,也无需在 App 中集成 SDK。
能获取哪些数据
连接设备并将范围设为某个 App 后,您可以实时读取:
- 每个进程的 CPU(单核百分比)
- 随时间变化的内存占用
- GPU 利用率
- 实际呈现的帧率
- 网络收发字节数
- 能耗影响
这并不能覆盖 Instruments 的全部功能:没有内存分配调用树,也没有精确到符号的耗时分析。但它足以回答大多数性能排查最初要回答的问题:瓶颈出现在哪种资源上,在什么时候?
这通常就是关键问题
一份卡顿反馈要回答的并不是调用树,而是:问题受限于 CPU、GPU,还是被其他因素阻塞。这三种情况的修复方向完全不同,而图表上的四条曲线足以区分:
| 卡顿时刻 | 受限于 |
|---|---|
| CPU 升高、GPU 平稳 | 主线程上有繁重任务 |
| GPU 升高、CPU 平稳 | 渲染开销:过度绘制、模糊、离屏渲染 |
| 内存骤升 | 一次大的内存分配,通常是图片解码 |
| 各项指标均无变化 | 阻塞在其他地方,或设备正在降频 |
确定属于哪一种情况,就知道该检查哪部分代码。之后往往不再需要调用树;即使需要,您也已经知道该把哪段交互交给有 Mac 的同事去分析。
一个容易被忽视的因素
在您手中的设备上做分析,价值不如在用户手中的设备上做分析。三年前的机型帧预算更紧张、更早降频、电池容量已经衰减,而桌上的旗舰机会掩盖这些问题。
因此,能在任意桌面系统上做性能分析,对没有 Mac 的团队而言不只是方便。这意味着您可以分析团队中的任何一台设备,例如测试同事的手机、售后备用机或闲置的旧机型,而不必局限于与某台 Mac 配对过的设备。
推荐的操作流程
- 拿到出现问题的那台设备,或您支持的最旧机型。
- 等设备冷却。发热的设备表现会明显不同。
- 将范围设为您的 App。
- 采集一段固定的交互,例如同一个列表、相同的滑动距离、持续 10 秒。
- 找到帧率下降的位置,查看该时刻的其他曲线。
- 只修改一处,然后重新采集同一段交互。
- 对比两次结果。
第 6 步最常被跳过。单次采集只是一个数字;改动前后的两次采集,才能得出结论。
它无法替代的部分
需要明确以下边界:
- 内存分配分析。 无法从内存趋势中看出的泄漏,仍需借助堆分析工具。
- 符号级 CPU 归因。 知道受限于 CPU,并不等于知道是哪个函数导致的。
- 网络内容。 字节数不等于请求内容,后者需要抓包工具。
设备级计数器的作用,是把「感觉有点卡」转化为「在这款机型上,页面转场期间受限于 GPU」。性能排查的大部分价值就在这一步,而您在日常使用的电脑上即可完成。
