Diagnose Power and Performance regressions in your app
Developer Tools 高级 22m

诊断 app 的功耗与性能退化问题

Diagnose Power and Performance regressions in your app

2021年6月9日

在 Apple 官方观看视频

一句话判断

Xcode 13 的性能分析工具链终于能让你在 CI 里自动捕获功耗和帧率退化了 —— xctestmeasure(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

最佳实践

  1. 为每个关键用户路径创建性能测试用例:启动时间、首页加载、列表滚动、视频播放、后台同步。每个用例独立设置基线,不要把所有操作混在一个测试里。
  2. 功耗测试在充满电的设备上运行。低电量模式下 iOS 会限制 CPU 频率,导致测试结果不可比。建议充电到 80% 以上再跑功耗测试。
  3. 在 Xcode Organizer 中定期查看生产环境的性能数据。测试环境永远无法完全模拟用户的设备状态和网络条件,Organizer 中的真实数据才是最终裁判。

还有什么值得关注

  • Xcode Organizer 现在支持按 app 版本查看功耗趋势,可以定位是哪个版本引入了功耗退化。
  • os_signpost.event 类型适合标记离散事件(如网络请求完成),在 Instruments 时间线中显示为点。
  • XCTMetric 支持自定义指标,你可以测量 GPU 使用率等非标准指标。
WWDC 2021