From c88a4778f2fc2d0fcac9531345639ccf789e0a4b Mon Sep 17 00:00:00 2001 From: Daniel Svensson Date: Mon, 11 Mar 2024 11:08:12 +0100 Subject: [PATCH 1/5] Add fast path for UInt128 division on x64 when divisor is as most 64bits --- .../System.Private.CoreLib/src/System/UInt128.cs | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/src/libraries/System.Private.CoreLib/src/System/UInt128.cs b/src/libraries/System.Private.CoreLib/src/System/UInt128.cs index f988ab886ec835..dd8b282d1dfdea 100644 --- a/src/libraries/System.Private.CoreLib/src/System/UInt128.cs +++ b/src/libraries/System.Private.CoreLib/src/System/UInt128.cs @@ -8,6 +8,7 @@ using System.Numerics; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; +using System.Runtime.Intrinsics.X86; namespace System { @@ -1098,7 +1099,18 @@ public static UInt128 Log2(UInt128 value) ThrowHelper.ThrowDivideByZeroException(); } - if (left._upper == 0) + if (X86Base.X64.IsSupported) + { + ulong highRes = 0ul; + ulong remainder = left._upper; + + // We might need 2 division to avoid overflow + if (remainder >= right._lower) + (highRes, remainder) = X86Base.X64.DivRem(remainder, 0, right._lower); + + return new UInt128(highRes, X86Base.X64.DivRem(left._lower, remainder, right._lower).Quotient); + } + else if (left._upper == 0) { // left and right are both uint64 return left._lower / right._lower; From a5dd98e0c451d2c5c54f696a43b4272a3a3101b5 Mon Sep 17 00:00:00 2001 From: Daniel Svensson Date: Thu, 14 Mar 2024 22:29:10 +0100 Subject: [PATCH 2/5] Update src/libraries/System.Private.CoreLib/src/System/UInt128.cs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Günther Foidl --- src/libraries/System.Private.CoreLib/src/System/UInt128.cs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/libraries/System.Private.CoreLib/src/System/UInt128.cs b/src/libraries/System.Private.CoreLib/src/System/UInt128.cs index dd8b282d1dfdea..2fb0268af2d314 100644 --- a/src/libraries/System.Private.CoreLib/src/System/UInt128.cs +++ b/src/libraries/System.Private.CoreLib/src/System/UInt128.cs @@ -1104,9 +1104,10 @@ public static UInt128 Log2(UInt128 value) ulong highRes = 0ul; ulong remainder = left._upper; - // We might need 2 division to avoid overflow if (remainder >= right._lower) - (highRes, remainder) = X86Base.X64.DivRem(remainder, 0, right._lower); + { + (highRes, remainder) = X86Base.X64.DivRem(left._upper, 0, right._lower); + } return new UInt128(highRes, X86Base.X64.DivRem(left._lower, remainder, right._lower).Quotient); } From 60cc066279d98b13addee017bb8096e25c0202e2 Mon Sep 17 00:00:00 2001 From: Daniel Svensson Date: Fri, 7 Jun 2024 08:52:27 +0200 Subject: [PATCH 3/5] Supress warning CA2252 // This API requires opting into preview features --- src/libraries/System.Private.CoreLib/src/System/UInt128.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/libraries/System.Private.CoreLib/src/System/UInt128.cs b/src/libraries/System.Private.CoreLib/src/System/UInt128.cs index 2fb0268af2d314..34d4f18396f173 100644 --- a/src/libraries/System.Private.CoreLib/src/System/UInt128.cs +++ b/src/libraries/System.Private.CoreLib/src/System/UInt128.cs @@ -1104,12 +1104,14 @@ public static UInt128 Log2(UInt128 value) ulong highRes = 0ul; ulong remainder = left._upper; +#pragma warning disable CA2252 // This API requires opting into preview features if (remainder >= right._lower) { (highRes, remainder) = X86Base.X64.DivRem(left._upper, 0, right._lower); } return new UInt128(highRes, X86Base.X64.DivRem(left._lower, remainder, right._lower).Quotient); +#pragma warning restore CA2252 // This API requires opting into preview features } else if (left._upper == 0) { From 19b92d90876a5cc014e8149b2dbf8356623659ba Mon Sep 17 00:00:00 2001 From: Daniel Svensson Date: Tue, 11 Jun 2024 22:31:56 +0200 Subject: [PATCH 4/5] Check upper bits for 0 and do normal divion for x64 (faster when it is known that upper is zero such as division by constant) --- .../System.Private.CoreLib/src/System/UInt128.cs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/libraries/System.Private.CoreLib/src/System/UInt128.cs b/src/libraries/System.Private.CoreLib/src/System/UInt128.cs index 34d4f18396f173..a4c749101d44d8 100644 --- a/src/libraries/System.Private.CoreLib/src/System/UInt128.cs +++ b/src/libraries/System.Private.CoreLib/src/System/UInt128.cs @@ -1099,7 +1099,12 @@ public static UInt128 Log2(UInt128 value) ThrowHelper.ThrowDivideByZeroException(); } - if (X86Base.X64.IsSupported) + if (left._upper == 0) + { + // left and right are both uint64 + return left._lower / right._lower; + } + else if (X86Base.X64.IsSupported) { ulong highRes = 0ul; ulong remainder = left._upper; @@ -1113,11 +1118,6 @@ public static UInt128 Log2(UInt128 value) return new UInt128(highRes, X86Base.X64.DivRem(left._lower, remainder, right._lower).Quotient); #pragma warning restore CA2252 // This API requires opting into preview features } - else if (left._upper == 0) - { - // left and right are both uint64 - return left._lower / right._lower; - } } if (right >= left) From 6d185b31298defbb1a9f9d91bb90e129946d2c85 Mon Sep 17 00:00:00 2001 From: Daniel Svensson Date: Thu, 12 Sep 2024 17:04:00 +0200 Subject: [PATCH 5/5] Change supression to SYSLIB5004 after DivRem was changes to [Experimental] --- src/libraries/System.Private.CoreLib/src/System/UInt128.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/libraries/System.Private.CoreLib/src/System/UInt128.cs b/src/libraries/System.Private.CoreLib/src/System/UInt128.cs index cbfa1da4b4a15c..873ba65e7e5219 100644 --- a/src/libraries/System.Private.CoreLib/src/System/UInt128.cs +++ b/src/libraries/System.Private.CoreLib/src/System/UInt128.cs @@ -1109,14 +1109,14 @@ public static UInt128 Log2(UInt128 value) ulong highRes = 0ul; ulong remainder = left._upper; -#pragma warning disable CA2252 // This API requires opting into preview features +#pragma warning disable SYSLIB5004 // DivRem is marked as [Experimental], partly because it does not get optmized by the JIT for constant inputs if (remainder >= right._lower) { (highRes, remainder) = X86Base.X64.DivRem(left._upper, 0, right._lower); } return new UInt128(highRes, X86Base.X64.DivRem(left._lower, remainder, right._lower).Quotient); -#pragma warning restore CA2252 // This API requires opting into preview features +#pragma warning restore SYSLIB5004 // DivRem is marked as [Experimental] } }