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

Commit 1d59ab4

Browse files
committed
Improve performance of BigInteger.Pow/ModPow
- To introduce further performance tweaks, the exponentiation algorithms are rewritten and ported to `BigIntegerCalculator`. - To scale better for bigger numbers a `FastReducer` is in use instead of "ordinary" modulo operations, which is based on multiplications. - Furthermore the newly introduced `FastReducer` triggers a bad corner case within the division algorithm, which gets fixed too. - A performance regression at 64 bits within integer division was found, which gets fixed too (no more allocations within that code). - The test code for threshold values of square / multiply / modpow now modifies these thresholds for more thorough testing.
1 parent 39cbff9 commit 1d59ab4

11 files changed

+987
-370
lines changed

src/System.Runtime.Numerics/src/System.Runtime.Numerics.csproj

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,10 @@
1919
</PropertyGroup>
2020
<ItemGroup>
2121
<Compile Include="System\Numerics\BigIntegerCalculator.AddSub.cs" />
22+
<Compile Include="System\Numerics\BigIntegerCalculator.BitsBuffer.cs" />
2223
<Compile Include="System\Numerics\BigIntegerCalculator.DivRem.cs" />
24+
<Compile Include="System\Numerics\BigIntegerCalculator.FastReducer.cs" />
25+
<Compile Include="System\Numerics\BigIntegerCalculator.PowMod.cs" />
2326
<Compile Include="System\Numerics\BigIntegerCalculator.SquMul.cs" />
2427
<Compile Include="System\Numerics\BigInteger.cs" />
2528
<Compile Include="System\Numerics\BigIntegerBuilder.cs" />

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

Lines changed: 34 additions & 168 deletions
Original file line numberDiff line numberDiff line change
@@ -1049,45 +1049,6 @@ public static BigInteger Min(BigInteger left, BigInteger right)
10491049
return right;
10501050
}
10511051

