Slow App Startup Times

Apple suggest to aim for a total app launch time of under 400ms and you must do it in less than 20 seconds or the system will kill your app. You control what your application delegate does but how do you debug slow startup times that happen before your code is even called? Here is a tip from WWDC 2016 that might help.

Pre-main Time

A lot happens before the system executes your app’s main() function and calls app delegate functions like applicationWillFinishLaunching. Before iOS 10 it was not easy to understand why an app was slow to launch for reasons other than your own code. It has been possible to add the DYLD_PRINT_STATISTICS environment variable to your project scheme but the output was hard to figure out. With iOS 10 Apple has made the output from enabling DYLD_PRINT_STATISTICS much easier to understand.

To try it out add the environment variable DYLD_PRINT_STATISTICS to your project scheme with a value of 1:

Enabling DYLD_PRINT_STATISTICS

Here is a typical output for an Objective-C project running on an iPad Air 2 (using iOS 10 beta 3). The pre-main time of under 75ms is well within target:

Total pre-main time:  74.37 milliseconds (100.0%)
       dylib loading time:  41.05 milliseconds (55.2%)
      rebase/binding time:   8.10 milliseconds (10.9%)
          ObjC setup time:   9.87 milliseconds (13.2%)
         initializer time:  15.23 milliseconds (20.4%)
         slowest intializers :
           libSystem.B.dylib :   6.58 milliseconds (8.8%)
 libBacktraceRecording.dylib :   6.27 milliseconds (8.4%)

When testing for slow app launch times remember to use the slowest device you support (if you can).

The output shows you the total time taken up to the point the system calls your application main() followed by a breakdown of the main steps. WWDC 2016 Session 406 Optimizing App Startup Time goes into detail on each of these steps together with tips for improving times but here are my quick notes:

After all that the system calls main() which in turn calls UIApplicationMain() and your app delegate methods get a chance to run.

Loading Frameworks Takes Time

To finish up I thought I would run a quick experiment to see how loading extra frameworks slows down launch time.

As a baseline here is the launch time for a new Swift project created with Xcode 8 measured on an iPad Air 2 running iOS 10 beta 3. I rebooted the device before each measurement to cause a cold launch with no caching of the app data in memory:

Total pre-main time: 408.97 milliseconds (100.0%)
     dylib loading time: 383.84 milliseconds (93.8%)
    rebase/binding time:   7.86 milliseconds (1.9%)
        ObjC setup time:   6.82 milliseconds (1.6%)
       initializer time:  10.36 milliseconds (2.5%)
       slowest intializers :
         libSystem.B.dylib :   2.33 milliseconds (0.5%)

It takes the system over 380ms to load the frameworks (compare that to the Objective-C example). I assume this is due to the Swift libraries embedded in the App bundle. For comparison here is what happens when I add 10 external frameworks (a selection of Swift cocoapods):

Total pre-main time: 682.90 milliseconds (100.0%)
     dylib loading time: 631.17 milliseconds (92.4%)
    rebase/binding time:  17.06 milliseconds (2.4%)
        ObjC setup time:  17.47 milliseconds (2.5%)
       initializer time:  17.09 milliseconds (2.5%)
       slowest intializers :
         libSystem.B.dylib :   6.05 milliseconds (0.8%)

The time to load the frameworks has increased from 380ms to 630ms. A not so scientific test but enough to make you think next time you load up an app with external dependencies. Try it on your apps and see how it compares.

Further Reading

Never miss a post!

iOS Size Classes Cheat Sheet

Subscribe and get my free iOS Size Classes Cheat Sheet

Unsubscribe at any time.
No time to watch WWDC videos?

Sign up to get my iOS posts direct to your inbox and I will send you a free PDF of my iOS Size Classes Cheat Sheet.

Unsubscribe at any time.
Archives Categories
comments powered by Disqus