Measuring your iOS app’s true startup time in production
Introduction
Before an app even runs main and +applicationDidFinishLaunching, a considerable amount of work is done, including setting up dylibs for use, running +load methods, and more. This can take 500ms or more. Blog posts like this one show you how to measure it with the debugger, using DYLD_PRINT_STATISTICS, but it’s hard to find any help for measurement in the wild. Note the special handling for iOS 15’s pre-warming feature.
How To Do It
You can get the process start time with the following code:
Caveat: absolute timing
The process start time that we get from processStartTime is given in seconds since 1970. Unfortunately, this is not so great for timing because it can be changed, e.g. if the phone synchronizes its time with a server in between when the process starts and when we run [NSDate date].timeIntervalSince1970 in main(). This would be a very rare case, but it should be kept in mind (you may want to remove negative and ridiculously large values from your results).
Main thread CPU Time
You can also get the amount of CPU time the main thread has spent by running this code on the main thread:
This number represents the amount of time the main thread has spent doing work, but does not include the amount of time it was waiting, e.g. due to being preempted by another process or thread. In practice, it will be about the same as the time taken from the method in the gist (I measured it being consistently 7ms less). This number may be preferable if you don’t want to time anything that’s out of your control.
Conclusion
The pre-main() time provides an important piece of information in understanding your overall launch time. By measuring from the very start of the process to when the app is actually interactive, you can gain insight into the actual experience for your users.