Reputation: 16246
I have set the Swift compiler flag -warn-long-function-bodies
to 90 milliseconds, to see which functions in my project are taking too long to compile (due to type checking).
I have the following method:
func someKey(_ sectionType: SectionType, row: Int) -> String {
let suffix = row == 0 ? "top" : "content"
return "\(sectionType)_\(suffix)"
}
(SectionType
is an String-backed enum)
As it is above, it takes 96ms on a 2017 MacBook Pro. The first thing I tried is to circumvent string interpolation and use \(sectionType.rawValue)
instead of \(sectionType)
, but now it gives me 106 ms. Wrong move...
Next, I changed:
let suffix = row == 0 ? "top" : "content"
to:
let suffix = "top"
The warning goes away, so it is the ternary operator that is causing trouble.
I tried this instead:
let suffix: String = { // Note the type annotation!
if row == 0 {
return "top"
}
return "content"
}()
...but now it is the closure that takes 97 ms (the whole function, 101).
I even tried the more explicit:
let suffix: String = {
if row == 0 {
return String("top")
} else {
return String("content")
}
}()
...and I get closure: 94ms; function: 98ms.
What's going on?
Is my 90-milliseconds limit too low? I know there was (is?) a type-checking bug with dictionary literals, but this seems something entirely different...?
My environment is Xcode 8.3.2 (8E2002),
Swift: Apple Swift version 3.1 (swiftlang-802.0.53 clang-802.0.42)
But Wait! There's more...
I tried this function body:
func someKey(_ sectionType: SectionType, row: Int) -> String {
if row == 0 {
return "\(sectionType.rawValue)_top"
} else {
return "\(sectionType.rawValue)_content"
}
}
...and it takes 97ms~112ms!?
Addendum: I transplanted the function and enum to a clean, minimal project (Single View Application) set up the same warning but it does not happen. I'm sure the project as a whole is affecting this one method somehow, but can't quite figure how yet...
Addendum 2: I tested the static version of my function: use fixed suffix "top" regardless of the value of row
(this takes less than 90 ms and triggers no warning), but added the following if
block:
func someKey(_ sectionType: SectionType, row: Int) -> String {
if row == 0 {
print("zero")
} else {
print("non-zero")
}
let suffix: String = "top"
return "\(sectionType)_\(suffix)"
}
This takes me back to 96~98 ms! So the problem arises when comparing row to zero?
Workaround: I kept playing with my code and somehow discovered that if I replace the if
block with a switch
statement, the issue goes away:
func someKey(_ sectionType: SectionType, row: Int) -> String {
let suffix: String = {
switch row {
case 0:
return "top"
default:
return "content"
}
}()
return "\(sectionType)_\(suffix)"
}
(I won't answer my own question because I do not consider this an explanation of what's really going on)
Upvotes: 25
Views: 9436
Reputation: 16242
I think it's the ternary operator.
I had similar results in Xcode 11 (~93ms) but the compile time decreases to ~23ms with:
func someKey(_ sectionType: SectionType, row: Int) -> String {
var suffix = "top"
if row != 0 {
suffix = "content"
}
return "\(sectionType)_\(suffix)"
}
By changing the logic on this line, I think we can prove it's the ternary logic because the method reduces to ~1ms. I've just made row a Boolean.
func someKey(_ sectionType: SectionType, row: Bool) -> String {
let suffix = row ? "top" : "content"
return "\(sectionType)_\(suffix)"
}
Equally (no pun intended) changing the ternary logic to let suffix = row != 0 ? "top" : "content"
halves the compile time. Which is comparable to my first code block. !=
is faster for Swift to understand than ==
.
Upvotes: 5