Skip to content

Commit

Permalink
Better codegen / perf form Math.Max and Math.Min (#33851)
Browse files Browse the repository at this point in the history
* Forced inline of Math.Min/Max double/float

* Math.Min split in fast and slow

* Math.Min another try

* Added more tests to Math

* Picked variant "MinReorder"
  • Loading branch information
gfoidl committed Apr 14, 2020
1 parent dda0a57 commit 7b5b7f1
Show file tree
Hide file tree
Showing 2 changed files with 88 additions and 54 deletions.
54 changes: 24 additions & 30 deletions src/libraries/System.Private.CoreLib/src/System/Math.cs
Original file line number Diff line number Diff line change
Expand Up @@ -539,6 +539,7 @@ public static decimal Max(decimal val1, decimal val2)
return decimal.Max(val1, val2);
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static double Max(double val1, double val2)
{
// This matches the IEEE 754:2019 `maximum` function
Expand All @@ -547,17 +548,17 @@ public static double Max(double val1, double val2)
// otherwise returns the larger of the inputs. It
// treats +0 as larger than -0 as per the specification.

if ((val1 > val2) || double.IsNaN(val1))
if (val1 != val2)
{
return val1;
}
if (!double.IsNaN(val1))
{
return val2 < val1 ? val1 : val2;
}

if (val1 == val2)
{
return double.IsNegative(val1) ? val2 : val1;
return val1;
}

return val2;
return double.IsNegative(val2) ? val1 : val2;
}

[NonVersionable]
Expand Down Expand Up @@ -585,6 +586,7 @@ public static sbyte Max(sbyte val1, sbyte val2)
return (val1 >= val2) ? val1 : val2;
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static float Max(float val1, float val2)
{
// This matches the IEEE 754:2019 `maximum` function
Expand All @@ -593,17 +595,17 @@ public static float Max(float val1, float val2)
// otherwise returns the larger of the inputs. It
// treats +0 as larger than -0 as per the specification.

if ((val1 > val2) || float.IsNaN(val1))
if (val1 != val2)
{
return val1;
}
if (!float.IsNaN(val1))
{
return val2 < val1 ? val1 : val2;
}

if (val1 == val2)
{
return float.IsNegative(val1) ? val2 : val1;
return val1;
}

return val2;
return float.IsNegative(val2) ? val1 : val2;
}

[CLSCompliant(false)]
Expand Down Expand Up @@ -663,6 +665,7 @@ public static decimal Min(decimal val1, decimal val2)
return decimal.Min(val1, val2);
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static double Min(double val1, double val2)
{
// This matches the IEEE 754:2019 `minimum` function
Expand All @@ -671,17 +674,12 @@ public static double Min(double val1, double val2)
// otherwise returns the larger of the inputs. It
// treats +0 as larger than -0 as per the specification.

if ((val1 < val2) || double.IsNaN(val1))
{
return val1;
}

if (val1 == val2)
if (val1 != val2 && !double.IsNaN(val1))
{
return double.IsNegative(val1) ? val1 : val2;
return val1 < val2 ? val1 : val2;
}

return val2;
return double.IsNegative(val1) ? val1 : val2;
}

[NonVersionable]
Expand Down Expand Up @@ -709,6 +707,7 @@ public static sbyte Min(sbyte val1, sbyte val2)
return (val1 <= val2) ? val1 : val2;
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static float Min(float val1, float val2)
{
// This matches the IEEE 754:2019 `minimum` function
Expand All @@ -717,17 +716,12 @@ public static float Min(float val1, float val2)
// otherwise returns the larger of the inputs. It
// treats +0 as larger than -0 as per the specification.

if ((val1 < val2) || float.IsNaN(val1))
{
return val1;
}

if (val1 == val2)
if (val1 != val2 && !float.IsNaN(val1))
{
return float.IsNegative(val1) ? val1 : val2;
return val1 < val2 ? val1 : val2;
}

return val2;
return float.IsNegative(val1) ? val1 : val2;
}

[CLSCompliant(false)]
Expand Down
88 changes: 64 additions & 24 deletions src/libraries/System.Runtime.Extensions/tests/System/Math.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1052,12 +1052,22 @@ public static void Max_Decimal()

[Theory]
[InlineData(double.NegativeInfinity, double.PositiveInfinity, double.PositiveInfinity)]
[InlineData(double.MinValue, double.MaxValue, double.MaxValue)]
[InlineData(double.NaN, double.NaN, double.NaN)]
[InlineData(-0.0, 0.0, 0.0)]
[InlineData(2.0, -3.0, 2.0)]
[InlineData(3.0, -2.0, 3.0)]
[InlineData(double.PositiveInfinity, double.NaN, double.NaN)]
[InlineData(double.PositiveInfinity, double.NegativeInfinity, double.PositiveInfinity)]
[InlineData(double.MinValue, double.MaxValue, double.MaxValue)]
[InlineData(double.MaxValue, double.MinValue, double.MaxValue)]
[InlineData(double.NaN, double.NaN, double.NaN)]
[InlineData(double.NaN, 1.0, double.NaN)]
[InlineData(1.0, double.NaN, double.NaN)]
[InlineData(double.PositiveInfinity, double.NaN, double.NaN)]
[InlineData(double.NegativeInfinity, double.NaN, double.NaN)]
[InlineData(double.NaN, double.PositiveInfinity, double.NaN)]
[InlineData(double.NaN, double.NegativeInfinity, double.NaN)]
[InlineData(-0.0, 0.0, 0.0)]
[InlineData( 0.0, -0.0, 0.0)]
[InlineData( 2.0, -3.0, 2.0)]
[InlineData(-3.0, 2.0, 2.0)]
[InlineData( 3.0, -2.0, 3.0)]
[InlineData(-2.0, 3.0, 3.0)]
public static void Max_Double_NotNetFramework(double x, double y, double expectedResult)
{
AssertEqual(expectedResult, Math.Max(x, y), 0.0);
Expand Down Expand Up @@ -1093,12 +1103,22 @@ public static void Max_SByte()

[Theory]
[InlineData(float.NegativeInfinity, float.PositiveInfinity, float.PositiveInfinity)]
[InlineData(float.MinValue, float.MaxValue, float.MaxValue)]
[InlineData(float.NaN, float.NaN, float.NaN)]
[InlineData(-0.0f, 0.0f, 0.0f)]
[InlineData(2.0f, -3.0f, 2.0f)]
[InlineData(3.0f, -2.0f, 3.0f)]
[InlineData(float.PositiveInfinity, float.NaN, float.NaN)]
[InlineData(float.PositiveInfinity, float.NegativeInfinity, float.PositiveInfinity)]
[InlineData(float.MinValue, float.MaxValue, float.MaxValue)]
[InlineData(float.MaxValue, float.MinValue, float.MaxValue)]
[InlineData(float.NaN, float.NaN, float.NaN)]
[InlineData(float.NaN, 1.0, float.NaN)]
[InlineData(1.0, float.NaN, float.NaN)]
[InlineData(float.PositiveInfinity, float.NaN, float.NaN)]
[InlineData(float.NegativeInfinity, float.NaN, float.NaN)]
[InlineData(float.NaN, float.PositiveInfinity, float.NaN)]
[InlineData(float.NaN, float.NegativeInfinity, float.NaN)]
[InlineData(-0.0, 0.0, 0.0)]
[InlineData( 0.0, -0.0, 0.0)]
[InlineData( 2.0, -3.0, 2.0)]
[InlineData(-3.0, 2.0, 2.0)]
[InlineData( 3.0, -2.0, 3.0)]
[InlineData(-2.0, 3.0, 3.0)]
public static void Max_Single_NotNetFramework(float x, float y, float expectedResult)
{
AssertEqual(expectedResult, Math.Max(x, y), 0.0f);
Expand Down Expand Up @@ -1141,12 +1161,22 @@ public static void Min_Decimal()

[Theory]
[InlineData(double.NegativeInfinity, double.PositiveInfinity, double.NegativeInfinity)]
[InlineData(double.MinValue, double.MaxValue, double.MinValue)]
[InlineData(double.NaN, double.NaN, double.NaN)]
[InlineData(-0.0, 0.0, -0.0)]
[InlineData(2.0, -3.0, -3.0)]
[InlineData(3.0, -2.0, -2.0)]
[InlineData(double.PositiveInfinity, double.NaN, double.NaN)]
[InlineData(double.PositiveInfinity, double.NegativeInfinity, double.NegativeInfinity)]
[InlineData(double.MinValue, double.MaxValue, double.MinValue)]
[InlineData(double.MaxValue, double.MinValue, double.MinValue)]
[InlineData(double.NaN, double.NaN, double.NaN)]
[InlineData(double.NaN, 1.0, double.NaN)]
[InlineData(1.0, double.NaN, double.NaN)]
[InlineData(double.PositiveInfinity, double.NaN, double.NaN)]
[InlineData(double.NegativeInfinity, double.NaN, double.NaN)]
[InlineData(double.NaN, double.PositiveInfinity, double.NaN)]
[InlineData(double.NaN, double.NegativeInfinity, double.NaN)]
[InlineData(-0.0, 0.0, -0.0)]
[InlineData( 0.0, -0.0, -0.0)]
[InlineData( 2.0, -3.0, -3.0)]
[InlineData(-3.0, 2.0, -3.0)]
[InlineData( 3.0, -2.0, -2.0)]
[InlineData(-2.0, 3.0, -2.0)]
public static void Min_Double_NotNetFramework(double x, double y, double expectedResult)
{
AssertEqual(expectedResult, Math.Min(x, y), 0.0);
Expand Down Expand Up @@ -1182,12 +1212,22 @@ public static void Min_SByte()

[Theory]
[InlineData(float.NegativeInfinity, float.PositiveInfinity, float.NegativeInfinity)]
[InlineData(float.MinValue, float.MaxValue, float.MinValue)]
[InlineData(float.NaN, float.NaN, float.NaN)]
[InlineData(-0.0f, 0.0f, -0.0f)]
[InlineData(2.0f, -3.0f, -3.0f)]
[InlineData(3.0f, -2.0f, -2.0f)]
[InlineData(float.PositiveInfinity, float.NaN, float.NaN)]
[InlineData(float.PositiveInfinity, float.NegativeInfinity, float.NegativeInfinity)]
[InlineData(float.MinValue, float.MaxValue, float.MinValue)]
[InlineData(float.MaxValue, float.MinValue, float.MinValue)]
[InlineData(float.NaN, float.NaN, float.NaN)]
[InlineData(float.NaN, 1.0, float.NaN)]
[InlineData(1.0, float.NaN, float.NaN)]
[InlineData(float.PositiveInfinity, float.NaN, float.NaN)]
[InlineData(float.NegativeInfinity, float.NaN, float.NaN)]
[InlineData(float.NaN, float.PositiveInfinity, float.NaN)]
[InlineData(float.NaN, float.NegativeInfinity, float.NaN)]
[InlineData(-0.0, 0.0, -0.0)]
[InlineData( 0.0, -0.0, -0.0)]
[InlineData( 2.0, -3.0, -3.0)]
[InlineData(-3.0, 2.0, -3.0)]
[InlineData( 3.0, -2.0, -2.0)]
[InlineData(-2.0, 3.0, -2.0)]
public static void Min_Single_NotNetFramework(float x, float y, float expectedResult)
{
AssertEqual(expectedResult, Math.Min(x, y), 0.0f);
Expand Down

0 comments on commit 7b5b7f1

Please sign in to comment.