Skip to content
This repository was archived by the owner on Jan 23, 2023. It is now read-only.

Commit b79a60c

Browse files
committed
Reduce array allocations within BigIntegerCalculator
- An allocation for BigInteger.DivRem with small divisor is unnecessary, which gets fixed. BigIngeger handles these cases better now. - The square and multiply code submitted with #1436 used stackalloc, which led to stack overflows for huge numbers. With #1618 these has been changed to ordinary array allocations, which put some pressure on the managed heap. Thus, a hybrid solution should be better.
1 parent 3279ca8 commit b79a60c

File tree

4 files changed

+79
-15
lines changed

4 files changed

+79
-15
lines changed

src/System.Runtime.Numerics/src/System/Numerics/BigInteger.cs

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -945,10 +945,10 @@ public static BigInteger DivRem(BigInteger dividend, BigInteger divisor, out Big
945945

946946
if (trivialDivisor)
947947
{
948-
uint[] rest;
948+
uint rest;
949949
uint[] bits = BigIntegerCalculator.Divide(dividend._bits, NumericsHelpers.Abs(divisor._sign), out rest);
950950

951-
remainder = new BigInteger(rest, dividend._sign < 0);
951+
remainder = dividend._sign < 0 ? -1 * (long)rest : rest;
952952
return new BigInteger(bits, (dividend._sign < 0) ^ (divisor._sign < 0));
953953
}
954954

@@ -1065,12 +1065,12 @@ public static BigInteger ModPow(BigInteger value, BigInteger exponent, BigIntege
10651065

10661066
if (trivialModulus)
10671067
{
1068-
long bits = trivialValue && trivialExponent ? BigIntegerCalculator.Pow(NumericsHelpers.Abs(value._sign), NumericsHelpers.Abs(exponent._sign), NumericsHelpers.Abs(modulus._sign)) :
1068+
uint bits = trivialValue && trivialExponent ? BigIntegerCalculator.Pow(NumericsHelpers.Abs(value._sign), NumericsHelpers.Abs(exponent._sign), NumericsHelpers.Abs(modulus._sign)) :
10691069
trivialValue ? BigIntegerCalculator.Pow(NumericsHelpers.Abs(value._sign), exponent._bits, NumericsHelpers.Abs(modulus._sign)) :
10701070
trivialExponent ? BigIntegerCalculator.Pow(value._bits, NumericsHelpers.Abs(exponent._sign), NumericsHelpers.Abs(modulus._sign)) :
10711071
BigIntegerCalculator.Pow(value._bits, exponent._bits, NumericsHelpers.Abs(modulus._sign));
10721072

1073-
return value._sign < 0 && !exponent.IsEven ? -1 * bits : bits;
1073+
return value._sign < 0 && !exponent.IsEven ? -1 * (long)bits : bits;
10741074
}
10751075
else
10761076
{
@@ -1718,8 +1718,8 @@ private static BigInteger Subtract(uint[] leftBits, int leftSign, uint[] rightBi
17181718

17191719
if (trivialDivisor)
17201720
{
1721-
long bits = BigIntegerCalculator.Remainder(dividend._bits, NumericsHelpers.Abs(divisor._sign));
1722-
return dividend._sign < 0 ? -1 * bits : bits;
1721+
uint bits = BigIntegerCalculator.Remainder(dividend._bits, NumericsHelpers.Abs(divisor._sign));
1722+
return dividend._sign < 0 ? -1 * (long)bits : bits;
17231723
}
17241724

17251725
if (dividend._bits.Length < divisor._bits.Length)

src/System.Runtime.Numerics/src/System/Numerics/BigIntegerCalculator.DivRem.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ namespace System.Numerics
66
internal static partial class BigIntegerCalculator
77
{
88
public static uint[] Divide(uint[] left, uint right,
9-
out uint[] remainder)
9+
out uint remainder)
1010
{
1111
Debug.Assert(left != null);
1212
Debug.Assert(left.Length >= 1);
@@ -24,7 +24,7 @@ public static uint[] Divide(uint[] left, uint right,
2424
quotient[i] = (uint)(value / right);
2525
carry = value % right;
2626
}
27-
remainder = new uint[] { (uint)carry };
27+
remainder = (uint)carry;
2828

2929
return quotient;
3030
}

src/System.Runtime.Numerics/src/System/Numerics/BigIntegerCalculator.SquMul.cs

Lines changed: 61 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ public unsafe static uint[] Square(uint[] value)
2626

2727
// Mutable for unit testing...
2828
private static int SquareThreshold = 32;
29+
private static int AllocationThreshold = 4096;
2930

3031
[SecuritySafeCritical]
3132
private unsafe static void Square(uint* value, int valueLength,
@@ -111,9 +112,12 @@ private unsafe static void Square(uint* value, int valueLength,
111112

112113
int foldLength = valueHighLength + 1;
113114
int coreLength = foldLength + foldLength;
114-
fixed (uint* fold = new uint[foldLength],
115-
core = new uint[coreLength])
115+
116+
if (coreLength < AllocationThreshold)
116117
{
118+
uint* fold = stackalloc uint[foldLength];
119+
uint* core = stackalloc uint[coreLength];
120+
117121
// ... compute z_a = a_1 + a_0 (call it fold...)
118122
Add(valueHigh, valueHighLength,
119123
valueLow, valueLowLength,
@@ -129,6 +133,27 @@ private unsafe static void Square(uint* value, int valueLength,
129133
// ... and finally merge the result! :-)
130134
AddSelf(bits + n, bitsLength - n, core, coreLength);
131135
}
136+
else
137+
{
138+
fixed (uint* fold = new uint[foldLength],
139+
core = new uint[coreLength])
140+
{
141+
// ... compute z_a = a_1 + a_0 (call it fold...)
142+
Add(valueHigh, valueHighLength,
143+
valueLow, valueLowLength,
144+
fold, foldLength);
145+
146+
// ... compute z_1 = z_a * z_a - z_0 - z_2
147+
Square(fold, foldLength,
148+
core, coreLength);
149+
SubtractCore(bitsHigh, bitsHighLength,
150+
bitsLow, bitsLowLength,
151+
core, coreLength);
152+
153+
// ... and finally merge the result! :-)
154+
AddSelf(bits + n, bitsLength - n, core, coreLength);
155+
}
156+
}
132157
}
133158
}
134159

@@ -270,10 +295,13 @@ private unsafe static void Multiply(uint* left, int leftLength,
270295
int leftFoldLength = leftHighLength + 1;
271296
int rightFoldLength = rightHighLength + 1;
272297
int coreLength = leftFoldLength + rightFoldLength;
273-
fixed (uint* leftFold = new uint[leftFoldLength],
274-
rightFold = new uint[rightFoldLength],
275-
core = new uint[coreLength])
298+
299+
if (coreLength < AllocationThreshold)
276300
{
301+
uint* leftFold = stackalloc uint[leftFoldLength];
302+
uint* rightFold = stackalloc uint[rightFoldLength];
303+
uint* core = stackalloc uint[coreLength];
304+
277305
// ... compute z_a = a_1 + a_0 (call it fold...)
278306
Add(leftHigh, leftHighLength,
279307
leftLow, leftLowLength,
@@ -295,6 +323,34 @@ private unsafe static void Multiply(uint* left, int leftLength,
295323
// ... and finally merge the result! :-)
296324
AddSelf(bits + n, bitsLength - n, core, coreLength);
297325
}
326+
else
327+
{
328+
fixed (uint* leftFold = new uint[leftFoldLength],
329+
rightFold = new uint[rightFoldLength],
330+
core = new uint[coreLength])
331+
{
332+
// ... compute z_a = a_1 + a_0 (call it fold...)
333+
Add(leftHigh, leftHighLength,
334+
leftLow, leftLowLength,
335+
leftFold, leftFoldLength);
336+
337+
// ... compute z_b = b_1 + b_0 (call it fold...)
338+
Add(rightHigh, rightHighLength,
339+
rightLow, rightLowLength,
340+
rightFold, rightFoldLength);
341+
342+
// ... compute z_1 = z_a * z_b - z_0 - z_2
343+
Multiply(leftFold, leftFoldLength,
344+
rightFold, rightFoldLength,
345+
core, coreLength);
346+
SubtractCore(bitsHigh, bitsHighLength,
347+
bitsLow, bitsLowLength,
348+
core, coreLength);
349+
350+
// ... and finally merge the result! :-)
351+
AddSelf(bits + n, bitsLength - n, core, coreLength);
352+
}
353+
}
298354
}
299355
}
300356

src/System.Runtime.Numerics/tests/BigInteger/multiply.cs

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -38,8 +38,16 @@ public static void RunMultiply_TwoLargeBigIntegers()
3838
public static void RunMultiply_TwoLargeBigIntegers_Threshold()
3939
{
4040
// Again, with lower threshold
41-
BigIntTools.Utils.RunWithFakeThreshold("SquareThreshold", 8, RunMultiply_TwoLargeBigIntegers);
42-
BigIntTools.Utils.RunWithFakeThreshold("MultiplyThreshold", 8, RunMultiply_TwoLargeBigIntegers);
41+
BigIntTools.Utils.RunWithFakeThreshold("SquareThreshold", 8, () =>
42+
BigIntTools.Utils.RunWithFakeThreshold("MultiplyThreshold", 8, RunMultiply_TwoLargeBigIntegers)
43+
);
44+
45+
// Again, with lower threshold
46+
BigIntTools.Utils.RunWithFakeThreshold("SquareThreshold", 8, () =>
47+
BigIntTools.Utils.RunWithFakeThreshold("MultiplyThreshold", 8, () =>
48+
BigIntTools.Utils.RunWithFakeThreshold("AllocationThreshold", 8, RunMultiply_TwoLargeBigIntegers)
49+
)
50+
);
4351
}
4452

4553
[Fact]

0 commit comments

Comments
 (0)