1052-
1053-
private static void ModPowUpdateResult(ref BigIntegerBuilder regRes, ref BigIntegerBuilder regVal, ref BigIntegerBuilder regMod, ref BigIntegerBuilder regTmp)
1054-
{
1055-
NumericsHelpers.Swap(ref regRes, ref regTmp);
1056-
regRes.Mul(ref regTmp, ref regVal); // result = result * value;
1057-
regRes.Mod(ref regMod); // result = result % modulus;
1058-
}
1059-
1060-
private static void ModPowSquareModValue(ref BigIntegerBuilder regVal, ref BigIntegerBuilder regMod, ref BigIntegerBuilder regTmp)
1061-
{
1062-
NumericsHelpers.Swap(ref regVal, ref regTmp);
1063-
regVal.Mul(ref regTmp, ref regTmp); // value = value * value;
1064-
regVal.Mod(ref regMod); // value = value % modulus;
1065-
}
1066-
1067-
private static void ModPowInner(uint exp, ref BigIntegerBuilder regRes, ref BigIntegerBuilder regVal, ref BigIntegerBuilder regMod, ref BigIntegerBuilder regTmp)
1068-
{
1069-
while (exp != 0) // !(Exponent.IsZero)
1070-
{
1071-
if ((exp & 1) == 1) // !(Exponent.IsEven)
1072-
ModPowUpdateResult(ref regRes, ref regVal, ref regMod, ref regTmp);
1073-
if (exp == 1) // Exponent.IsOne - we can exit early
1074-
break;
1075-
ModPowSquareModValue(ref regVal, ref regMod, ref regTmp);
1076-
exp >>= 1;
1077-
}
1078-
}
1079-
1080-
private static void ModPowInner32(uint exp, ref BigIntegerBuilder regRes, ref BigIntegerBuilder regVal, ref BigIntegerBuilder regMod, ref BigIntegerBuilder regTmp)
1081-
{
1082-
for (int i = 0; i < 32; i++)
1083-
{
1084-
if ((exp & 1) == 1) // !(Exponent.IsEven)
1085-
ModPowUpdateResult(ref regRes, ref regVal, ref regMod, ref regTmp);
1086-
ModPowSquareModValue(ref regVal, ref regMod, ref regTmp);
1087-
exp >>= 1;
1088-
}
1089-
}
1090-
10911052
public static BigInteger ModPow(BigInteger value, BigInteger exponent, BigInteger modulus)
10921053
{
10931054
if (exponent.Sign < 0)
@@ -1098,36 +1059,31 @@ public static BigInteger ModPow(BigInteger value, BigInteger exponent, BigIntege
10981059
exponent.AssertValid();
10991060
modulus.AssertValid();
11001061

1101-
int signRes = +1;
1102-
int signVal = +1;
1103-
int signMod = +1;
1104-
bool expIsEven = exponent.IsEven;
1105-
BigIntegerBuilder regRes = new BigIntegerBuilder(BigInteger.One, ref signRes);
1106-
BigIntegerBuilder regVal = new BigIntegerBuilder(value, ref signVal);
1107-
BigIntegerBuilder regMod = new BigIntegerBuilder(modulus, ref signMod);
1108-
BigIntegerBuilder regTmp = new BigIntegerBuilder(regVal.Size);
1062+
bool trivialValue = value._bits == null;
1063+
bool trivialExponent = exponent._bits == null;
1064+
bool trivialModulus = modulus._bits == null;
11091065

1110-
regRes.Mod(ref regMod); // Handle special case of exponent=0, modulus=1
1066+
if (trivialModulus)
1067+
{
1068+
long bits = trivialValue && trivialExponent ? BigIntegerCalculator.Pow(NumericsHelpers.Abs(value._sign), NumericsHelpers.Abs(exponent._sign), NumericsHelpers.Abs(modulus._sign)) :
1069+
trivialValue ? BigIntegerCalculator.Pow(NumericsHelpers.Abs(value._sign), exponent._bits, NumericsHelpers.Abs(modulus._sign)) :
1070+
trivialExponent ? BigIntegerCalculator.Pow(value._bits, NumericsHelpers.Abs(exponent._sign), NumericsHelpers.Abs(modulus._sign)) :
1071+
BigIntegerCalculator.Pow(value._bits, exponent._bits, NumericsHelpers.Abs(modulus._sign));
11111072

1112-
if (exponent._bits == null)
1113-
{ // exponent fits into an Int32
1114-
ModPowInner((uint)exponent._sign, ref regRes, ref regVal, ref regMod, ref regTmp);
1073+
return value._sign < 0 && !exponent.IsEven ? -1 * bits : bits;
11151074
}
11161075
else
1117-
{ // very large exponent
1118-
int len = Length(exponent._bits);
1119-
for (int i = 0; i < len - 1; i++)
1120-
{
1121-
uint exp = exponent._bits[i];
1122-
ModPowInner32(exp, ref regRes, ref regVal, ref regMod, ref regTmp);
1123-
}
1124-
ModPowInner(exponent._bits[len - 1], ref regRes, ref regVal, ref regMod, ref regTmp);
1125-
}
1076+
{
1077+
uint[] bits = trivialValue && trivialExponent ? BigIntegerCalculator.Pow(NumericsHelpers.Abs(value._sign), NumericsHelpers.Abs(exponent._sign), modulus._bits) :
1078+
trivialValue ? BigIntegerCalculator.Pow(NumericsHelpers.Abs(value._sign), exponent._bits, modulus._bits) :
1079+
trivialExponent ? BigIntegerCalculator.Pow(value._bits, NumericsHelpers.Abs(exponent._sign), modulus._bits) :
1080+
BigIntegerCalculator.Pow(value._bits, exponent._bits, modulus._bits);
11261081

1127-
return regRes.GetInteger(value._sign > 0 ? +1 : expIsEven ? +1 : -1);
1082+
return new BigInteger(bits, value._sign < 0 && !exponent.IsEven);
1083+
}
11281084
}
11291085

1130-
public static BigInteger Pow(BigInteger value, Int32 exponent)
1086+
public static BigInteger Pow(BigInteger value, int exponent)
11311087
{
11321088
if (exponent < 0)
11331089
throw new ArgumentOutOfRangeException("exponent", SR.ArgumentOutOfRange_MustBeNonNeg);
@@ -1136,75 +1092,27 @@ public static BigInteger Pow(BigInteger value, Int32 exponent)
11361092
value.AssertValid();
11371093

11381094
if (exponent == 0)
1139-
return BigInteger.One;
1095+
return s_bnOneInt;
11401096
if (exponent == 1)
11411097
return value;
1142-
if (value._bits == null)
1098+
1099+
bool trivialValue = value._bits == null;
1100+
1101+
if (trivialValue)
11431102
{
11441103
if (value._sign == 1)
11451104
return value;
11461105
if (value._sign == -1)
1147-
return (exponent & 1) != 0 ? value : 1;
1106+
return (exponent & 1) != 0 ? value : s_bnOneInt;
11481107
if (value._sign == 0)
11491108
return value;
11501109
}
11511110

1152-
int sign = +1;
1153-
BigIntegerBuilder regSquare = new BigIntegerBuilder(value, ref sign);
1154-
1155-
// Get an estimate of the size needed for regSquare and regRes, so we can minimize allocations.
1156-
int cuSquareMin = regSquare.Size;
1157-
int cuSquareMax = cuSquareMin;
1158-
uint uSquareMin = regSquare.High;
1159-
uint uSquareMax = uSquareMin + 1;
1160-
if (uSquareMax == 0)
1161-
{
1162-
cuSquareMax++;
1163-
uSquareMax = 1;
1164-
}
1165-
int cuResMin = 1;
1166-
int cuResMax = 1;
1167-
uint uResMin = 1;
1168-
uint uResMax = 1;
1169-
1170-
for (int expTmp = exponent; ;)
1171-
{
1172-
if ((expTmp & 1) != 0)
1173-
{
1174-
MulUpper(ref uResMax, ref cuResMax, uSquareMax, cuSquareMax);
1175-
MulLower(ref uResMin, ref cuResMin, uSquareMin, cuSquareMin);
1176-
}
1177-
1178-
if ((expTmp >>= 1) == 0)
1179-
break;
1180-
1181-
MulUpper(ref uSquareMax, ref cuSquareMax, uSquareMax, cuSquareMax);
1182-
MulLower(ref uSquareMin, ref cuSquareMin, uSquareMin, cuSquareMin);
1183-
}
1184-
1185-
if (cuResMax > 1)
1186-
regSquare.EnsureWritable(cuResMax, 0);
1187-
BigIntegerBuilder regTmp = new BigIntegerBuilder(cuResMax);
1188-
BigIntegerBuilder regRes = new BigIntegerBuilder(cuResMax);
1189-
regRes.Set(1);
1190-
1191-
if ((exponent & 1) == 0)
1192-
sign = +1;
1193-
1194-
for (int expTmp = exponent; ;)
1195-
{
1196-
if ((expTmp & 1) != 0)
1197-
{
1198-
NumericsHelpers.Swap(ref regRes, ref regTmp);
1199-
regRes.Mul(ref regSquare, ref regTmp);
1200-
}
1201-
if ((expTmp >>= 1) == 0)
1202-
break;
1203-
NumericsHelpers.Swap(ref regSquare, ref regTmp);
1204-
regSquare.Mul(ref regTmp, ref regTmp);
1205-
}
1111+
uint[] bits = trivialValue
1112+
? BigIntegerCalculator.Pow(NumericsHelpers.Abs(value._sign), NumericsHelpers.Abs(exponent))
1113+
: BigIntegerCalculator.Pow(value._bits, NumericsHelpers.Abs(exponent));
12061114

1207-
return regRes.GetInteger(sign);
1115+
return new BigInteger(bits, value._sign < 0 && (exponent & 1) != 0);
12081116
}
12091117

12101118
#endregion public static methods
@@ -1810,8 +1718,8 @@ private static BigInteger Subtract(uint[] leftBits, int leftSign, uint[] rightBi
18101718

18111719
if (trivialDivisor)
18121720
{
1813-
uint[] bits = BigIntegerCalculator.Remainder(dividend._bits, NumericsHelpers.Abs(divisor._sign));
1814-
return new BigInteger(bits, dividend._sign < 0);
1721+
long bits = BigIntegerCalculator.Remainder(dividend._bits, NumericsHelpers.Abs(divisor._sign));
1722+
return dividend._sign < 0 ? -1 * bits : bits;
18151723
}
18161724

18171725
if (dividend._bits.Length < divisor._bits.Length)
@@ -2032,11 +1940,10 @@ private void SetBitsFromDouble(Double value)
20321940
[Pure]
20331941
internal static int Length(uint[] rgu)
20341942
{
2035-
int cu = rgu.Length;
2036-
if (rgu[cu - 1] != 0)
2037-
return cu;
2038-
Debug.Assert(cu >= 2 && rgu[cu - 2] != 0);
2039-
return cu - 1;
1943+
Debug.Assert(rgu[rgu.Length - 1] != 0);
1944+
1945+
// no leading zeros
1946+
return rgu.Length;
20401947
}
20411948

20421949
internal int _Sign { get { return _sign; } }
@@ -2088,47 +1995,6 @@ private static bool GetPartsForBitManipulation(ref BigInteger x, out uint[] xd,
20881995
return x._sign < 0;
20891996
}
20901997

2091-
// Gets an upper bound on the product of uHiRes * (2^32)^(cuRes-1) and uHiMul * (2^32)^(cuMul-1).
2092-
// The result is put int uHiRes and cuRes.
2093-
private static void MulUpper(ref uint uHiRes, ref int cuRes, uint uHiMul, int cuMul)
2094-
{
2095-
ulong uu = (ulong)uHiRes * uHiMul;
2096-
uint uHi = NumericsHelpers.GetHi(uu);
2097-
if (uHi != 0)
2098-
{
2099-
if (NumericsHelpers.GetLo(uu) != 0 && ++uHi == 0)
2100-
{
2101-
uHi = 1;
2102-
cuRes++;
2103-
}
2104-
uHiRes = uHi;
2105-
cuRes += cuMul;
2106-
}
2107-
else
2108-
{
2109-
uHiRes = NumericsHelpers.GetLo(uu);
2110-
cuRes += cuMul - 1;
2111-
}
2112-
}
2113-
2114-
// Gets a lower bound on the product of uHiRes * (2^32)^(cuRes-1) and uHiMul * (2^32)^(cuMul-1).
2115-
// The result is put int uHiRes and cuRes.
2116-
private static void MulLower(ref uint uHiRes, ref int cuRes, uint uHiMul, int cuMul)
2117-
{
2118-
ulong uu = (ulong)uHiRes * uHiMul;
2119-
uint uHi = NumericsHelpers.GetHi(uu);
2120-
if (uHi != 0)
2121-
{
2122-
uHiRes = uHi;
2123-
cuRes += cuMul;
2124-
}
2125-
else
2126-
{
2127-
uHiRes = NumericsHelpers.GetLo(uu);
2128-
cuRes += cuMul - 1;
2129-
}
2130-
}
2131-
21321998
internal static int GetDiffLength(uint[] rgu1, uint[] rgu2, int cu)
21331999
{
21342000
for (int iv = cu; --iv >= 0;)

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -197,6 +197,7 @@ private unsafe static void Subtract(uint* left, int leftLength,
197197
bits[i] = (uint)digit;
198198
carry = digit >> 32;
199199
}
200+
200201
Debug.Assert(carry == 0);
201202
}
202203

0 commit comments

Comments
 (0)