-
Notifications
You must be signed in to change notification settings - Fork 4.9k
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
Improve CopySign performance for integer types #90970
Changes from all commits
271014a
630c704
e8cee13
1dd6e3d
12274ee
725bd61
fe5b230
64044bd
3e650ba
3c1be6d
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change | ||||
---|---|---|---|---|---|---|
|
@@ -1290,22 +1290,33 @@ public static Int128 Clamp(Int128 value, Int128 min, Int128 max) | |||||
/// <inheritdoc cref="INumber{TSelf}.CopySign(TSelf, TSelf)" /> | ||||||
public static Int128 CopySign(Int128 value, Int128 sign) | ||||||
{ | ||||||
Int128 absValue = value; | ||||||
|
||||||
if (IsNegative(absValue)) | ||||||
{ | ||||||
absValue = -absValue; | ||||||
} | ||||||
|
||||||
if (IsPositive(sign)) | ||||||
#if TARGET_32BIT | ||||||
Int128 t = value; | ||||||
if (Int128.IsPositive(sign)) | ||||||
{ | ||||||
if (IsNegative(absValue)) | ||||||
if (Int128.IsNegative(t)) | ||||||
{ | ||||||
Math.ThrowNegateTwosCompOverflow(); | ||||||
t = -t; | ||||||
if (Int128.IsNegative(t)) | ||||||
{ | ||||||
Math.ThrowNegateTwosCompOverflow(); | ||||||
} | ||||||
} | ||||||
return absValue; | ||||||
return t; | ||||||
} | ||||||
return -absValue; | ||||||
if (Int128.IsPositive(t)) | ||||||
{ | ||||||
t = -t; | ||||||
} | ||||||
return t; | ||||||
#else | ||||||
// Int128.MinValue == value && 0 <= sign | ||||||
if ((long)value._upper == long.MinValue && value._lower == 0 && (long)sign._upper >= 0) | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'd just do this the simple/readable way as:
Suggested change
The JIT should take care of inlining the comparison and IsPositive check to give good codegen. |
||||||
{ | ||||||
Math.ThrowNegateTwosCompOverflow(); | ||||||
} | ||||||
return value * Math.SignOrOneIfZero((long)value._upper ^ (long)sign._upper); | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm still not a fan of this name nor of the cost for multiplication (which increases significantly as the size of the type does). I think the entire logic here can be simplified down to: if (SameSign(value, sign))
{
return value;
}
else if (value == Int128.MinValue)
{
ThrowNegateTwosCompOverflow();
}
return -value; where you have: bool SameSign(Int128 left, Int128 right)
{
// All positive values have the most significant bit clear
// All negative values have the most significant bit set
//
// (x ^ y) produces 0 if the bits are the same and 1 if they
// differ. Therefore, (x ^ y) produces a positive value if the
// signs are the same and a negative value if they differ.
return (long)(left._upper ^ right._upper) >= 0;
} This gives you up to the same 2 comparisons you currently have, but reduces the bit toggle to simply a two's complement operation (negation), which is far cheaper. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The |
||||||
#endif | ||||||
} | ||||||
|
||||||
/// <inheritdoc cref="INumber{TSelf}.Max(TSelf, TSelf)" /> | ||||||
|
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.
Int128.
is unnecessary as we are in theInt128
typeI'm notably not really a fan of this additional complexity to maintain two copies of the code. 32-bit is already extremely pessimized for
Int128
and I don't think it's worth the additional micro-optimizations here. Just have the singular code path that is consistent with the other types instead.