诊断 app 的功耗与性能退化问题
Diagnose Power and Performance regressions in your app
2021年6月9日
一句话判断
Xcode 13 的性能分析工具链终于能让你在 CI 里自动捕获功耗和帧率退化了 —— xctest 的 measure(metrics:) 新增了功耗指标,这是”性能即功能”从口号走向工程实践的关键一步。
这场 Session 讲了什么
Session 介绍了 Xcode 13 中用于诊断功耗和性能退化的新工具和 API。核心内容是 XCTest 框架中新增的 XCTMetric 类型:XCTClockMetric(CPU 时间)、XCTMemoryMetric(内存使用)、XCTStorageMetric(磁盘 I/O)、XCTCPUMetric(CPU 指令数)和全新的 XCTPowerMetric(功耗测量)。
功耗测量需要连接真机并且使用特定的硬件配置,但可以在自动化测试中集成。通过 measure(metrics:block:) 方法,你可以为关键用户路径设置性能基线,后续运行如果超过基线阈值,测试会自动失败。
还讲解了 Instruments 中 os_signpost 的新用法,以及如何在 Xcode Organizer 中追踪 app 版本间的性能趋势。
值得深挖的点
功耗测量的实际工作方式。XCTPowerMetric 不是通过软件估算的,而是直接读取设备的电量计(Fuel Gauge)数据。这意味着测试必须在真机上运行,而且要保证测试期间没有其他高耗电操作(如后台同步)。单个测试用例的功耗数据可能不精确(采样间隔太短),苹果建议至少运行 10 秒以上的操作才能获得有意义的功耗读数。
性能基线(Baseline)的回归检测。measure 方法的第一次运行会建立基线。后续运行会和基线比较,如果偏差超过阈值(默认 10%,可配置),测试会标记为 regression。这个机制非常适合 CI 环境 —— 每次提交自动跑性能测试,一旦某次提交导致帧率下降或功耗增加,立即被发现。
代码片段
功耗性能测试:
import XCTest
class PowerPerformanceTests: XCTestCase {
// 测试视频播放的功耗
func testVideoPlaybackPower() throws {
let app = XCUIApplication()
app.launch()
// 使用功耗指标测量
let options = XCTMeasureOptions()
options.iterationCount = 3 // 运行 3 次取平均
measure(metrics: [
XCTPowerMetric(), // 功耗
XCTClockMetric(), // CPU 时间
XCTMemoryMetric() // 内存使用
], options: options) {
// 播放一段 15 秒的视频
app.buttons["playVideo"].tap()
sleep(15)
}
}
}
自定义性能指标的 signpost:
import os.signpost
class RenderEngine {
let log = OSLog(subsystem: "com.app.render", category: "RenderLoop")
func renderFrame() {
// 标记帧渲染开始
os_signpost(.begin, log: log, name: "FrameRender")
defer {
// 标记帧渲染结束
os_signpost(.end, log: log, name: "FrameRender")
}
// 执行渲染逻辑
performHeavyRendering()
}
func renderBatch(_ count: Int) {
// 使用 interval 类型标记批量操作
os_signpost(.event, log: log, name: "BatchSize", "%d frames", count)
for _ in 0..<count {
renderFrame()
}
}
}
CI 中集成性能回归检测:
# 在 CI 脚本中运行性能测试
xcodebuild test \
-scheme MyApp \
-destination 'platform=iOS,name=iPhone 13 Pro' \
-only-testing:MyAppTests/PowerPerformanceTests \
-resultBundlePath TestResults.xcresult
# 解析结果检查是否有性能退化
xcrun xcresulttool get test-results summary \
--path TestResults.xcresult
最佳实践
- 为每个关键用户路径创建性能测试用例:启动时间、首页加载、列表滚动、视频播放、后台同步。每个用例独立设置基线,不要把所有操作混在一个测试里。
- 功耗测试在充满电的设备上运行。低电量模式下 iOS 会限制 CPU 频率,导致测试结果不可比。建议充电到 80% 以上再跑功耗测试。
- 在 Xcode Organizer 中定期查看生产环境的性能数据。测试环境永远无法完全模拟用户的设备状态和网络条件,Organizer 中的真实数据才是最终裁判。
还有什么值得关注
- Xcode Organizer 现在支持按 app 版本查看功耗趋势,可以定位是哪个版本引入了功耗退化。
os_signpost的.event类型适合标记离散事件(如网络请求完成),在 Instruments 时间线中显示为点。XCTMetric支持自定义指标,你可以测量 GPU 使用率等非标准指标。