From 7b5b7f10fb6a79b9e4e293550fe11e6f261704f9 Mon Sep 17 00:00:00 2001 From: =?utf8?q?G=C3=BCnther=20Foidl?= Date: Tue, 14 Apr 2020 16:58:39 +0200 Subject: [PATCH] Better codegen / perf form Math.Max and Math.Min (#33851) * 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" --- .../System.Private.CoreLib/src/System/Math.cs | 54 ++++++------- .../System.Runtime.Extensions/tests/System/Math.cs | 88 ++++++++++++++++------ 2 files changed, 88 insertions(+), 54 deletions(-) diff --git a/src/libraries/System.Private.CoreLib/src/System/Math.cs b/src/libraries/System.Private.CoreLib/src/System/Math.cs index 8432f45..9e90ad9 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Math.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Math.cs @@ -539,6 +539,7 @@ namespace System return decimal.Max(val1, val2); } + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static double Max(double val1, double val2) { // This matches the IEEE 754:2019 `maximum` function @@ -547,17 +548,17 @@ namespace System // 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] @@ -585,6 +586,7 @@ namespace System return (val1 >= val2) ? val1 : val2; } + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static float Max(float val1, float val2) { // This matches the IEEE 754:2019 `maximum` function @@ -593,17 +595,17 @@ namespace System // 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)] @@ -663,6 +665,7 @@ namespace System return decimal.Min(val1, val2); } + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static double Min(double val1, double val2) { // This matches the IEEE 754:2019 `minimum` function @@ -671,17 +674,12 @@ namespace System // 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] @@ -709,6 +707,7 @@ namespace System return (val1 <= val2) ? val1 : val2; } + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static float Min(float val1, float val2) { // This matches the IEEE 754:2019 `minimum` function @@ -717,17 +716,12 @@ namespace System // 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)] diff --git a/src/libraries/System.Runtime.Extensions/tests/System/Math.cs b/src/libraries/System.Runtime.Extensions/tests/System/Math.cs index 03a9ba8..7614d2b 100644 --- a/src/libraries/System.Runtime.Extensions/tests/System/Math.cs +++ b/src/libraries/System.Runtime.Extensions/tests/System/Math.cs @@ -1052,12 +1052,22 @@ namespace System.Tests [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); @@ -1093,12 +1103,22 @@ namespace System.Tests [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); @@ -1141,12 +1161,22 @@ namespace System.Tests [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); @@ -1182,12 +1212,22 @@ namespace System.Tests [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); -- 2.7.4