Swift build time optimizations — Part 2

It’s soon been two months since I wrote the first post on this topic. That post introduced the Build Time Analyzer for Xcode which was written to help identifying areas where the Swift compiler struggled.

The plug-in has since then spread throughout the Swift community and during the WWDC labs, lots of people were seen using it. Consequently, something quite interesting happened. Apple reached out to me and made an enhancement request (see the new Occurrences column).

There are two types of causes for slow build times that can be identified with the plug-in. The first is where an individual routine takes too long to compile. This was the focus of my previous post and I listed a few examples and workarounds there. The other is where closures and lazy properties are getting type checked far too many times. This will be the focus of this post.

Closures and lazy properties

I recently added a new column to the plug-in window called Occurrences. The purpose is to make it easier to understand why relatively simple code sometimes slows down the build process.

As seen above, a couple of lazy getters are occurring 159 times in my Xcode build log. In fact, I can open up any file in the log and see a reference to it. Below are some examples taken from 3 separate files, none of which references CMGridView in the code.

5.7ms  /CMGridView.swift:63:27 @objc get {}
16.5ms /CMGridView.swift:63:27 @objc get {}
15.5ms /CMGridView.swift:63:27 @objc get {}

So it turns out that the compiler is type checking my lazy property for every single .swift file in the target, adding up to a cumulative build time of 1905.5ms (using Swift 2.2). For Swift 3.0, the issue persists but the build time is nearly cut in half.

If you are using lazy properties in your projects, I really recommend paying attention to this. Before refactoring my code in the above example, I used to have quite a few more of these.

Let’s have a look at the code.

…and if you think 2 seconds is bad for compiling that, it gets even worse if a closure is used.

The code:

Workaround

To improve the build time for these, simply move out the code to private methods wherever possible.

The lazy property above will still receive repeated type checking but with the code moved out, the build time is now reduced by 96.7%.

Build times in Swift 3.0 so far

With Xcode 8.0, the era of Xcode plug-ins ended and a new era of Xcode extensions begun. Given the limitations of extensions, I am working on making the plug-in a standalone app. It will (hopefully) be out well before Xcode 8.0 comes out of beta. In the meantime, here are some comparisons based on the examples listed in my previous post.

Ternary operator

Nil Coalescing Operator

ArrayOfStuff + [Stuff]

Performance surprises in Swift 3.0

While the above looks promising, I did notice several new issues in Swift 3.0. But to be fair, Xcode 8 is still in beta and I didn’t benchmark the overall build time. Time will tell if I need to write a third post on this topic… 😉