Skip to content

Commit 7a5fb4f

Browse files
authoredJul 3, 2024··
Optimize BigInteger.Parse (#97589)
* Optimize NumberToBigInteger * DisableParallelization * Trailing zero * Fix assertion * Recursive parse * Skip trailing zeros * Shrink PowersOf1e9 * OmittedLength * pre-calculate 1000000000^(1<<5) * Move PowersOf1e9 * fix * Fix large case * public * Use PowersOf1e9.MaxPartialDigits, PowersOf1e9.TenPowMaxPartial * Remove NumberBufferToBigInteger * intDigits * Use BigInteger(ReadOnlySpan<uint> value, bool negative) * base1E9 * Remove RecursiveLarge
1 parent 015b7ed commit 7a5fb4f

File tree

7 files changed

+617
-401
lines changed

7 files changed

+617
-401
lines changed
 

‎src/libraries/System.Runtime.Numerics/src/System/Number.BigInteger.cs

+459-300
Large diffs are not rendered by default.

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

+1-1
Original file line numberDiff line numberDiff line change
@@ -494,7 +494,7 @@ internal BigInteger(int n, uint[]? rgu)
494494
/// </summary>
495495
/// <param name="value">The absolute value of the number</param>
496496
/// <param name="negative">The bool indicating the sign of the value.</param>
497-
private BigInteger(ReadOnlySpan<uint> value, bool negative)
497+
internal BigInteger(ReadOnlySpan<uint> value, bool negative)
498498
{
499499
// Try to conserve space as much as possible by checking for wasted leading span entries
500500
// sometimes the span has leading zeros from bit manipulation operations & and ^

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

+2-2
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@ public static void Add(ReadOnlySpan<uint> left, ReadOnlySpan<uint> right, Span<u
5656
Add(left, bits, ref resultPtr, startIndex: i, initialCarry: carry);
5757
}
5858

59-
private static void AddSelf(Span<uint> left, ReadOnlySpan<uint> right)
59+
public static void AddSelf(Span<uint> left, ReadOnlySpan<uint> right)
6060
{
6161
Debug.Assert(left.Length >= right.Length);
6262

@@ -129,7 +129,7 @@ public static void Subtract(ReadOnlySpan<uint> left, ReadOnlySpan<uint> right, S
129129
Subtract(left, bits, ref resultPtr, startIndex: i, initialCarry: carry);
130130
}
131131

132-
private static void SubtractSelf(Span<uint> left, ReadOnlySpan<uint> right)
132+
public static void SubtractSelf(Span<uint> left, ReadOnlySpan<uint> right)
133133
{
134134
Debug.Assert(left.Length >= right.Length);
135135
// Assertion failing per https://github.com/dotnet/runtime/issues/97780

‎src/libraries/System.Runtime.Numerics/src/System/Numerics/BigIntegerCalculator.Utils.cs

+1-1
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ public static int Compare(ReadOnlySpan<uint> left, ReadOnlySpan<uint> right)
3131
return left[iv] < right[iv] ? -1 : 1;
3232
}
3333

34-
private static int ActualLength(ReadOnlySpan<uint> value)
34+
public static int ActualLength(ReadOnlySpan<uint> value)
3535
{
3636
// Since we're reusing memory here, the actual length
3737
// of a given value may be less then the array's length

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

+20-16
Original file line numberDiff line numberDiff line change
@@ -33,22 +33,6 @@ public static void RunMultiply_TwoLargeBigIntegers()
3333
}
3434
}
3535

36-
[Fact]
37-
public static void RunMultiply_TwoLargeBigIntegers_Threshold()
38-
{
39-
// Again, with lower threshold
40-
BigIntTools.Utils.RunWithFakeThreshold(BigIntegerCalculator.SquareThreshold, 8, () =>
41-
BigIntTools.Utils.RunWithFakeThreshold(BigIntegerCalculator.MultiplyKaratsubaThreshold, 8, RunMultiply_TwoLargeBigIntegers)
42-
);
43-
44-
// Again, with lower threshold
45-
BigIntTools.Utils.RunWithFakeThreshold(BigIntegerCalculator.SquareThreshold, 8, () =>
46-
BigIntTools.Utils.RunWithFakeThreshold(BigIntegerCalculator.MultiplyKaratsubaThreshold, 8, () =>
47-
BigIntTools.Utils.RunWithFakeThreshold(BigIntegerCalculator.StackAllocThreshold, 8, RunMultiply_TwoLargeBigIntegers)
48-
)
49-
);
50-
}
51-
5236
[Fact]
5337
public static void RunMultiply_TwoSmallBigIntegers()
5438
{
@@ -293,4 +277,24 @@ private static string Print(byte[] bytes)
293277
return MyBigIntImp.Print(bytes);
294278
}
295279
}
280+
281+
[Collection(nameof(DisableParallelization))]
282+
public class multiplyTestThreshold
283+
{
284+
[Fact]
285+
public static void RunMultiply_TwoLargeBigIntegers()
286+
{
287+
// Again, with lower threshold
288+
BigIntTools.Utils.RunWithFakeThreshold(BigIntegerCalculator.SquareThreshold, 8, () =>
289+
BigIntTools.Utils.RunWithFakeThreshold(BigIntegerCalculator.MultiplyKaratsubaThreshold, 8, multiplyTest.RunMultiply_TwoLargeBigIntegers)
290+
);
291+
292+
// Again, with lower threshold
293+
BigIntTools.Utils.RunWithFakeThreshold(BigIntegerCalculator.SquareThreshold, 8, () =>
294+
BigIntTools.Utils.RunWithFakeThreshold(BigIntegerCalculator.MultiplyKaratsubaThreshold, 8, () =>
295+
BigIntTools.Utils.RunWithFakeThreshold(BigIntegerCalculator.StackAllocThreshold, 8, multiplyTest.RunMultiply_TwoLargeBigIntegers)
296+
)
297+
);
298+
}
299+
}
296300
}

