-
Notifications
You must be signed in to change notification settings - Fork 10.5k
[stdlib] Implement efficient DoubleWidth division and fix division-related bugs #14219
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
|
||
if isNegative { | ||
let (lowComplement, overflow) = (~low).addingReportingOverflow(1) | ||
return (~high + (overflow ? 1 : 0), lowComplement) | ||
return (~high + (overflow ? 1 : 0 as DoubleWidth), lowComplement) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
(Silences a compiler warning about deprecated Strideable
operators.)
where Base.Words : Collection, Base.Magnitude.Words : Collection { | ||
public struct DoubleWidth<Base : FixedWidthInteger> : | ||
_ExpressibleByBuiltinIntegerLiteral | ||
where Base.Magnitude : UnsignedInteger, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
SignedInteger
doesn't (and shouldn't, in fact) have the constraint Magnitude : UnsignedInteger
.
But for DoubleWidth
, we've assumed this constraint in many places (reasonable for SignedInteger & FixedWidthInteger
), so we'd better state it. It's necessary now because division helper methods only need to be available for Magnitude
, so with this constraint they'll only need to be defined in an extension for unsigned types.
@swift-ci Please smoke test |
1 similar comment
@swift-ci Please smoke test |
@swift-ci Please smoke benchmark |
1 similar comment
@swift-ci Please smoke benchmark |
@swift-ci Please smoke benchmark |
@swift-ci Please smoke benchmark |
Per #14220, apparently the benchmarks are currently all sticking at CSV parsing. Something to do with the recent String guts changes? |
} | ||
let quotient_ = (self.high < (0 as High)) != (other.high < (0 as High)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think extracting at least this line into a isNegative
variable would help readability, even leaving aside the nested ?:
s.
guard DoubleWidth.isSigned else { | ||
return (DoubleWidth(quotient), DoubleWidth(remainder)) | ||
} | ||
let quotient_ = (self.high < (0 as High)) != (other.high.high < (0 as High)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This part looks sufficiently similar to the one above (inside quotientAndRemainder
. I wonder if it can be extracted into a separate @_inline(always)
function without affecting performance.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Unfortunately, not possible because they’re dealing with different types (this is a heterogeneous operation).
@@ -531,7 +536,12 @@ extension DoubleWidth : FixedWidthInteger { | |||
|
|||
@_inlineable // FIXME(sil-serialize-all) | |||
public static func &>>=(lhs: inout DoubleWidth, rhs: DoubleWidth) { | |||
let rhs = rhs & DoubleWidth(DoubleWidth.bitWidth &- 1) | |||
// FIXME(integers): test types with bit widths that aren't powers of 2 | |||
let rhs = DoubleWidth.bitWidth.nonzeroBitCount == 1 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This masking piece should also be extracted into a function, I think, which can have an additional benefit of a descriptive name.
if _slowPath(rhs == (0 as ${Self})) { | ||
_preconditionFailure("Remainder of division by zero") | ||
_preconditionFailure("Division by zero") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would argue that the old message was a bit more helpful.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This was to align with what you get when you write ‘42 % 0’ or ‘42 / 0’—I think it’s important to be clear that the remainder never overflows; but let me see if I can improve on it a little bit.
) { | ||
_preconditionFailure("Overflow in remainder of division") | ||
if _slowPath(${'lhs == %s.min && rhs == (-1 as %s)' % (Self, Self)}) { | ||
_preconditionFailure("Division results in an overflow") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Same here. With the new messages it is impossible to tell which one of the operations failed in the expression like a / b % c
.
@swift-ci Please smoke benchmark |
Build comment file:Optimized (O)Regression (8)
Improvement (15)
No Changes (332)
Unoptimized (Onone)Regression (17)
Improvement (21)
No Changes (317)
Hardware Overview
|
Nice :) |
@swift-ci Please smoke test and merge |
1 similar comment
@swift-ci Please smoke test and merge |
This PR implements more efficient
DoubleWidth
division using full-width division primitives available onBase
. It's one chunk of changes from #13784, which CI won't test anymore.Also in this PR:
{U}Int64.dividingFullWidth
in terms ofDoubleWidth<{U}Int32>.dividingFullWidth
(which is required to avoid circularity).Existing
DoubleWidth
tests cover this change.remainderReportingOverflow
for builtin types when dividing by-1
.A test is added to cover this change.
DoubleWidth
masking shifts.No types exist to test this change.