From a747ad92517186ddf386d13974252e072e26d045 Mon Sep 17 00:00:00 2001 From: Adam Sitnik Date: Wed, 19 Jul 2023 16:43:56 +0200 Subject: [PATCH 1/2] avoid boundaries checks by using Unsafe --- .../Numerics/BigIntegerCalculator.DivRem.cs | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigIntegerCalculator.DivRem.cs b/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigIntegerCalculator.DivRem.cs index bddeb75298f48..053bace469224 100644 --- a/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigIntegerCalculator.DivRem.cs +++ b/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigIntegerCalculator.DivRem.cs @@ -3,6 +3,8 @@ using System.Buffers; using System.Diagnostics; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; namespace System.Numerics { @@ -16,14 +18,14 @@ public static void Divide(ReadOnlySpan left, uint right, Span quotie // Executes the division for one big and one 32-bit integer. // Thus, we've similar code than below, but there is no loop for // processing the 32-bit integer, since it's a single element. - + ref uint quotientPtr = ref MemoryMarshal.GetReference(quotient); ulong carry = 0UL; for (int i = left.Length - 1; i >= 0; i--) { ulong value = (carry << 32) | left[i]; ulong digit = value / right; - quotient[i] = (uint)digit; + Unsafe.Add(ref quotientPtr, i) = (uint)digit; carry = value - digit * right; } remainder = (uint)carry; @@ -35,13 +37,14 @@ public static void Divide(ReadOnlySpan left, uint right, Span quotie Debug.Assert(quotient.Length == left.Length); // Same as above, but only computing the quotient. - + ref uint quotientPtr = ref MemoryMarshal.GetReference(quotient); ulong carry = 0UL; + for (int i = left.Length - 1; i >= 0; i--) { ulong value = (carry << 32) | left[i]; ulong digit = value / right; - quotient[i] = (uint)digit; + Unsafe.Add(ref quotientPtr, i) = (uint)digit; carry = value - digit * right; } } @@ -199,11 +202,12 @@ private static uint AddDivisor(Span left, ReadOnlySpan right) // Repairs the dividend, if the last subtract was too much + ref uint leftPtr = ref MemoryMarshal.GetReference(left); ulong carry = 0UL; for (int i = 0; i < right.Length; i++) { - ref uint leftElement = ref left[i]; + ref uint leftElement = ref Unsafe.Add(ref leftPtr, i); ulong digit = (leftElement + carry) + right[i]; leftElement = unchecked((uint)digit); carry = digit >> 32; @@ -220,6 +224,7 @@ private static uint SubtractDivisor(Span left, ReadOnlySpan right, u // Combines a subtract and a multiply operation, which is naturally // more efficient than multiplying and then subtracting... + ref uint leftPtr = ref MemoryMarshal.GetReference(left); ulong carry = 0UL; for (int i = 0; i < right.Length; i++) @@ -227,7 +232,7 @@ private static uint SubtractDivisor(Span left, ReadOnlySpan right, u carry += right[i] * q; uint digit = unchecked((uint)carry); carry >>= 32; - ref uint leftElement = ref left[i]; + ref uint leftElement = ref Unsafe.Add(ref leftPtr, i); if (leftElement < digit) ++carry; leftElement = unchecked(leftElement - digit); From d4c0d4cbb084fb3240be8d0de3848c79f35602b2 Mon Sep 17 00:00:00 2001 From: Adam Sitnik Date: Wed, 19 Jul 2023 20:56:38 +0200 Subject: [PATCH 2/2] apply suggested optimization that removes one branch --- .../src/System/Numerics/BigIntegerCalculator.DivRem.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigIntegerCalculator.DivRem.cs b/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigIntegerCalculator.DivRem.cs index 053bace469224..75e237d77f185 100644 --- a/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigIntegerCalculator.DivRem.cs +++ b/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigIntegerCalculator.DivRem.cs @@ -233,9 +233,9 @@ private static uint SubtractDivisor(Span left, ReadOnlySpan right, u uint digit = unchecked((uint)carry); carry >>= 32; ref uint leftElement = ref Unsafe.Add(ref leftPtr, i); - if (leftElement < digit) - ++carry; - leftElement = unchecked(leftElement - digit); + ulong newDigit = unchecked((ulong)leftElement - digit); + carry += (newDigit >> 32) & 0x1; // This is the same as if (leftElement < digit) ++carry + leftElement = unchecked((uint)newDigit); } return (uint)carry;