The Swift type-checker remains a performance bottleneck for compile times, though it has improved tremendously over the past two years. You could even say the type-checker has gone from being drunk to sober. To help users debug these issues, awhile back Jordan Rose added a frontend Swift compiler flag that would emit warnings in Xcode for functions that took too long to compile, or rather took too long to type-check. In Xcode 9, there’s a new, similar flag for checking expressions.
About -warn-long-function-bodies
Bryan Irace and Soroush Khanlou originally wrote about the -warn-long-function-bodies
flag when it was first introduced. You could specify a threshold in milliseconds that would trigger a warning. For example: -Xfrontend -warn-long-function-bodies=100
would trigger a warning in Xcode for any function that took longer than 100ms to type-check. This was always considered an experimental flag, as Jordan notes in his original commit: As a frontend option, this is UNSUPPORTED and may be removed without notice at any future date. As far as I can tell, this is still the case. However, this flag still works in Xcode 9 and I haven’t seen any discussion about removing it.
About -warn-long-expression-type-checking
In Xcode 9, there is a new, similar flag for type-checking expressions, not just functions. However, this time the flag made an appearance in the official Xcode 9 GM release notes:
The compiler can now warn about individual expressions that take a long time to type check.
To enable this warning, go the Build Settings, “Swift Compiler - Custom Flags”, “Other Swift Flags”, and add:
-Xfrontend -warn-long-expression-type-checking=<limit>
where<limit>
is the lower limit of the number of milliseconds that an expression must take to type check in order for the warning to be emitted.This allows users to identify those expressions that are contributing significantly to build times and rework them by splitting them up or adding type annotations to attempt to reduce the time spent on those expressions. (32619658)
This time, you can thank Mark Lacey for the flag. (pull request for Swift 4)
Using these flags to improve compile times
As mentioned, after you add these flags you will start getting warnings. Keep in mind that if the threshold is too low, for example 10ms, then you will get a ton of warnings that cannot be fixed. Experiment with these threshold values and adjust as needed. I suggest starting at 200
and tuning from there. If your code base is large, it might make more sense to use a higher value for your project (say 500
) and try to decrease it over time. Otherwise, you’ll be spending a lot of time trying to get all functions and expressions to compile in under 200ms. Also, I recommend setting these flags only for DEBUG
build configurations.
There are two common scenarios where Xcode will start emitting warnings with these flags: (1) very complex expressions or functions, and (2) expressions that omit explicit types and rely on type inference. To silence the warnings — and thus improve compile times — try breaking up expressions into smaller steps with intermediate variables, and adding explicit types to variable declarations and closure parameters. The code may not look as elegant after these changes, but what’s more important to you and your team?
A temporary solution
Using these flags is obviously a temporary (and kind of hacky) solution to improving compile times. It only treats the symptoms rather than the cause, but it is better than nothing. As I said before, the Swift team is well aware of the problems and they are working hard to address them. Every single Swift release is getting better and I’m sure Swift 5 will bring even more improvements.
I’m hoping using these flags — and having to change your code to improve compilation times — will soon be a hack of the past. Eventually it will be, we just don’t know when.
If you are interested in learning more about the type-checker design and implementation, see this doc in the main Swift repo.