‎src/libraries/System.Runtime.Numerics/tests/BigInteger/parse.cs

+133-81
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,11 @@
55
using System.Diagnostics;
66
using System.Globalization;
77
using System.Tests;
8+
using System.Text.RegularExpressions;
89
using System.Threading;
910
using Microsoft.DotNet.RemoteExecutor;
1011
using Xunit;
12+
using static System.Net.Mime.MediaTypeNames;
1113

1214
namespace System.Numerics.Tests
1315
{
@@ -36,57 +38,51 @@ public static IEnumerable<object[]> Cultures
3638
[OuterLoop]
3739
public static void RunParseToStringTests(CultureInfo culture)
3840
{
39-
Test();
40-
BigIntTools.Utils.RunWithFakeThreshold(Number.s_naiveThreshold, 0, Test);
41-
42-
void Test()
43-
{
44-
byte[] tempByteArray1 = new byte[0];
45-
using (new ThreadCultureChange(culture))
41+
byte[] tempByteArray1 = new byte[0];
42+
using (new ThreadCultureChange(culture))
43+
{
44+
//default style
45+
VerifyDefaultParse(s_random);
46+
47+
//single NumberStyles
48+
VerifyNumberStyles(NumberStyles.None, s_random);
49+
VerifyNumberStyles(NumberStyles.AllowLeadingWhite, s_random);
50+
VerifyNumberStyles(NumberStyles.AllowTrailingWhite, s_random);
51+
VerifyNumberStyles(NumberStyles.AllowLeadingSign, s_random);
52+
VerifyNumberStyles(NumberStyles.AllowTrailingSign, s_random);
53+
VerifyNumberStyles(NumberStyles.AllowParentheses, s_random);
54+
VerifyNumberStyles(NumberStyles.AllowDecimalPoint, s_random);
55+
VerifyNumberStyles(NumberStyles.AllowThousands, s_random);
56+
VerifyNumberStyles(NumberStyles.AllowExponent, s_random);
57+
VerifyNumberStyles(NumberStyles.AllowCurrencySymbol, s_random);
58+
VerifyNumberStyles(NumberStyles.AllowHexSpecifier, s_random);
59+
VerifyBinaryNumberStyles(NumberStyles.AllowBinarySpecifier, s_random);
60+
61+
//composite NumberStyles
62+
VerifyNumberStyles(NumberStyles.Integer, s_random);
63+
VerifyNumberStyles(NumberStyles.HexNumber, s_random);
64+
VerifyBinaryNumberStyles(NumberStyles.BinaryNumber, s_random);
65+
VerifyNumberStyles(NumberStyles.Number, s_random);
66+
VerifyNumberStyles(NumberStyles.Float, s_random);
67+
VerifyNumberStyles(NumberStyles.Currency, s_random);
68+
VerifyNumberStyles(NumberStyles.Any, s_random);
69+
70+
//invalid number style
71+
// ******InvalidNumberStyles
72+
NumberStyles invalid = (NumberStyles)0x7c00;
73+
AssertExtensions.Throws<ArgumentException>("style", () =>
4674
{
47-
//default style
48-
VerifyDefaultParse(s_random);
49-
50-
//single NumberStyles
51-
VerifyNumberStyles(NumberStyles.None, s_random);
52-
VerifyNumberStyles(NumberStyles.AllowLeadingWhite, s_random);
53-
VerifyNumberStyles(NumberStyles.AllowTrailingWhite, s_random);
54-
VerifyNumberStyles(NumberStyles.AllowLeadingSign, s_random);
55-
VerifyNumberStyles(NumberStyles.AllowTrailingSign, s_random);
56-
VerifyNumberStyles(NumberStyles.AllowParentheses, s_random);
57-
VerifyNumberStyles(NumberStyles.AllowDecimalPoint, s_random);
58-
VerifyNumberStyles(NumberStyles.AllowThousands, s_random);
59-
VerifyNumberStyles(NumberStyles.AllowExponent, s_random);
60-
VerifyNumberStyles(NumberStyles.AllowCurrencySymbol, s_random);
61-
VerifyNumberStyles(NumberStyles.AllowHexSpecifier, s_random);
62-
VerifyBinaryNumberStyles(NumberStyles.AllowBinarySpecifier, s_random);
63-
64-
//composite NumberStyles
65-
VerifyNumberStyles(NumberStyles.Integer, s_random);
66-
VerifyNumberStyles(NumberStyles.HexNumber, s_random);
67-
VerifyBinaryNumberStyles(NumberStyles.BinaryNumber, s_random);
68-
VerifyNumberStyles(NumberStyles.Number, s_random);
69-
VerifyNumberStyles(NumberStyles.Float, s_random);
70-
VerifyNumberStyles(NumberStyles.Currency, s_random);
71-
VerifyNumberStyles(NumberStyles.Any, s_random);
72-
73-
//invalid number style
74-
// ******InvalidNumberStyles
75-
NumberStyles invalid = (NumberStyles)0x7c00;
76-
AssertExtensions.Throws<ArgumentException>("style", () =>
77-
{
78-
BigInteger.Parse("1", invalid).ToString("d");
79-
});
80-
AssertExtensions.Throws<ArgumentException>("style", () =>
81-
{
82-
BigInteger junk;
83-
BigInteger.TryParse("1", invalid, null, out junk);
84-
Assert.Equal("1", junk.ToString("d"));
85-
});
75+
BigInteger.Parse("1", invalid).ToString("d");
76+
});
77+
AssertExtensions.Throws<ArgumentException>("style", () =>
78+
{
79+
BigInteger junk;
80+
BigInteger.TryParse("1", invalid, null, out junk);
81+
Assert.Equal("1", junk.ToString("d"));
82+
});
8683

87-
//FormatProvider tests
88-
RunFormatProviderParseStrings();
89-
}
84+
//FormatProvider tests
85+
RunFormatProviderParseStrings();
9086
}
9187
}
9288

@@ -99,36 +95,23 @@ void Test()
9995
[InlineData("1\03456789", 0, 1, "1")]
10096
[InlineData("1\03456789", 0, 2, "1")]
10197
[InlineData("123456789\0", 0, 10, "123456789")]
102-
public void Parse_Subspan_Success(string input, int offset, int length, string expected)
98+
public static void Parse_Subspan_Success(string input, int offset, int length, string expected)
10399
{
104-
Test();
105-
106-
BigIntTools.Utils.RunWithFakeThreshold(Number.s_naiveThreshold, 0, Test);
107-
108-
void Test()
109-
{
110-
Eval(BigInteger.Parse(input.AsSpan(offset, length)), expected);
111-
Assert.True(BigInteger.TryParse(input.AsSpan(offset, length), out BigInteger test));
112-
Eval(test, expected);
113-
}
100+
Eval(BigInteger.Parse(input.AsSpan(offset, length)), expected);
101+
Assert.True(BigInteger.TryParse(input.AsSpan(offset, length), out BigInteger test));
102+
Eval(test, expected);
114103
}
115104

116105
[Fact]
117-
public void Parse_EmptySubspan_Fails()
106+
public static void Parse_EmptySubspan_Fails()
118107
{
119-
Test();
120-
BigIntTools.Utils.RunWithFakeThreshold(Number.s_naiveThreshold, 0, Test);
121-
122-
void Test()
123-
{
124-
BigInteger result;
108+
BigInteger result;
125109

126-
Assert.False(BigInteger.TryParse("12345".AsSpan(0, 0), out result));
127-
Assert.Equal(0, result);
110+
Assert.False(BigInteger.TryParse("12345".AsSpan(0, 0), out result));
111+
Assert.Equal(0, result);
128112

129-
Assert.False(BigInteger.TryParse([], out result));
130-
Assert.Equal(0, result);
131-
}
113+
Assert.False(BigInteger.TryParse([], out result));
114+
Assert.Equal(0, result);
132115
}
133116

134117
[Fact]
@@ -148,7 +131,7 @@ public void Parse_Hex32Bits()
148131

149132
Assert.True(BigInteger.TryParse("0F0000001", NumberStyles.HexNumber, null, out result));
150133
Assert.Equal(0xF0000001u, result);
151-
134+
152135
Assert.True(BigInteger.TryParse("F00000001", NumberStyles.HexNumber, null, out result));
153136
Assert.Equal(-0xFFFFFFFFL, result);
154137

@@ -210,16 +193,10 @@ public static IEnumerable<object[]> RegressionIssueRuntime94610_TestData()
210193

211194
[Theory]
212195
[MemberData(nameof(RegressionIssueRuntime94610_TestData))]
213-
public void RegressionIssueRuntime94610(string text)
196+
public static void RegressionIssueRuntime94610(string text)
214197
{
215198
// Regression test for: https://github.com/dotnet/runtime/issues/94610
216-
Test();
217-
BigIntTools.Utils.RunWithFakeThreshold(Number.s_naiveThreshold, 0, Test);
218-
219-
void Test()
220-
{
221-
VerifyParseToString(text, NumberStyles.Integer, true);
222-
}
199+
VerifyParseToString(text, NumberStyles.Integer, true);
223200
}
224201

225202
private static void RunFormatProviderParseStrings()
@@ -268,6 +245,19 @@ private static void VerifyDefaultParse(Random random)
268245
VerifyParseToString(GetDigitSequence(100, 1000, random));
269246
}
270247

