there's some woodo magic in CGFloat, one could naïvely hope "it's just an alias to Double" but that's not the case. try Double, i bet that would be quicker.
Yeah, random guess is that a single line gets allocated some fixed time overhead for some reason - so probably a red herring. But I’m curious what someone who knows sees. Is it always the first or last line that gets the time? (Try with a function for each)
The time measurements in the type checker include all time spent while the type checker is working on that expression, even if the compiler is doing something only tangentially related to typechecking itself. And the compiler is structured to be very lazy, so that work can be pretty substantial.
In your case, I would guess that because this expression is the first time you’re using the + operator in the program, the compiler has to deserialize all of the + operators it knows about (something like 30-50, IIRC) so that it can figure out the right overload to use. This will likely involve disk I/O, memory allocations, analyzing their generic signatures, etc. The second time you use +, all of this has already been computed and cached, so the compiler only has to do the actual typechecking, which is a lot faster.
Because the typechecker timer also encompasses other work that’s done lazily during typechecking, it’s a very crude measure with a lot of noise and seemingly inexplicable variance. You should usually either set thresholds very high (like 500ms) so that it only tells you about expressions that are taking completely unreasonable amounts of time, or use the timer only when you specifically think there’s some kind of compilation performance problem and interpret the diagnostics with a skeptical eye.
Thanks for this detailed explanation, it explains the behavior I have in my test project.
However, in the real project I'm dealing with, I have a compiler warning on all first operator use of every file.
Here are a few examples during a single compilation, which seems to me there is another thing than "caching" operator during the first use.
In both below examples, constants are CGFloat.
titleLabel.bottomAnchor.constraint(
equalTo: bottomAnchor, constant: -style.titleToBottomSpacing
) // Expression took 169ms to type-check
optionsContainer.trailingAnchor.constraint(
equalTo: descriptionContainer.trailingAnchor, constant: -style.optionsMargins.right
) // Expression took 157ms to type-check
In that one, I've even tried all these alternatives: let maxY = -scrollView.contentOffset.y - scrollView.adjustedContentInset.top // Expression took 104ms to type-check
let maxY: CGFloat = -scrollView.contentOffset.y - scrollView.adjustedContentInset.top
--> 356ms
let maxY: CGFloat = -(scrollView.contentOffset.y + scrollView.adjustedContentInset.top)
--> 216ms
let maxY: Double = -(scrollView.contentOffset.y + scrollView.adjustedContentInset.top)
--> 351ms
let contentOffsetY: CGFloat = scrollView.contentOffset.y
let adjustedOffsetTop: CGFloat = scrollView.adjustedContentInset.top
let maxY: CGFloat = -(contentOffsetY + adjustedOffsetTop)
--> 388ms
let contentOffsetY: CGFloat = scrollView.contentOffset.y
let adjustedOffsetTop: CGFloat = scrollView.adjustedContentInset.top
let maxY: CGFloat = -contentOffsetY - adjustedOffsetTop
--> 365ms
let contentOffsetY: CGFloat = -scrollView.contentOffset.y
let adjustedOffsetTop: CGFloat = -scrollView.adjustedContentInset.top
let maxY: CGFloat = contentOffsetY + adjustedOffsetTop
--> 330ms
It wouldn't really be a problem if that was, as you explained, only on the first use of an operator, but when you have thousands of files, and each one is loosing between 100 and 300ms, global compilation time becomes really long.