Swift build time reanalysis & optimisations
Some time ago I’ve seen a topic about Swift compilation time optimisations and decided to make my own research. Why? The issues from the article was actually repeatable even for Swift 3, but when I scanned one of my projects for bottlenecks such as ternary operator, nil coalescing operator and other… everything were just fine.
My project was compiling fast and easy even tho I was using performance unrecommended solutions. There were two possible explanations for behavior mentioned: my project was magically protected from compiler issues or there was something wrong with Robert’s research. To understand how and why my project was compiling fast, let’s go step by step and start from the things covered in his article.
Nil Coalescing Operator
One of my favourite Swift syntax. Let’s take the example from the article I’ve mentioned before:
Compilation time: 12 seconds! Even worse then it was in Swift 2.2.
And no, my Mac isn’t wooden.
You might say: “Whoa, Apple, wtf?”, and mark the issue as confirmed but don’t judge it too fast. Let us optimise the code a little bit and split it into sub-expressions:
Compilation time: 30 ms (milliseconds).
Looks like its not about evil coalescing operator. But that would just be too easy. Let us make it slightly more challenging:
Compilation time: 35 ms.
Conclusion: Using nil coalescing operator is safe. But what was the problem then?
I guess its already obvious for everyone that a new suspect is long-complex expressions. Before sending it to the jail lets get some more clues.
Below is an example where we won’t split the code into sub-expressions:
It won’t take much time to get a result:
Expression was too complex to be solved in reasonable time; consider breaking up the expression into distinct sub-expressions.
I think thats it.
Workaround: to improve build time, simply split your expression into sub-expressions and store them into variables. It would make your compiler’s life much easier.
We begin with the old code again:
Btw I didn’t have type0ToString method so I’ve replaced it:
It didn’t really make any difference. As expected, compilation time is still around 250 ms.
But I think ternary operator is innocent. And again we begin with splitting our example into sub-expressions:
Compilation time: 45 ms.
Much better now. But you can’t be serious. It takes 45 ms for the ternary operator itself? I use it almost at every line. If it was truth then my code should be compiled for hours.
Now we remove everything but ternary operator from our experiment:
Compilation time: 7 ms. Well it smells like … victory.
Conclusion: its all again about complex expressions. After some research I’ve found that complex expressions is just an underling. But real ‘Doctor Evil’ is Swift type-checker. I would provide more details in my next article.
Few more amnesties
Compilation time: 19ms
// Build time: 1433.7ms
let expansion = a — b — c + round(d * 0.66) + e
Compilation time: 6 ms. Mind that all variables should be the same type. As I mentioned previously, type checking is a pain for Swift.
And the sweetest:
Compilation time: 86 ms. At least not 12 hours. It could be even more faster but I would keep it for the next part because it requires some review of the compiler structure.
And the last surprise block from the second article:
Could also be refactored to:
Compilation time: 7.3 ms. Which is nice.
This is it for the first part. We’ve busted some myths about nil coalescing operator, ternary operator and few other operations. Also we’ve discovered that the main reason for slow compilation is complex expression.
Next time I will go for further details about compilation problems and how to solve them.
I’d be happy to hear your feedback.