An open source tool to speed up iOS app launch

What do the Snapchat, Airbnb, and Spotify iOS apps have in common? They all use order files to speed up their iOS app launch times.
Order files re-order your binary to improve how symbols are loaded into memory. No code changes are necessary, but generating an optimized order file can be cumbersome, so it’s mostly done by larger teams or teams willing to pay for a service like Emerge Tools’ Launch Booster.
It just so happens that Emerge Tools is now part of Sentry. As our first major release together, we’re open-sourcing the core functionality of Launch Booster in a new Swift package: FaultOrdering.
In the repo, you can inspect the code (and hopefully contribute!), build it yourself, and tailor it to your app’s workflow. Read on to learn more about how order files work and how to integrate FaultOrdering with your project.
Order files instruct the linker to group startup-critical symbols together in the binary, ultimately reducing page faults – the total number of pages loaded during startup. An order file itself is just a text file that has one line per symbol in your app. For example, the Emerge Tools HackerNews app’s order file contains lines like:
+[SentryAppStartTracker load]
+[SentryCrashDefaultMachineContextWrapper load]
_main
_$s10HackerNews0aB3AppVAC7SwiftUI0C0AAWl
_$s10HackerNews0aB3AppV7SwiftUI0C0AadEPxycfCTW
_$s10HackerNews0aB3AppVACycfCTf4d_n
This example contains symbols for Objective-C, C, and Swift code used during an app launch. The more symbols used during app launch that you can track, the more an order file can optimize them to reduce app launch time. When you go to build your app, the linker takes this order file as an input and arranges code in the resulting binary exactly as prescribed.
How does this translate into faster app start time? When a user launches your app, the iOS kernel loads it into memory via memory mapping. As the app launches and code is being executed, the necessary memory pages are faulted in by the operating system. The performance of this is noticeable when happening many times at once. And remember, the OS has to load the entire 16KB page even if only a tiny bit of it is used! By grouping app startup code together, fewer pages are needed to load your code into memory, making the process faster.
If you want a more in-depth look at order files, you can read the Emerge Tools blog post.
There are many ways to generate an order file. A common strategy is to use compiler flags that instrument extra hooks into your app’s code to track which functions get called – similar to code coverage. However, this requires changes to your app’s build process since that can be invasive and difficult to maintain.
Instead, FaultOrdering works by adding a breakpoint to every function in the app, launching the app, and then tracking which breakpoints are hit. This doesn’t require any extra build configuration and can be done with the same app that you ship to the app store. Setting these breakpoints with lldb
would be very slow, which is why we created our own minimal debugger, SimpleDebugger, to handle writing breakpoints and responding when triggered. We chose functions as a good level of symbol granularity that is known to work well with both breakpoints and order files.
Launch Booster orders the symbols that are used during app launch as well as symbols that are not used. This is done because there are symbols that are not capable of being ordered (typically the ones generated by the linker). By ordering everything in the app we force all the unorderable symbols to be grouped together - taking up as little memory as possible. This extra optimization maximizes the effect order files can have on your app.
The new Swift package takes this entire process and runs it within a standard XCUITest. The test produces an order file as a XCTAttachment.
let app = XCUIApplication()
let test = FaultOrderingTest { app in
// Perform setup such as logging in
}
test.testApp(testCase: self, app: app)
You can add this UI test to your CI pipelines to easily generate a new order file for every release.
The framework also lets you set up the app before instrumentation starts, to output an order file optimized for a specific scenario. A common use case for this is to log into the app, so that all the authenticated code is hit when measuring page faults.
So, how much will order files improve your app’s launch time? As usual – “it depends.” The more pages your app uses during app launch, the greater the potential benefit (you can use Sentry’s tracing to measure the improvement). In practice, we’ve seen apps have a 20% improvement in startup time.
There are no downsides to using an order file, it just used to be a pain to implement. Now anyone can easily use an order file and we hope there will be even more adoption to help apps get faster.
On the Emerge<>Sentry integration, we have some more exciting open source updates to come (hint: ☠️). Stay tuned for more!