Skip to content

Unify parsing part of BigInteger with CoreLib - #85978

Merged
adamsitnik merged 20 commits into
dotnet:mainfrom
huoyaoyuan:numerics-format-1
Nov 29, 2023
Merged

Unify parsing part of BigInteger with CoreLib#85978
adamsitnik merged 20 commits into
dotnet:mainfrom
huoyaoyuan:numerics-format-1

Conversation

@huoyaoyuan

Copy link
Copy Markdown
Member

Part of #28657. This is my third attempt of working on this. I'd like to keep formatting and further cleanup to follow-up PRs to help reviewing.

This PR does not refactor the algorithm part. It just adapts corelib-style patterns for BigInteger. Reviewing commit by commit is recommended.

@ghostghost added area-System.Numerics community-contribution Indicates that the PR has been added by a community member labels May 9, 2023
@ghost

ghost commented May 9, 2023

Copy link
Copy Markdown

Tagging subscribers to this area: @dotnet/area-system-numerics
See info in area-owners.md if you want to be subscribed.

Issue Details

Part of #28657. This is my third attempt of working on this. I'd like to keep formatting and further cleanup to follow-up PRs to help reviewing.

This PR does not refactor the algorithm part. It just adapts corelib-style patterns for BigInteger. Reviewing commit by commit is recommended.

Author:huoyaoyuan
Assignees:-
Labels:

area-System.Numerics

Milestone:-

@huoyaoyuan

Copy link
Copy Markdown
MemberAuthor

Also asking a question about formatting code here: the following pattern is heavily used in formatting code to share between UTF8 and UTF16:

privatestaticvoidFormatNumber<TChar>(refValueListBuilder<TChar>vlb,refNumberBuffernumber,intnMaxDigits,NumberFormatInfoinfo)whereTChar: unmanaged,IUtfChar<TChar>

What's the best approach to share those code out of CoreLib? Using #ifdef?

@huoyaoyuan

Copy link
Copy Markdown
MemberAuthor

It's better to review and merge #84792 first.

@danmoseley

Copy link
Copy Markdown
Contributor

Merge conflicts - and then this is reviewable? Looks like the other one went in.

@huoyaoyuan

Copy link
Copy Markdown
MemberAuthor

There's also potential massive conflict with #85392/#86875, and minor dependency with other numeric PRs. Can someone determine an order to review these?

@huoyaoyuan

Copy link
Copy Markdown
MemberAuthor

@tannergooding wanna to discuss how to deal with this.

#86875 uses IUtf8Char in parsing, brings the same problem from formatting. IUtf8Char can't be used in System.Runtime.Numerics thus the majority of code need to update.

The approach I tried looks like this:

#if !SYSTEM_PRIVATE_CORELIBusingTChar=System.Char;
#pragma warning disable SA1121// Use built-in type alias
#endif
#if SYSTEM_PRIVATE_CORELIBinternalstaticunsafeTChar*UInt32ToDecChars<TChar>(TChar*bufferEnd,uintvalue,intdigits)whereTChar: unmanaged,IUtfChar<TChar>
#else
internal static unsafe char*UInt32ToDecChars(char*bufferEnd,uintvalue,intdigits)
#endif
#if SYSTEM_PRIVATE_CORELIBprivatestaticReadOnlySpan<TChar>NegativeSign<TChar>(NumberFormatInfoinfo)whereTChar: unmanaged,IUtfChar<TChar>=>info.NegativeSignTChar<TChar>();
#else
privatestaticReadOnlySpan<char>NegativeSign<TChar>(NumberFormatInfoinfo)=>info.NegativeSign;
#endif

The workaround just works, but isn't expandable if we want to support UTF8 for BigInteger. What do you think about the best approach? Should I ask Stephen or someone else?

@adamsitnikadamsitnik self-assigned this Oct 20, 2023

