Instruments 之 使用 Time Profiler 优化启动速度

在性能优化过程中,启动速度优化一直是一个比较大的点。Apple 官方期望 APP 的启动时间为 0.4 s,用户的期待时间为 2 s (来自调查)。不过作为开发者,当然希望我们的 APP 的启动速度能够尽可能的快。若是对 Time Profiler 不熟悉的同学可以先阅读一下先前的文章 Instruments 之 Time Profiler 使用。

冷启动和热启动

APP 启动分为冷启动(Cold Launches),当 APP 长时间没有被启动的时候,用户再次启动 APP 的时候就是冷启动,若是手机重启之后,APP 的第一次启动也是冷启动;冷启动对应的是热启动(Warm Launches),当 APP 启动时需要的 dylibs 仍然停留在设备的磁盘缓存的时候,这个时候就是热启动,热启动的速度会更快。

优化案例

使用 Xcode 版本为 8.3.2 ,设备为 iPhone 6 ,系统版本 10.3.1。 APP 每次启动之前需要重启一下手机,达到冷启动的效果。案例使用 raywenderlich 的
Catstagram 启动优化。该案例是一个带图片的列表。

Instruments 之 使用 Time Profiler 优化启动速度_第1张图片
Catstagram

优化 before main()

Instruments 之 使用 Time Profiler 优化启动速度_第2张图片
before main()

APP 启动优化可以分为 2 个部分,一个部分在 main() 函数之前,另一部分在 main()函数之后。对于 APP 的启动细节可以参考 WWDC 的 Optimizing App Startup Time 章节,本文主要讲使用 Time Profiler 来分析 APP ,然后根据分析结果来优化 APP,着重讲解 Time Profiler 的使用过程。
接下来,打开 Catstagram 案例,添加 Scheme 的 DYLD_PRINT_STATISTICS 参数,并设置值为 YES,见下图所示。该DYLD_PRINT_STATISTICS参数用于让 Xcode 控制台输出 APP 在 before main() 时机之前的花费时间。

Instruments 之 使用 Time Profiler 优化启动速度_第3张图片
Scheme
Instruments 之 使用 Time Profiler 优化启动速度_第4张图片
DYLD_PRINT_STATISTICS

设置好了之后,Command + R 在冷启动情况下启动 APP ,可以看到控制台的输出

Total pre-main time: 1.5 seconds (100.0%)
         dylib loading time: 814.09 milliseconds (52.6%)
        rebase/binding time:  52.20 milliseconds (3.3%)
            ObjC setup time: 241.27 milliseconds (15.6%)
           initializer time: 437.29 milliseconds (28.3%)
           slowest intializers :
             libSystem.B.dylib :  19.26 milliseconds (1.2%)
               AsyncDisplayKit : 145.63 milliseconds (9.4%)
                    Catstagram : 277.81 milliseconds (17.9%)

重点关注 Total pre-main time: 1.5 seconds (100.0%) 的信息,冷启动情况在 pre-main 时机中可以看到信息 dylib loading time: 814.09 milliseconds (52.6%) ,也就是说我们的 dylib loading time 加载时间占据了 52.6% 。联想到我们的第三方库是采用 pod 管理,并且是 use_frameworks ,frameworks 是一个可优化点,

# Uncomment the next line to define a global platform for your project
# platform :ios, '9.0'

target 'Catstagram' do
  use_frameworks!
  pod 'AFNetworking'
  pod 'AsyncDisplayKit', '~> 2.2'
  pod 'Yoga', '~> 1.3'
  pod 'Firebase', '~> 3.15'
  #pod 'FirebaseUI', '~> 3.1'
end
Instruments 之 使用 Time Profiler 优化启动速度_第5张图片
frameworks

这个 frameworks 是一个可优化点,打开 Podfile,并注释掉 use_frameworks ,然后命令行执行 pod install 命令 ,更新工程,可以看到工程设置发生了变化。

# Uncomment the next line to define a global platform for your project
# platform :ios, '9.0'

target 'Catstagram' do
  #use_frameworks!
  pod 'AFNetworking'
  pod 'AsyncDisplayKit', '~> 2.2'
  pod 'Yoga', '~> 1.3'
  pod 'Firebase', '~> 3.15'
  #pod 'FirebaseUI', '~> 3.1'
end
Instruments 之 使用 Time Profiler 优化启动速度_第6张图片
非 frameworks

修改好了之后,Command + R 在冷启动情况下启动 APP ,查看控制台的输出。

Total pre-main time: 1.1 seconds (100.0%)
         dylib loading time: 470.55 milliseconds (39.6%)
        rebase/binding time:  31.07 milliseconds (2.6%)
            ObjC setup time: 255.35 milliseconds (21.4%)
           initializer time: 430.58 milliseconds (36.2%)
           slowest intializers :
             libSystem.B.dylib :  12.89 milliseconds (1.0%)
                    Catstagram : 792.04 milliseconds (66.6%)

从 log 中可以看到明显的变化,Total pre-main time 由之前 1.5 seconds 降到 1.1 seconds 。dylib loading time 由之前的 814.09 milliseconds (52.6%) 降到 470.55 milliseconds (39.6%) 。优化效果非常明显。

优化 after main()

Instruments 之 使用 Time Profiler 优化启动速度_第7张图片
after main()

优化完 before main() 之后,开始来优化 after main()。从图中可以看出启动优化的点是集中在 UIApplicationMain()上。
打开 Instruments 选择 Time Profiler 来分析 APP。

Instruments 之 使用 Time Profiler 优化启动速度_第8张图片
Time Profiler
Instruments 之 使用 Time Profiler 优化启动速度_第9张图片
launch 周期

选择 APP 生命周期中的 Launching 生命周期来分析,如上图所示,可以清晰的看到耗时操作主要发生在 log 操作中,所以我们回到关于 log 的这段代码代码中,它可能是一个可以优化的点。

    func application(_ application: UIApplication, willFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey : Any]? = nil) -> Bool {

        let appearance = UINavigationBar.appearance()
        appearance.backgroundColor = .white
        appearance.barTintColor = .white

        CoolLogger.reportLogs()
        
        return true
    }

上述打 log 代码在 main 线程中执行,所以这段代码是可以优化的,我们将这段代码放到非 main 线程中执行。

      DispatchQueue.global(qos: .background).async {
            CoolLogger.reportLogs()
        }

修改代码之后,重新启动手机,让 APP 进行冷启动,继续使用 Time Profiler 分析 APP,从下图的分析结果总可以看到优化取得了效果,APP 的 Laucning 生命周期没有出现 APP 的生命周期中,说明使用了及其短的时间来完成启动,这时间长度可以忽略。

Instruments 之 使用 Time Profiler 优化启动速度_第10张图片
极速启动

总结

Time Profiler 可以看到代码的运行时长,配合它的Lift Cycle 工具可以用来优化 APP 的启动速度。 Time Profiler 只是一个工具,它只能帮助记录 APP 的运行状态,而开发者可以根据记录的状态分析 APP 的耗时操作,然后进行修改,再用 Time Profiler 验证。

参考

本文是 raywenderlich 的课程笔记,内容参考 Practical Instruments 课程
1、Demo 项目 https://files.betamax.raywenderlich.com/attachments/videos/786/0965b118-95eb-492f-804c-3135c7347130.zip

2、https://videos.raywenderlich.com/courses/74-practical-instruments/lessons/4

你可能感兴趣的:(Instruments 之 使用 Time Profiler 优化启动速度)