0%

APP终极性能生存指南

性能是客户端永远绕不开的话题,一起来康康今年的WWDC提供了哪些有关性能提升的建议吧~

(温馨提示:本片内容来源自WWDC21:Ultimate application performance survival guide

想了解更多WWDC2021内容的小伙伴,可以阅读我以下文章,欢迎多多交流和指正

检测和诊断内存问题

一文带你读完WWDC21核心(新)技术点

iOS中的Hang是什么?该如何治理?

诊断APP的电源和性能回归

1:38

  • 使用的五种工具

    • Xcode Organizer
    • MetricKit
    • Instruments
    • XCTest
    • App Store Connect API
  • 参考的八个性能指标

    1:58

    • 电量

    • 启动时间

    • 超时响应率(Hang rate)

      APP无法响应用户的输入或者行为超过250ms,即记作一个hang

    • 内存

    • 磁盘写入

    • 滚动卡顿

    • APP终止

    • MXSignposts

  • 电量(battery usage)

    • 优化电池使用需要关注的几点:

      • CPU
      • 网络
      • 定位
      • 音频
      • 蓝牙
      • GPU

      其中CPU、网络、定位是电量消耗大户

    • 使用Xcode工具查看Debug期间的电量损耗情况

      4:10

      • 查看CPU超过20%的阶段(CPU High Utilization)
      • 查看CPU从闲置被唤醒的阶段(CPU Wake Overhead)

      4:41

    • 使用Instrument中的Time Profile工具查看该阶段更详细的信息

      (比如使用Location Energy Model确保应用如预期中正确地使用定位功能)

      这张是我本地截屏

    • 使用MetricKit收集用户的性能数据

      6:41

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      // 创建遵守`MXMetricManagerSubscriber`的协议的类AppMetrics
      class AppMetrics: MXMetricManagerSubscriber {
      init() {
      // 初始化时将AppMetrics加入到`MXMetricManager`单例中
      let shared = MXMetricManager.shared
      shared.add(self)
      }
      deinit {
      // 销毁时进行移除
      let shared = MXMetricManager.shared
      shared.remove(self)
      }

      // 处理每日的metrics
      func didReceive(_ payloads: [MXMetricPayload]) {

      }

      // 处理diagnostics
      func didReceive(_ payloads: [MXDiagnositcPayload]) {

      }
      }
  • 使用Xcode Organizer查看线上性能数据

    Xcode Organizer对MetrixKit收集到的数据进行了聚合,忽略了单一用户的详细数据,展现的是整体的趋势;

    而使用MetricKit可以更具针对性的对某一用户在一段时间内的性能表现进行分析。

    6:57

    • 查看用户的电量使用情况

    7:03

    • Regressions

      Regressions是Xcode 13新增的模块,它将各个版本的性能指标中发生劣化的情况单独拎出来展示,让开发者更清晰的看到哪些性能指标亟待优化。

      7:22

    • 查看问题发生时对应的代码

      7:58

  • 超时响应率和滚动卡顿(Hang rate & Scrolling)

    • 超时响应

      长时间无法响应是导致用户强退应用的重要原因

    • 滚动卡顿

      当在下一次屏幕刷新时,新的内容还未ready就会出现卡顿

    Hang rate & Scrollings是表明APP没有及时响应的两个指标,一旦发生会严重影响用户体验,甚至让用户在使用应用时产生挫败感,从而降低用户的使用APP的意愿。

    • 使用Xcode Organizer查看线上的卡顿情况

      • Hang rate

      9:40

      • Scrolling

      9:42

    • 使用Instrument进行分析

      10:05

      • Thread State Trace

        可以查看线程被阻塞的详细情况

      • System Call Trace

        可以查看系统函数调用的时机和时长

    • 使用XCTest进行性能测试

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      func testScrollingAnimationPerformance() throws {
      app.launch()
      app.staticTexts["Meal Planner"].tap()
      let foodCollection = app.collectionViews.firstMatch
      // `measure`函数默认重复5次,可以通过option设置手动停止
      let measureOptions = XCTMeasureOptions()
      measureOptions.invocationOptions = [.manuallyStop]
      // 开始measure
      measure(metrics: [XCTOSSignpostMetric.scrollDecelerationMetric],
      option: measureOptions) {
      // 滑动
      foodCollection.swipeUP(velocity: .fast)
      // 停止measure
      stopMeasuring()
      // 重置状态
      foodCollection.swipeDown(velocity: .fast)
      }
      }
    • 使用MetricKit收集线上数据

      在iOS 14中,MetricKit会收集用户使用过程中发生的问题,然后每24小时集中上报一次。

      在iOS 15和MacOS 12中,MetricKit仍是每天上报一次,但会在addSubscriber后,并且立刻进行回调

  • iOS 15中MetricKit新增动画性能检测的API,能够记录动画期间详细的性能数据和卡顿情况

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    func startAnimating() {
    // 标记动画开始执行
    mxSignpostAnimaitionIntervalBegin(
    log: MXMetricManager.makeLogHandle(category: "animation"_telemetry),
    name: "custom_animation")
    )
    }

    func animationDidComplete() {
    // 标记动画结束
    mxSignpost(OSSignpostType.end, log: MXMetricManager.makeLogHandle(category: "animation_telemetry"), name: "custom_animation")
    }
    1
    2

    > 注:MXSignpost是MetricKit中封装的API,可以用来监控关键代码的运行情况

    11:20

  • 磁盘写入

    • 使用Instrument中的File Activity查看磁盘写入情况

      13:22

      • 优化建议:

        • 对于频繁写入的case,推荐使用Core Data

        • 避免快速创建和删除文件

    • 使用XCTest进行性能测试

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      func testSaveMeal() {
      let app = XCUIApplication()
      let options = XCTMeasureOptions()
      options.invocationOptions = [.manullyStart]
      // 检测下面代码运行时的磁盘写入情况
      measure(metrics: [XCTStorageMetric(application: app)], options: options) {
      app.launch()
      startMeasuring()

      let firstCell = app.cells.firstMatch
      firstCell.buttons["Save meal"].firstMatch.tap()

      let savedButton = firstCell.buttons["Saved"].firshMatch
      XCTAssertTure(savedButton.waitForExistence(timeout: 2))
      }
      }
    • 使用Xcode Organizer查看线上磁盘写入情况

      14:35

      • 查看在Disk Writes报告中可以查看24小时内写入量超过1GB的case

      15:01

      • 在Xcode 13中,还可以得到一些优化建议

      15:22

    • 使用MetricKit收集用户的磁盘写入情况

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      // 标记开始磁盘写入
      func syncAllContentsToDB() {
      mxSignpost(OSSignpostType.begin,
      log:MXMetricManager.makeLogHandle(categroy: "diskWrite_telemetry"),
      name: "custom_diskWrites")

      // sync contents to database

      // 标记磁盘写入结束
      mxSignpost(OSSignpostType.end,
      log:MXMetricManager.makeLogHandle(category: "diskWrite_telemetry"),
      name: "custom_diskWrites")
      }
  • 启动时间

    • 使用Xcode Organizer查看线上启动数据

      18:15

      • 查看因启动超时导致的程序终止

      18:47

    • 使用Instrument中的App Launch工具进一步分析

      19:13

  • Memory

    • 使用Xcode Organizer查看线上内存使用情况

      20:21

    • 使用Instrument中的Leak、Allocations和VM Tracker三个模板

      • Leak检测内存泄漏
      • Allocations分析内存的生命周期
      • VM Tracker展示虚拟内存空间的使用情况
    • 使用MetricKit收集线上数据

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      func saveAppAssets() {
      mxSignpost(OSSignpostType.begin,
      log: MXMetricManager.makeLogHandle(category: "memory_telemetry"),
      name: "custom_memory")

      // save app metadata

      mxSignpost(OSSignpostType.end,
      log: MXMetricManger.makeLogHandle(category: "memory_telemetry"),
      name: "custom_memory")
      }

更多有关电量优化的Session:

Improving battery life and performance WWDC19

Analyze HTTP traffic in Instruments WWDC 21

更多Hang优化的Session:

Understand and Eliminate Hangs from you app WWDC21

更多滚动优化的Session:

Explore UI animation hitches and the render loop WWDC20

Eliminate animation hitches with XCTest WWDC20

更多磁盘写入优化的Session:

Diagnose power and performance regressions in your app WWDC21

更多启动优化的Session:

Why is my app getting killed WWDC20

更多内存优化的Session:

Detect and diagnose memory issues

更多关于性能工具的Session:

Diagnose performance issues with Xcode Organizer WWDC20

What’s new in MetricKit WWDC20

Identify trends with Power and Performance API WWDC20

Getting started with Instruments WWDC19