@adamsitnikadamsitnik left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please excuse me for our Team not providing any review for so long. We have been hit by a wonderful talent redeployment quite hard, and this caused the delay.

Big thanks for removing the code duplication!

I've added some comments and I made it clear which can be ignored (or addressed in separate PR).

Overall the PR looks good, it's very thoughtful. However there are merge conflict so I am going to hit "request changes".

@huoyaoyuan thank you for your contribution!

int exp = 0;
do
{
// Check if we are about to overflow past our limit of 9 digits

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This refactoring change introduces a behavior change: https://www.diffchecker.com/ycRc6AAy/

image

I used git blame to verify that this is most likely desired, as it was introduced in #73643 as a bug fix with no breaking change label. More than a year has passed, so I assume it's safe.

cc @tannergooding

Comment threadsrc/libraries/Common/src/System/Number.Parsing.Common.cs
{
result = default;
return ParsingStatus.Failed;
throw e; // TryParse still throws ArgumentException on invalid NumberStyles

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Throwing an exception here will prevent from TryParseBigInteger getting inlined.

How about moving the throw to a private helper method?

staticvoidThrow(Exceptione)=>throwe;

If you don't have the time to check it right now I am fine with that.

if (value.Length < 255)
{
throw e; // TryParse still throws ArgumentException on invalid NumberStyles
buffer = stackalloc byte[value.Length + 1 + 1];

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

IIRC it's recommended to use constant-size stack allocations for small buffers.

@jkotas please correct me if I am wrong

{
result = default;
return ParsingStatus.Failed;
buffer = arrayFromPool = ArrayPool<byte>.Shared.Rent(value.Length + 1 + 1);

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it's the second time 1 + 1 is being used and as a person who reads the code for the first time it's not obvious to me. Could you please add a comment? Or introduce a const with-self-describing-name and use it in both places?

}
}

FailExit:

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is using goto actually beneficial for the performance here?

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't actually remember where I take this pattern from.
The hex parsing itself can be rewritten with HexConverter in a follow-up PR. I will switch to simplest code.

try
{
totalDigitCount = Math.Min(number.digits.Length - 1, numberScale);
totalDigitCount = Math.Min(number.DigitsCount, numberScale);

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is DigitsCount equal to number.digits.Length - 1 here?

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Tests are passing so I assume nothing is wrong. digits has a trailing zero in the comment.

@ghostghost added the needs-author-action An issue or pull request that requires more info or actions from the author. label Oct 30, 2023
@huoyaoyuan

Copy link
Copy Markdown
MemberAuthor

ACK for this PR:

Conflicts are coming from new features/refactors after creation of this PR. I'll redo some moving work to ensure no changes are lost.

Currently I will focus on my another PR first.

@ghostghost removed the needs-author-action An issue or pull request that requires more info or actions from the author. label Nov 7, 2023
@adamsitnikadamsitnik added the needs-author-action An issue or pull request that requires more info or actions from the author. label Nov 7, 2023
@ghost

Copy link
Copy Markdown

This pull request has been automatically marked no-recent-activity because it has not had any activity for 14 days. It will be closed if no further activity occurs within 14 more days. Any new comment (by anyone, not necessarily the author) will remove no-recent-activity.

@ghostghost removed needs-author-action An issue or pull request that requires more info or actions from the author. no-recent-activity labels Nov 28, 2023
@huoyaoyuan

Copy link
Copy Markdown
MemberAuthor

Updated. It really helps that BigInteger hasn't adopt UTF-8, otherwise this will be totally invalidated.

@huoyaoyuan

huoyaoyuan commented Nov 28, 2023

Copy link
Copy Markdown
MemberAuthor

BenchmarkDotNet v0.13.11-nightly.20231126.107, Windows 11 (10.0.22631.2715/23H2/2023Update/SunValley3)
13th Gen Intel Core i9-13900K, 1 CPU, 32 logical and 24 physical cores
.NET SDK 8.0.100
[Host] : .NET 8.0.0 (8.0.23.53103), X64 RyuJIT AVX2
Job-UKWEZQ : .NET 9.0.0 (42.42.42.42424), X64 RyuJIT AVX2
Job-RXOVAY : .NET 9.0.0 (42.42.42.42424), X64 RyuJIT AVX2

PowerPlanMode=00000000-0000-0000-0000-000000000000 Arguments=/p:EnableUnsafeBinaryFormatterSerialization=true IterationTime=250.0000 ms
MaxIterationCount=20 MinIterationCount=15 WarmupCount=1

MethodJobToolchainnumberStringMeanErrorStdDevMedianMinMaxRatioGen0AllocatedAlloc Ratio
ParseJob-UKWEZQ\PR\corerun.exe-214748364839.18 ns0.604 ns0.565 ns39.00 ns38.49 ns40.28 ns0.850.001632 B0.24
ParseJob-RXOVAY\main\corerun.exe-214748364845.89 ns0.420 ns0.372 ns45.80 ns45.40 ns46.75 ns1.000.0072136 B1.00
ParseJob-UKWEZQ\PR\corerun.exe12320.52 ns0.149 ns0.139 ns20.49 ns20.31 ns20.77 ns0.72--0.00
ParseJob-RXOVAY\main\corerun.exe12328.36 ns0.384 ns0.340 ns28.41 ns27.57 ns28.92 ns1.000.0055104 B1.00
ParseJob-UKWEZQ\PR\corerun.exe123456789012(...)901234567890 [200]612.07 ns4.446 ns4.159 ns612.18 ns605.97 ns619.63 ns0.730.0049112 B0.11
ParseJob-RXOVAY\main\corerun.exe123456789012(...)901234567890 [200]833.89 ns9.352 ns8.748 ns830.38 ns825.52 ns853.02 ns1.000.0504984 B1.00

Result for corelib integers:

Details
MethodJobToolchainvalueMeanErrorStdDevMedianMinMaxRatioRatioSDAllocatedAlloc Ratio
ParseJob-NKUDXL\PR\corerun.exe-922337203685477580810.961 ns0.1929 ns0.1710 ns10.951 ns10.556 ns11.252 ns1.020.02-NA
ParseJob-PUBWNA\main\corerun.exe-922337203685477580810.702 ns0.1391 ns0.1233 ns10.732 ns10.406 ns10.894 ns1.000.00-NA
TryParseJob-NKUDXL\PR\corerun.exe-922337203685477580810.570 ns0.1169 ns0.0976 ns10.555 ns10.405 ns10.811 ns0.980.01-NA
TryParseJob-PUBWNA\main\corerun.exe-922337203685477580810.736 ns0.1037 ns0.0866 ns10.758 ns10.560 ns10.865 ns1.000.00-NA
ParseSpanJob-NKUDXL\PR\corerun.exe-922337203685477580811.077 ns0.1187 ns0.1110 ns11.094 ns10.866 ns11.249 ns0.990.01-NA
ParseSpanJob-PUBWNA\main\corerun.exe-922337203685477580811.171 ns0.1055 ns0.0987 ns11.155 ns11.037 ns11.343 ns1.000.00-NA
TryParseSpanJob-NKUDXL\PR\corerun.exe-922337203685477580810.851 ns0.1555 ns0.1454 ns10.829 ns10.632 ns11.183 ns1.010.02-NA
TryParseSpanJob-PUBWNA\main\corerun.exe-922337203685477580810.708 ns0.1344 ns0.1257 ns10.692 ns10.487 ns10.941 ns1.000.00-NA
ParseJob-NKUDXL\PR\corerun.exe123454.501 ns0.0587 ns0.0521 ns4.498 ns4.430 ns4.617 ns0.970.01-NA
ParseJob-PUBWNA\main\corerun.exe123454.633 ns0.0326 ns0.0305 ns4.637 ns4.588 ns4.677 ns1.000.00-NA
TryParseJob-NKUDXL\PR\corerun.exe123454.586 ns0.0285 ns0.0252 ns4.581 ns4.561 ns4.639 ns1.000.01-NA
TryParseJob-PUBWNA\main\corerun.exe123454.570 ns0.0223 ns0.0174 ns4.573 ns4.536 ns4.600 ns1.000.00-NA
ParseSpanJob-NKUDXL\PR\corerun.exe123454.700 ns0.0385 ns0.0360 ns4.702 ns4.644 ns4.766 ns1.010.01-NA
ParseSpanJob-PUBWNA\main\corerun.exe123454.665 ns0.0268 ns0.0250 ns4.660 ns4.626 ns4.714 ns1.000.00-NA
TryParseSpanJob-NKUDXL\PR\corerun.exe123454.656 ns0.0173 ns0.0145 ns4.655 ns4.634 ns4.683 ns1.000.01-NA
TryParseSpanJob-PUBWNA\main\corerun.exe123454.645 ns0.0240 ns0.0213 ns4.646 ns4.616 ns4.685 ns1.000.00-NA
ParseJob-NKUDXL\PR\corerun.exe922337203685477580710.011 ns0.0631 ns0.0590 ns10.007 ns9.923 ns10.092 ns1.000.01-NA
ParseJob-PUBWNA\main\corerun.exe92233720368547758079.978 ns0.0602 ns0.0563 ns9.959 ns9.907 ns10.076 ns1.000.00-NA
TryParseJob-NKUDXL\PR\corerun.exe922337203685477580710.058 ns0.0749 ns0.0701 ns10.058 ns9.916 ns10.198 ns1.010.02-NA
TryParseJob-PUBWNA\main\corerun.exe92233720368547758079.951 ns0.1824 ns0.1706 ns9.916 ns9.725 ns10.278 ns1.000.00-NA
ParseSpanJob-NKUDXL\PR\corerun.exe92233720368547758079.992 ns0.0587 ns0.0520 ns9.995 ns9.897 ns10.076 ns1.020.01-NA
ParseSpanJob-PUBWNA\main\corerun.exe92233720368547758079.794 ns0.0796 ns0.0744 ns9.789 ns9.697 ns9.910 ns1.000.00-NA
TryParseSpanJob-NKUDXL\PR\corerun.exe92233720368547758079.867 ns0.0668 ns0.0625 ns9.847 ns9.782 ns9.951 ns0.990.01-NA
TryParseSpanJob-PUBWNA\main\corerun.exe922337203685477580710.004 ns0.0328 ns0.0256 ns10.005 ns9.949 ns10.040 ns1.000.00-NA

Should be in acceptable noise range.

@adamsitnikadamsitnik left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM, thank you for your contribution and patience @huoyaoyuan !

PS. I am jealous of your hardware ;)

Comment threadsrc/libraries/Common/src/System/Number.Parsing.Common.cs
@adamsitnik

Copy link
Copy Markdown
Member

Failures are unrelated (#95298), merging!

@adamsitnik
adamsitnik merged commit f66c1c1 into dotnet:mainNov 29, 2023
@adamsitnikadamsitnik added this to the 9.0.0 milestone Nov 29, 2023
@huoyaoyuan
huoyaoyuan deleted the numerics-format-1 branch November 29, 2023 12:36
@huoyaoyuan

Copy link
Copy Markdown
MemberAuthor

Well earlier today I noticed the UTF-8/16 unification of CoreLib is not shared. This reveals the pain I faced for formatting. Will open a follow-up PR for that.

@huoyaoyuan

Copy link
Copy Markdown
MemberAuthor

Opened #95402 as follow up.

Sign up for freeto subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

area-System.Numericscommunity-contributionIndicates that the PR has been added by a community member

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants

@huoyaoyuan@danmoseley@adamsitnik