248+
// Trailing Zero - Small
249+
VerifyParseToString("99000000000");
250+
for (int i = 0; i < s_samples; i++)
251+
{
252+
VerifyParseToString(GetDigitSequence(1, 10, random) + new string('0', random.Next(10, 50)));
253+
}
254+
255+
// Trailing Zero - Large
256+
for (int i = 0; i < s_samples; i++)
257+
{
258+
VerifyParseToString(GetDigitSequence(10, 100, random) + new string('0', random.Next(100, 1000)));
259+
}
260+
271261
// Leading White
272262
for (int i = 0; i < s_samples; i++)
273263
{
@@ -531,6 +521,13 @@ private static void VerifyNumberStyles(NumberStyles ns, Random random)
531521
VerifyParseToString(GetDigitSequence(100, 1000, random), ns, true);
532522
}
533523

524+
// Trailing Zero
525+
VerifyParseToString("99000000000", ns, true);
526+
for (int i = 0; i < s_samples; i++)
527+
{
528+
VerifyParseToString(GetDigitSequence(1, 10, random) + "1000000000", ns, true);
529+
}
530+
534531
// Leading White
535532
for (int i = 0; i < s_samples; i++)
536533
{
@@ -1169,4 +1166,59 @@ private static void Eval(BigInteger x, string expected)
11691166
Assert.Equal(expected, actual);
11701167
}
11711168
}
1169+
1170+
[Collection(nameof(DisableParallelization))]
1171+
public class parseTestThreshold
1172+
{
1173+
public static IEnumerable<object[]> Cultures => parseTest.Cultures;
1174+
[Theory]
1175+
[MemberData(nameof(Cultures))]
1176+
[OuterLoop]
1177+
public static void RunParseToStringTests(CultureInfo culture)
1178+
{
1179+
BigIntTools.Utils.RunWithFakeThreshold(Number.BigIntegerParseNaiveThreshold, 0, () =>
1180+
BigIntTools.Utils.RunWithFakeThreshold(Number.BigIntegerParseNaiveThresholdInRecursive, 10, () =>
1181+
{
1182+
parseTest.RunParseToStringTests(culture);
1183+
}));
1184+
}
1185+
1186+
[Theory]
1187+
[InlineData("123456789", 0, 9, "123456789")]
1188+
[InlineData("123456789", 0, 1, "1")]
1189+
[InlineData("123456789", 1, 3, "234")]
1190+
[InlineData("123456789", 8, 1, "9")]
1191+
[InlineData("123456789abc", 8, 1, "9")]
1192+
[InlineData("1\03456789", 0, 1, "1")]
1193+
[InlineData("1\03456789", 0, 2, "1")]
1194+
[InlineData("123456789\0", 0, 10, "123456789")]
1195+
public static void Parse_Subspan_Success(string input, int offset, int length, string expected)
1196+
{
1197+
BigIntTools.Utils.RunWithFakeThreshold(Number.BigIntegerParseNaiveThreshold, 0, () =>
1198+
BigIntTools.Utils.RunWithFakeThreshold(Number.BigIntegerParseNaiveThresholdInRecursive, 10, () =>
1199+
{
1200+
parseTest.Parse_Subspan_Success(input, offset, length, expected);
1201+
}));
1202+
}
1203+
1204+
[Fact]
1205+
public static void Parse_EmptySubspan_Fails()
1206+
{
1207+
BigIntTools.Utils.RunWithFakeThreshold(Number.BigIntegerParseNaiveThreshold, 0, () =>
1208+
BigIntTools.Utils.RunWithFakeThreshold(Number.BigIntegerParseNaiveThresholdInRecursive, 10, parseTest.Parse_EmptySubspan_Fails));
1209+
}
1210+
1211+
public static IEnumerable<object[]> RegressionIssueRuntime94610_TestData() => parseTest.RegressionIssueRuntime94610_TestData();
1212+
1213+
[Theory]
1214+
[MemberData(nameof(RegressionIssueRuntime94610_TestData))]
1215+
public static void RegressionIssueRuntime94610(string text)
1216+
{
1217+
BigIntTools.Utils.RunWithFakeThreshold(Number.BigIntegerParseNaiveThreshold, 0, () =>
1218+
BigIntTools.Utils.RunWithFakeThreshold(Number.BigIntegerParseNaiveThresholdInRecursive, 10, () =>
1219+
{
1220+
parseTest.RegressionIssueRuntime94610(text);
1221+
}));
1222+
}
1223+
}
11721224
}

‎src/libraries/System.Runtime.Numerics/tests/System.Runtime.Numerics.Tests.csproj

+1
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@
5555
<Compile Include="ComplexTests.cs" />
5656
</ItemGroup>
5757
<ItemGroup>
58+
<Compile Include="$(CommonTestPath)TestUtilities\System\DisableParallelization.cs" Link="Common\TestUtilities\System\DisableParallelization.cs" />
5859
<Compile Include="$(CommonTestPath)System\GenericMathHelpers.cs" Link="Common\System\GenericMathHelpers.cs" />
5960
<Compile Include="$(CommonTestPath)System\Diagnostics\DebuggerAttributes.cs" Link="Common\System\Diagnostics\DebuggerAttributes.cs" />
6061
<Compile Include="BigIntegerTests.GenericMath.cs" />

0 commit comments

Comments
 (0)
Please sign in to comment.