Tumblr Engineering — Swift Compilation Reporting at Tumblr

1.5M ratings
277k ratings

See, that’s what the app is perfect for.

Sounds perfect Wahhhh, I don’t wanna

Swift Compilation Reporting at Tumblr

Doing a clean build of the Tumblr iOS app takes…a while, and we have a bunch of developers on the team, assuming each person does at least two clean builds a day1 that adds up to, well, too much time. As we march forward with Swift for new features, we noticed our compilations times increasing at surprising rates. There have been a few blog posts outlining expressions that the Swift compiler has trouble on. For instance, type inference of nested literal expressions2 and operator overloads are expensive to resolve.

To address this, we decided to automate monitoring of compilation performance. The goal was to create a weekly job that would compile our project with specific debug flags3, process the results, and email out the slowest compilation paths.

The result is a Swift script, called SwiftCompilationPerformanceReporter (nicknamed SwiftCPR), that we use to generate our weekly compilation report. Below are the steps SwiftCPR takes:

  • Runs a clean build with the following command4

    xcodebuild -workspace workspacePath -scheme scheme clean build OTHER_SWIFT_FLAGS="-Xfrontend -debug-time-function-bodies" | grep [1-9].[0-9]ms | sort -nr > buildOutputDirectory

    where workspacePath, scheme, and buildOutputDirectory are the workspace file, scheme, and output directory for the raw compilation logs, respectively. These can be specified in the config.json file.

  • Processes the raw compilation logs and merges duplicate entires.

    Sample raw logs:

    5992.1ms  /Users/tumblr/workspace/SwiftCPR/orangina/Classes/PerformanceLoggingEvent.swift:267:37  final get {}
    5718.3ms  /Users/tumblr/workspace/SwiftCPR/orangina/Classes/PerformanceLoggingEvent.swift:267:37  final get {}
    4376.1ms  /Users/tumblr/workspace/SwiftCPR/orangina/Classes/UniversalLink.swift:127:25    private final class func dictionaryOfAppArgumentsFromQueryString(string: String) -> [NSObject : AnyObject]?
    ...
    
  • Outputs a final report with the total compilation time and the slowest limit compilation paths, where limit can be configured in the config.json file.

    Sample report:

    Total build time: 1115.24661797285
    11.7104   /Users/tumblr/workspace/SwiftCPR/orangina/Classes/PerformanceLoggingEvent.swift:267:37  final get {}
    8.5783    /Users/tumblr/workspace/SwiftCPR/orangina/Classes/UniversalLink.swift:127:25    private final class func dictionaryOfAppArgumentsFromQueryString(string: String) -> [NSObject : AnyObject]?
    ...
    

Once the above steps are complete, our job emails the report to the team! From these insights, we have been able to refactor functions that took over 10 seconds to compile to roughly a tenth of a second. Hope this script can help your team better profile Swift compilation times!

Jasdev Singh

1: Majority of our builds are incremental.

2: This has been resolved and the fix will ship with Swift 3!

3: Specifically, -debug-time-function-bodies

4: Thanks to Michael Skiba!