-
Notifications
You must be signed in to change notification settings - Fork 10.5k
[ABI] [stdlib] Remove magnitude-based overload of abs(_:). #21037
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
The standard library has two versions of the `abs(_:)` function: ``` func abs<T : SignedNumeric>(_ x: T) -> T where T.Magnitude == T func abs<T : SignedNumeric & Comparable>(_ x: T) -> T ``` The first is more specialized than the second because `T.Magnitude` is known to conform to `Comparable`. Indeed, it’s a more specialized implementation that returns `magnitude`. However, this overload behaves oddly: in the expression `abs(-8)`, the type checker will pick the first overload because it is more specialized. That’s a general guiding principle for overloading: pick the most specialized overload that works. However, to select that overload, it needs to pick a type for the literal “8” for which that overload works, and it chooses `Double`. The “obvious” answer, `Int`, doesn’t work because `Int.Magnitude == UInt`. There is a conflict between the two rules, here: we prefer more-specialized overloads (but we’ll fall back to less-specialized if those don’t work) and we prefer to use `Int` for integer literals (but we’ll fall back to `Double` if it doesn’t work). We have a few options from a type-checker perspective: 1. Consider the more-specialized-function rule to be more important 2. Consider the integer-literals-prefer-`Int` rule to be more important 3. Call the result ambiguous and make the user annotate it The type checker currently does #1, although at some point in the past it did #2. Moving forward, #1 is a better choice because it prunes the number of overloads that need to be considered: if the more-specialized overload succeeds its type-check, the others need not be considered. It’s also easier to reason about than the literal-scoring approach, because there can be a direct definition for “more specialized than” that can be reasoned about. I think we should dodge the issue by removing the more-specialized version of `abs(_:)`. Its use of `magnitude` seems unlikely to provide a significant performance benefit, and the presence of overloading either forces us to consider both overloads always (which is bad for type checker performance) or accept the regression that `abs(-8)` is `Double`. Better to eliminate the overloading and, if needed in the future, find a better way to introduce the more-specialized implementation without it being a separate signature. Fixes rdar://problem/42345366.
@swift-ci please benchmark |
@swift-ci please smoke test |
Potentially controversial standard library change here ;). Let's see if it affects benchmarks at all. |
Build comment file:Performance: -O
Code size: -O
Performance: -Osize
Code size: -Osize
Performance: -Onone
How to read the dataThe tables contain differences in performance which are larger than 8% and differences in code size which are larger than 1%.If you see any unexpected regressions, you should consider fixing the Noise: Sometimes the performance results (not code size!) contain false Hardware Overview
|
It's not only there for a performance benefit; the We can probably address this by adding a |
In a generic context, we won't get the |
For example, this does the wrong thing (prints func test<T: SignedNumeric & Comparable>(_ x: T) {
print(abs(x))
}
test(-0.0) |
Within the (single) implementation of abs(_:), dynamically check whether the numeric type and its `Magnitude` are of the same type and, if so, return the result of `magnitude`. This ensures that we do the right thing with respect to (e.g.) floating point values like -0.0, without resorting to overloading of abs(_:).
Hey @stephentyrone , I'd love your thoughts on the evil trick in ab75577 |
@swift-ci please test |
@swift-ci please benchmark |
1 similar comment
@swift-ci please benchmark |
@swift-ci please test |
Build failed |
Build comment file:Code size: -O
Code size: -Osize
Performance: -Onone
How to read the dataThe tables contain differences in performance which are larger than 8% and differences in code size which are larger than 1%.If you see any unexpected regressions, you should consider fixing the Noise: Sometimes the performance results (not code size!) contain false Hardware Overview
|
LGTM. |
There's a few things about this function that kind of stink, but your changes aren't making it any worse. We should take this as-is, but I may poke at it some more and send you another follow-on PR. |
(Also, please clone this for 5.0 when you have a chance.) |
Okay! |
The standard library has two versions of the
abs(_:)
function:The first is more specialized than the second because
T.Magnitude
isknown to conform to
Comparable
. Indeed, it’s a more specializedimplementation that returns
magnitude
.However, this overload behaves oddly: in the expression
abs(-8)
, the typechecker will pick the first overload because it is more specialized. That’s
a general guiding principle for overloading: pick the most specialized
overload that works.
However, to select that overload, it needs to pick a type for the literal
“8” for which that overload works, and it chooses
Double
. The “obvious”answer,
Int
, doesn’t work becauseInt.Magnitude == UInt
.There is a conflict between the two rules, here: we prefer more-specialized
overloads (but we’ll fall back to less-specialized if those don’t work) and we prefer to use
Int
for integer literals (but we’ll fall back toDouble
if it doesn’t work). We have a few options from a type-checkerperspective:
Int
rule to be more importantThe type checker currently does #1, although at some point in the past it
did #2. Moving forward, #1 is a better choice because it prunes the number
of overloads that need to be considered: if the more-specialized overload
succeeds its type-check, the others need not be considered. It’s also
easier to reason about than the literal-scoring approach, because there can
be a direct definition for “more specialized than” that can be reasoned
about.
I think we should dodge the issue by removing the more-specialized version
of
abs(_:)
. Its use ofmagnitude
seems unlikely to provide asignificant performance benefit, and the presence of overloading either
forces us to consider both overloads always (which is bad for type checker
performance) or accept the regression that
abs(-8)
isDouble
. Betterto eliminate the overloading and, if needed in the future, find a better
way to introduce the more-specialized implementation without it being a
separate signature.
Fixes rdar://problem/42345366.