Reduce fixed overhead of some Utf8Parser.TryParse methods - #33507

Merged
GrabYourPitchforks merged 3 commits into
dotnet:masterfrom
GrabYourPitchforks:tryparse_upd
Mar 13, 2020
Merged

Reduce fixed overhead of some Utf8Parser.TryParse methods#33507
GrabYourPitchforks merged 3 commits into
dotnet:masterfrom
GrabYourPitchforks:tryparse_upd

Conversation

@GrabYourPitchforks

Copy link
Copy Markdown
Member

This builds atop the work that Andy did as part of #33004. There are no behavioral changes here - only slight refactorings that produce better codegen at the entry points to some of the Utf8Parser.TryParse methods. In particular, this does not include many of the optimizations discussed at #32843.

Integral types

For the TryParse methods that work with integral types, reflowing the logic in this fashion moves the "default" behavior into a fast-path and reduces the total amount of logic in the switch statement. We're now able to tail-call into the workhorse methods without performing any register shuffling or stack spilling

MethodToolchainMeanErrorStdDevRatio
TryParseInt32master976.3 ns5.34 ns4.73 ns1.00
TryParseInt32tryparse913.9 ns8.03 ns6.71 ns0.94
;;; OLD CODEGEN ;;;00007fff`4803cb40 56pushrsi00007fff`4803cb41 4883ec20 subrsp,20h00007fff`4803cb45 410fb7f1 movzxesi,r9w00007fff`4803cb49 83fe4e cmpesi,4Eh00007fff`4803cb4c 772c ja System_Private_CoreLib!System.Buffers.Text.Utf8Parser.TryParse(System.ReadOnlySpan`1<Byte>, Int32 ByRef, Int32 ByRef, Char)+0xffffffff`a9e299ba (00007fff`4803cb7a)00007fff`4803cb4e 83fe44 cmpesi,44h00007fff`4803cb51 7713ja System_Private_CoreLib!System.Buffers.Text.Utf8Parser.TryParse(System.ReadOnlySpan`1<Byte>, Int32 ByRef, Int32 ByRef, Char)+0xffffffff`a9e299a6 (00007fff`4803cb66)00007fff`4803cb53 85f6 testesi,esi00007fff`4803cb55 7405je System_Private_CoreLib!System.Buffers.Text.Utf8Parser.TryParse(System.ReadOnlySpan`1<Byte>, Int32 ByRef, Int32 ByRef, Char)+0xffffffff`a9e2999c (00007fff`4803cb5c)00007fff`4803cb57 83fe44 cmpesi,44h00007fff`4803cb5a 754c jne System_Private_CoreLib!System.Buffers.Text.Utf8Parser.TryParse(System.ReadOnlySpan`1<Byte>, Int32 ByRef, Int32 ByRef, Char)+0xffffffff`a9e299e8 (00007fff`4803cba8)00007fff`4803cb5c 4883c420 addrsp,20h00007fff`4803cb60 5e poprsi00007fff`4803cb61 e97acaffff jmp CLRStub[MethodDescPrestub]@7fff480395e0 (00007fff`480395e0)00007fff`4803cb66 83fe47 cmpesi,47h00007fff`4803cb69 74f1 je System_Private_CoreLib!System.Buffers.Text.Utf8Parser.TryParse(System.ReadOnlySpan`1<Byte>, Int32 ByRef, Int32 ByRef, Char)+0xffffffff`a9e2999c (00007fff`4803cb5c)00007fff`4803cb6b 83fe4e cmpesi,4Eh00007fff`4803cb6e 7538jne System_Private_CoreLib!System.Buffers.Text.Utf8Parser.TryParse(System.ReadOnlySpan`1<Byte>, Int32 ByRef, Int32 ByRef, Char)+0xffffffff`a9e299e8 (00007fff`4803cba8)00007fff`4803cb70 4883c420 addrsp,20h00007fff`4803cb74 5e poprsi00007fff`4803cb75 e986caffff jmp CLRStub[MethodDescPrestub]@7fff48039600 (00007fff`48039600)00007fff`4803cb7a 83fe64 cmpesi,64h00007fff`4803cb7d 770c ja System_Private_CoreLib!System.Buffers.Text.Utf8Parser.TryParse(System.ReadOnlySpan`1<Byte>, Int32 ByRef, Int32 ByRef, Char)+0xffffffff`a9e299cb (00007fff`4803cb8b)00007fff`4803cb7f 83fe58 cmpesi,58h00007fff`4803cb82 7416je System_Private_CoreLib!System.Buffers.Text.Utf8Parser.TryParse(System.ReadOnlySpan`1<Byte>, Int32 ByRef, Int32 ByRef, Char)+0xffffffff`a9e299da (00007fff`4803cb9a)00007fff`4803cb84 83fe64 cmpesi,64h00007fff`4803cb87 751f jne System_Private_CoreLib!System.Buffers.Text.Utf8Parser.TryParse(System.ReadOnlySpan`1<Byte>, Int32 ByRef, Int32 ByRef, Char)+0xffffffff`a9e299e8 (00007fff`4803cba8)00007fff`4803cb89 ebd1 jmp System_Private_CoreLib!System.Buffers.Text.Utf8Parser.TryParse(System.ReadOnlySpan`1<Byte>, Int32 ByRef, Int32 ByRef, Char)+0xffffffff`a9e2999c (00007fff`4803cb5c)00007fff`4803cb8b 83fe67 cmpesi,67h00007fff`4803cb8e 74cc je System_Private_CoreLib!System.Buffers.Text.Utf8Parser.TryParse(System.ReadOnlySpan`1<Byte>, Int32 ByRef, Int32 ByRef, Char)+0xffffffff`a9e2999c (00007fff`4803cb5c)00007fff`4803cb90 83fe6e cmpesi,6Eh00007fff`4803cb93 74db je System_Private_CoreLib!System.Buffers.Text.Utf8Parser.TryParse(System.ReadOnlySpan`1<Byte>, Int32 ByRef, Int32 ByRef, Char)+0xffffffff`a9e299b0 (00007fff`4803cb70)00007fff`4803cb95 83fe78 cmpesi,78h00007fff`4803cb98 750e jne System_Private_CoreLib!System.Buffers.Text.Utf8Parser.TryParse(System.ReadOnlySpan`1<Byte>, Int32 ByRef, Int32 ByRef, Char)+0xffffffff`a9e299e8 (00007fff`4803cba8)00007fff`4803cb9a 33c0 xoreax,eax00007fff`4803cb9c 8902mov dword ptr [rdx],eax00007fff`4803cb9e 4883c420 addrsp,20h00007fff`4803cba2 5e poprsi00007fff`4803cba3 e9d8caffff jmp CLRStub[MethodDescPrestub]@7fff48039680 (00007fff`48039680)00007fff`4803cba8 33c0 xoreax,eax00007fff`4803cbaa 8902mov dword ptr [rdx],eax00007fff`4803cbac 418900mov dword ptr [r8],eax00007fff`4803cbaf e8dcd8d8ff call CLRStub[MethodDescPrestub]@7fff47dca490 (00007fff`47dca490)00007fff`4803cbb4 cc int3;;; NEW CODEGEN ;;;00007fff`4802cc60 410fb7c1 movzxeax,r9w00007fff`4802cc64 85c0 testeax,eax00007fff`4802cc66 7505jne System_Private_CoreLib!System.Buffers.Text.Utf8Parser.TryParse(System.ReadOnlySpan`1<Byte>, Int32 ByRef, Int32 ByRef, Char)+0xffffffff`a9e199bd (00007fff`4802cc6d)00007fff`4802cc68 e97bc9ffff jmp CLRStub[MethodDescPrestub]@7fff480295e8 (00007fff`480295e8)00007fff`4802cc6d 410fb7c1 movzxeax,r9w00007fff`4802cc71 83c820 oreax,20h00007fff`4802cc74 83f867 cmpeax,67h00007fff`4802cc77 7f0c jg System_Private_CoreLib!System.Buffers.Text.Utf8Parser.TryParse(System.ReadOnlySpan`1<Byte>, Int32 ByRef, Int32 ByRef, Char)+0xffffffff`a9e199d5 (00007fff`4802cc85)00007fff`4802cc79 83f864 cmpeax,64h00007fff`4802cc7c 74ea je System_Private_CoreLib!System.Buffers.Text.Utf8Parser.TryParse(System.ReadOnlySpan`1<Byte>, Int32 ByRef, Int32 ByRef, Char)+0xffffffff`a9e199b8 (00007fff`4802cc68)00007fff`4802cc7e 83f867 cmpeax,67h00007fff`4802cc81 74e5 je System_Private_CoreLib!System.Buffers.Text.Utf8Parser.TryParse(System.ReadOnlySpan`1<Byte>, Int32 ByRef, Int32 ByRef, Char)+0xffffffff`a9e199b8 (00007fff`4802cc68)00007fff`4802cc83 eb16 jmp System_Private_CoreLib!System.Buffers.Text.Utf8Parser.TryParse(System.ReadOnlySpan`1<Byte>, Int32 ByRef, Int32 ByRef, Char)+0xffffffff`a9e199eb (00007fff`4802cc9b)00007fff`4802cc85 83f86e cmpeax,6Eh00007fff`4802cc88 7407je System_Private_CoreLib!System.Buffers.Text.Utf8Parser.TryParse(System.ReadOnlySpan`1<Byte>, Int32 ByRef, Int32 ByRef, Char)+0xffffffff`a9e199e1 (00007fff`4802cc91)00007fff`4802cc8a 83f878 cmpeax,78h00007fff`4802cc8d 7407je System_Private_CoreLib!System.Buffers.Text.Utf8Parser.TryParse(System.ReadOnlySpan`1<Byte>, Int32 ByRef, Int32 ByRef, Char)+0xffffffff`a9e199e6 (00007fff`4802cc96)00007fff`4802cc8f eb0a jmp System_Private_CoreLib!System.Buffers.Text.Utf8Parser.TryParse(System.ReadOnlySpan`1<Byte>, Int32 ByRef, Int32 ByRef, Char)+0xffffffff`a9e199eb (00007fff`4802cc9b)00007fff`4802cc91 e972c9ffff jmp CLRStub[MethodDescPrestub]@7fff48029608 (00007fff`48029608)00007fff`4802cc96 e9edc9ffff jmp CLRStub[MethodDescPrestub]@7fff48029688 (00007fff`48029688)00007fff`4802cc9b e9a0fdffff jmp CLRStub[MethodDescPrestub]@7fff4802ca40 (00007fff`4802ca40)

TryParse(..., out bool, ...)

Minor improvement here to avoid the range check when dereferencing source[4]. There's also less register shuffling in the error path. Avoiding this shuffling doesn't impact the success case, but I saw it as low-hanging fruit since it reduces the overall method codegen size by a little bit.

TryParse(..., out Guid, ...)

Similar to the integral types, the switch statement has been restructured to have the common case go through a fast path. Additionally, by changing the signature of the TryParseGuidCore method, we can avoid the stack spillage that would normally result on Win64 from passing so many parameters to the workhorse routine.

MethodToolchainMeanErrorStdDevMedianRatioRatioSD
ParseGuid_Defaultmaster5,881.1 ns58.94 ns55.14 ns5,876.8 ns1.000.00
ParseGuid_Defaulttryparse5,545.2 ns88.36 ns82.65 ns5,517.9 ns0.940.02
ParseGuid_Bracesmaster6,154.5 ns119.47 ns132.79 ns6,137.5 ns1.000.00
ParseGuid_Bracestryparse5,821.1 ns67.85 ns63.47 ns5,803.3 ns0.950.02
;;; OLD CODEGEN ;;;00007fff`3ccccdd0 56pushrsi00007fff`3ccccdd1 4883ec40 subrsp,40h00007fff`3ccccdd5 c5f877 vzeroupper00007fff`3ccccdd8 33c0 xoreax,eax00007fff`3ccccdda 4889442430mov qword ptr [rsp+30h],rax00007fff`3ccccddf 410fb7f1 movzxesi,r9w00007fff`3ccccde3 83fe42 cmpesi,42h00007fff`3ccccde6 7745ja System_Private_CoreLib!System.Buffers.Text.Utf8Parser.TryParse(System.ReadOnlySpan`1<Byte>, System.Guid ByRef, Int32 ByRef, Char)+0xffffffff`a0c9a5ad (00007fff`3cccce2d)00007fff`3ccccde8 85f6 testesi,esi00007fff`3ccccdea 0f8481000000 je System_Private_CoreLib!System.Buffers.Text.Utf8Parser.TryParse(System.ReadOnlySpan`1<Byte>, System.Guid ByRef, Int32 ByRef, Char)+0xffffffff`a0c9a5f1 (00007fff`3cccce71)00007fff`3ccccdf0 83fe42 cmpesi,42h00007fff`3ccccdf3 0f85c3000000 jne System_Private_CoreLib!System.Buffers.Text.Utf8Parser.TryParse(System.ReadOnlySpan`1<Byte>, System.Guid ByRef, Int32 ByRef, Char)+0xffffffff`a0c9a63c (00007fff`3ccccebc)00007fff`3ccccdf9 c5fa6f01 vmovdqu xmm0, xmmword ptr [rcx]00007fff`3ccccdfd c5fa7f442430 vmovdqu xmmword ptr [rsp+30h],xmm000007fff`3cccce03 4889542420mov qword ptr [rsp+20h],rdx00007fff`3cccce08 4c89442428 mov qword ptr [rsp+28h],r800007fff`3cccce0d 488d4c2430 learcx,[rsp+30h]00007fff`3cccce12 ba01000000 movedx,100007fff`3cccce17 41b87b000000 movr8d,7Bh00007fff`3cccce1d 41b97d000000 movr9d,7Dh00007fff`3cccce23 e888c7ffff call CLRStub[MethodDescPrestub]@7fff3ccc95b0 (00007fff`3ccc95b0)00007fff`3cccce28 e986000000 jmp System_Private_CoreLib!System.Buffers.Text.Utf8Parser.TryParse(System.ReadOnlySpan`1<Byte>, System.Guid ByRef, Int32 ByRef, Char)+0xffffffff`a0c9a633 (00007fff`3cccceb3)00007fff`3cccce2d 83fe44 cmpesi,44h00007fff`3cccce30 743f je System_Private_CoreLib!System.Buffers.Text.Utf8Parser.TryParse(System.ReadOnlySpan`1<Byte>, System.Guid ByRef, Int32 ByRef, Char)+0xffffffff`a0c9a5f1 (00007fff`3cccce71)00007fff`3cccce32 83fe4e cmpesi,4Eh00007fff`3cccce35 7468je System_Private_CoreLib!System.Buffers.Text.Utf8Parser.TryParse(System.ReadOnlySpan`1<Byte>, System.Guid ByRef, Int32 ByRef, Char)+0xffffffff`a0c9a61f (00007fff`3cccce9f)00007fff`3cccce37 83fe50 cmpesi,50h00007fff`3cccce3a 0f857c000000 jne System_Private_CoreLib!System.Buffers.Text.Utf8Parser.TryParse(System.ReadOnlySpan`1<Byte>, System.Guid ByRef, Int32 ByRef, Char)+0xffffffff`a0c9a63c (00007fff`3ccccebc)00007fff`3cccce40 c5fa6f01 vmovdqu xmm0, xmmword ptr [rcx]00007fff`3cccce44 c5fa7f442430 vmovdqu xmmword ptr [rsp+30h],xmm000007fff`3cccce4a 4889542420mov qword ptr [rsp+20h],rdx00007fff`3cccce4f 4c89442428 mov qword ptr [rsp+28h],r800007fff`3cccce54 488d4c2430 learcx,[rsp+30h]00007fff`3cccce59 ba01000000 movedx,100007fff`3cccce5e 41b828000000 movr8d,28h00007fff`3cccce64 41b929000000 movr9d,29h00007fff`3cccce6a e841c7ffff call CLRStub[MethodDescPrestub]@7fff3ccc95b0 (00007fff`3ccc95b0)00007fff`3cccce6f eb42 jmp System_Private_CoreLib!System.Buffers.Text.Utf8Parser.TryParse(System.ReadOnlySpan`1<Byte>, System.Guid ByRef, Int32 ByRef, Char)+0xffffffff`a0c9a633 (00007fff`3cccceb3)00007fff`3cccce71 c5fa6f01 vmovdqu xmm0, xmmword ptr [rcx]00007fff`3cccce75 c5fa7f442430 vmovdqu xmmword ptr [rsp+30h],xmm000007fff`3cccce7b 4889542420mov qword ptr [rsp+20h],rdx00007fff`3cccce80 4c89442428 mov qword ptr [rsp+28h],r800007fff`3cccce85 488d4c2430 learcx,[rsp+30h]00007fff`3cccce8a 33d2 xoredx,edx00007fff`3cccce8c 41b820000000 movr8d,20h00007fff`3cccce92 41b920000000 movr9d,20h00007fff`3cccce98 e813c7ffff call CLRStub[MethodDescPrestub]@7fff3ccc95b0 (00007fff`3ccc95b0)00007fff`3cccce9d eb14 jmp System_Private_CoreLib!System.Buffers.Text.Utf8Parser.TryParse(System.ReadOnlySpan`1<Byte>, System.Guid ByRef, Int32 ByRef, Char)+0xffffffff`a0c9a633 (00007fff`3cccceb3)00007fff`3cccce9f c5fa6f01 vmovdqu xmm0, xmmword ptr [rcx]00007fff`3ccccea3 c5fa7f442430 vmovdqu xmmword ptr [rsp+30h],xmm000007fff`3ccccea9 488d4c2430 learcx,[rsp+30h]00007fff`3cccceae e8f5c6ffff call CLRStub[MethodDescPrestub]@7fff3ccc95a8 (00007fff`3ccc95a8)00007fff`3cccceb3 0fb6c0 movzxeax,al00007fff`3cccceb6 4883c440 addrsp,40h00007fff`3cccceba 5e poprsi00007fff`3ccccebb c3 ret00007fff`3ccccebc c5f857c0 vxorps xmm0,xmm0,xmm000007fff`3ccccec0 c5fa7f02 vmovdqu xmmword ptr [rdx],xmm000007fff`3ccccec4 33c0 xoreax,eax00007fff`3ccccec6 418900mov dword ptr [r8],eax00007fff`3ccccec9 e8c2d5d8ff call CLRStub[MethodDescPrestub]@7fff3ca5a490 (00007fff`3ca5a490)00007fff`3ccccece cc int3;;; NEW CODEGEN ;;;00007fff`3cccccc0 410fb7c1 movzxeax,r9w00007fff`3cccccc4 85c0 testeax,eax00007fff`3cccccc6 7508jne System_Private_CoreLib!System.Buffers.Text.Utf8Parser.TryParse(System.ReadOnlySpan`1<Byte>, System.Guid ByRef, Int32 ByRef, Char)+0xffffffff`a0c9a370 (00007fff`3cccccd0)00007fff`3cccccc8 4533c9 xorr9d,r9d00007fff`3ccccccb e9a8d1ffff jmp CLRStub[MethodDescPrestub]@7fff3ccc9e78 (00007fff`3ccc9e78)00007fff`3cccccd0 450fb7c9 movzxr9d,r9w00007fff`3cccccd4 4183f944 cmpr9d,44h00007fff`3cccccd8 770e ja System_Private_CoreLib!System.Buffers.Text.Utf8Parser.TryParse(System.ReadOnlySpan`1<Byte>, System.Guid ByRef, Int32 ByRef, Char)+0xffffffff`a0c9a388 (00007fff`3ccccce8)00007fff`3cccccda 4183f942 cmpr9d,42h00007fff`3cccccde 7416je System_Private_CoreLib!System.Buffers.Text.Utf8Parser.TryParse(System.ReadOnlySpan`1<Byte>, System.Guid ByRef, Int32 ByRef, Char)+0xffffffff`a0c9a396 (00007fff`3cccccf6)00007fff`3ccccce0 4183f944 cmpr9d,44h00007fff`3ccccce4 74e2 je System_Private_CoreLib!System.Buffers.Text.Utf8Parser.TryParse(System.ReadOnlySpan`1<Byte>, System.Guid ByRef, Int32 ByRef, Char)+0xffffffff`a0c9a368 (00007fff`3cccccc8)00007fff`3ccccce6 eb29 jmp System_Private_CoreLib!System.Buffers.Text.Utf8Parser.TryParse(System.ReadOnlySpan`1<Byte>, System.Guid ByRef, Int32 ByRef, Char)+0xffffffff`a0c9a3b1 (00007fff`3ccccd11)00007fff`3ccccce8 4183f94e cmpr9d,4Eh00007fff`3cccccec 741e je System_Private_CoreLib!System.Buffers.Text.Utf8Parser.TryParse(System.ReadOnlySpan`1<Byte>, System.Guid ByRef, Int32 ByRef, Char)+0xffffffff`a0c9a3ac (00007fff`3ccccd0c)00007fff`3cccccee 4183f950 cmpr9d,50h00007fff`3cccccf2 740d je System_Private_CoreLib!System.Buffers.Text.Utf8Parser.TryParse(System.ReadOnlySpan`1<Byte>, System.Guid ByRef, Int32 ByRef, Char)+0xffffffff`a0c9a3a1 (00007fff`3ccccd01)00007fff`3cccccf4 eb1b jmp System_Private_CoreLib!System.Buffers.Text.Utf8Parser.TryParse(System.ReadOnlySpan`1<Byte>, System.Guid ByRef, Int32 ByRef, Char)+0xffffffff`a0c9a3b1 (00007fff`3ccccd11)00007fff`3cccccf6 41b97b7d0000 movr9d,7D7Bh00007fff`3cccccfc e977d1ffff jmp CLRStub[MethodDescPrestub]@7fff3ccc9e78 (00007fff`3ccc9e78)00007fff`3ccccd01 41b928290000 movr9d,2928h00007fff`3ccccd07 e96cd1ffff jmp CLRStub[MethodDescPrestub]@7fff3ccc9e78 (00007fff`3ccc9e78)00007fff`3ccccd0c e95fd1ffff jmp CLRStub[MethodDescPrestub]@7fff3ccc9e70 (00007fff`3ccc9e70)00007fff`3ccccd11 e91afdffff jmp CLRStub[MethodDescPrestub]@7fff3cccca30 (00007fff`3cccca30)

if (ends != default)
{
if (source[0] != begin)
if (source[0] != (byte)ends)

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.

The JIT could produce better codegen for this pattern. See #33504.

@GrabYourPitchforksGrabYourPitchforks added the tenet-performance Performance related issue label Mar 12, 2020
@stephentoub

Copy link
Copy Markdown
Member

How does this relate to #32843?

Can similar tweaks be made to the corresponding char-based APIs (e.g. int.Parse)?

@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.

another really nice improvement @GrabYourPitchforks !

LGTM, thanks for adding the comment that explains the bit shifting logic

@GrabYourPitchforks

Copy link
Copy Markdown
MemberAuthor

@stephentoub That PR was primarily concerned with improving the workhorse routine. From that investigation came the idea of improving the entry point. This PR focuses just on the fixed overhead of the entry points while leaving the workhorse routines largely unchanged.

I haven't tried applying these optimizations to the char overloads yet because their entry points generally look different than these entry points. But I imagine once we get back to the PR that optimizes the workhorse routines the optimizations there will be generally applicable to both char and byte.

@tannergoodingtannergooding 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.

Changes LGTM. Had a couple questions about the codegen

@GrabYourPitchforks

Copy link
Copy Markdown
MemberAuthor

CI failures seem to be unrelated infrastructure failures.

@GrabYourPitchforks
GrabYourPitchforks merged commit 099bc4b into dotnet:masterMar 13, 2020
@GrabYourPitchforks
GrabYourPitchforks deleted the tryparse_upd branch March 13, 2020 00:57
@ghostghost locked as resolved and limited conversation to collaborators Dec 10, 2020
Sign up for freeto subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

area-System.Memorytenet-performancePerformance related issue

Projects

None yet

Development

Successfully merging this pull request may close these issues.

7 participants

@GrabYourPitchforks@stephentoub@lpereira@adamsitnik@tannergooding@davidwrighton@Dotnet-GitSync-Bot
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Add copy buttons to all
 blocks\n(function() {\n function addCopyButtons() {\n document.querySelectorAll('pre code').forEach(function(codeBlock) {\n if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;\n codeBlock.parentElement.setAttribute('data-copy-added', 'true');\n \n var btn = document.createElement('button');\n btn.textContent = 'Copy';\n btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';\n btn.onmouseover = function() { this.style.opacity = '1'; };\n btn.onmouseout = function() { this.style.opacity = '0.7'; };\n btn.onclick = function() {\n navigator.clipboard.writeText(codeBlock.textContent).then(function() {\n btn.textContent = 'Copied!';\n setTimeout(function() { btn.textContent = 'Copy'; }, 1500);\n });\n };\n codeBlock.parentElement.style.position = 'relative';\n codeBlock.parentElement.appendChild(btn);\n });\n }\n \n addCopyButtons();\n \n // Re-run on dynamic content\n var observer = new MutationObserver(addCopyButtons);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Add Copy Buttons to Code Blocks");
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
Skip to content

Reduce fixed overhead of some Utf8Parser.TryParse methods - #33507

Merged
GrabYourPitchforks merged 3 commits into
dotnet:masterfrom
GrabYourPitchforks:tryparse_upd
Mar 13, 2020
Merged

Reduce fixed overhead of some Utf8Parser.TryParse methods#33507
GrabYourPitchforks merged 3 commits into
dotnet:masterfrom
GrabYourPitchforks:tryparse_upd

Conversation

@GrabYourPitchforks

Copy link
Copy Markdown
Member

This builds atop the work that Andy did as part of #33004. There are no behavioral changes here - only slight refactorings that produce better codegen at the entry points to some of the Utf8Parser.TryParse methods. In particular, this does not include many of the optimizations discussed at #32843.

Integral types

For the TryParse methods that work with integral types, reflowing the logic in this fashion moves the "default" behavior into a fast-path and reduces the total amount of logic in the switch statement. We're now able to tail-call into the workhorse methods without performing any register shuffling or stack spilling

MethodToolchainMeanErrorStdDevRatio
TryParseInt32master976.3 ns5.34 ns4.73 ns1.00
TryParseInt32tryparse913.9 ns8.03 ns6.71 ns0.94
;;; OLD CODEGEN ;;;00007fff`4803cb40 56pushrsi00007fff`4803cb41 4883ec20 subrsp,20h00007fff`4803cb45 410fb7f1 movzxesi,r9w00007fff`4803cb49 83fe4e cmpesi,4Eh00007fff`4803cb4c 772c ja System_Private_CoreLib!System.Buffers.Text.Utf8Parser.TryParse(System.ReadOnlySpan`1<Byte>, Int32 ByRef, Int32 ByRef, Char)+0xffffffff`a9e299ba (00007fff`4803cb7a)00007fff`4803cb4e 83fe44 cmpesi,44h00007fff`4803cb51 7713ja System_Private_CoreLib!System.Buffers.Text.Utf8Parser.TryParse(System.ReadOnlySpan`1<Byte>, Int32 ByRef, Int32 ByRef, Char)+0xffffffff`a9e299a6 (00007fff`4803cb66)00007fff`4803cb53 85f6 testesi,esi00007fff`4803cb55 7405je System_Private_CoreLib!System.Buffers.Text.Utf8Parser.TryParse(System.ReadOnlySpan`1<Byte>, Int32 ByRef, Int32 ByRef, Char)+0xffffffff`a9e2999c (00007fff`4803cb5c)00007fff`4803cb57 83fe44 cmpesi,44h00007fff`4803cb5a 754c jne System_Private_CoreLib!System.Buffers.Text.Utf8Parser.TryParse(System.ReadOnlySpan`1<Byte>, Int32 ByRef, Int32 ByRef, Char)+0xffffffff`a9e299e8 (00007fff`4803cba8)00007fff`4803cb5c 4883c420 addrsp,20h00007fff`4803cb60 5e poprsi00007fff`4803cb61 e97acaffff jmp CLRStub[MethodDescPrestub]@7fff480395e0 (00007fff`480395e0)00007fff`4803cb66 83fe47 cmpesi,47h00007fff`4803cb69 74f1 je System_Private_CoreLib!System.Buffers.Text.Utf8Parser.TryParse(System.ReadOnlySpan`1<Byte>, Int32 ByRef, Int32 ByRef, Char)+0xffffffff`a9e2999c (00007fff`4803cb5c)00007fff`4803cb6b 83fe4e cmpesi,4Eh00007fff`4803cb6e 7538jne System_Private_CoreLib!System.Buffers.Text.Utf8Parser.TryParse(System.ReadOnlySpan`1<Byte>, Int32 ByRef, Int32 ByRef, Char)+0xffffffff`a9e299e8 (00007fff`4803cba8)00007fff`4803cb70 4883c420 addrsp,20h00007fff`4803cb74 5e poprsi00007fff`4803cb75 e986caffff jmp CLRStub[MethodDescPrestub]@7fff48039600 (00007fff`48039600)00007fff`4803cb7a 83fe64 cmpesi,64h00007fff`4803cb7d 770c ja System_Private_CoreLib!System.Buffers.Text.Utf8Parser.TryParse(System.ReadOnlySpan`1<Byte>, Int32 ByRef, Int32 ByRef, Char)+0xffffffff`a9e299cb (00007fff`4803cb8b)00007fff`4803cb7f 83fe58 cmpesi,58h00007fff`4803cb82 7416je System_Private_CoreLib!System.Buffers.Text.Utf8Parser.TryParse(System.ReadOnlySpan`1<Byte>, Int32 ByRef, Int32 ByRef, Char)+0xffffffff`a9e299da (00007fff`4803cb9a)00007fff`4803cb84 83fe64 cmpesi,64h00007fff`4803cb87 751f jne System_Private_CoreLib!System.Buffers.Text.Utf8Parser.TryParse(System.ReadOnlySpan`1<Byte>, Int32 ByRef, Int32 ByRef, Char)+0xffffffff`a9e299e8 (00007fff`4803cba8)00007fff`4803cb89 ebd1 jmp System_Private_CoreLib!System.Buffers.Text.Utf8Parser.TryParse(System.ReadOnlySpan`1<Byte>, Int32 ByRef, Int32 ByRef, Char)+0xffffffff`a9e2999c (00007fff`4803cb5c)00007fff`4803cb8b 83fe67 cmpesi,67h00007fff`4803cb8e 74cc je System_Private_CoreLib!System.Buffers.Text.Utf8Parser.TryParse(System.ReadOnlySpan`1<Byte>, Int32 ByRef, Int32 ByRef, Char)+0xffffffff`a9e2999c (00007fff`4803cb5c)00007fff`4803cb90 83fe6e cmpesi,6Eh00007fff`4803cb93 74db je System_Private_CoreLib!System.Buffers.Text.Utf8Parser.TryParse(System.ReadOnlySpan`1<Byte>, Int32 ByRef, Int32 ByRef, Char)+0xffffffff`a9e299b0 (00007fff`4803cb70)00007fff`4803cb95 83fe78 cmpesi,78h00007fff`4803cb98 750e jne System_Private_CoreLib!System.Buffers.Text.Utf8Parser.TryParse(System.ReadOnlySpan`1<Byte>, Int32 ByRef, Int32 ByRef, Char)+0xffffffff`a9e299e8 (00007fff`4803cba8)00007fff`4803cb9a 33c0 xoreax,eax00007fff`4803cb9c 8902mov dword ptr [rdx],eax00007fff`4803cb9e 4883c420 addrsp,20h00007fff`4803cba2 5e poprsi00007fff`4803cba3 e9d8caffff jmp CLRStub[MethodDescPrestub]@7fff48039680 (00007fff`48039680)00007fff`4803cba8 33c0 xoreax,eax00007fff`4803cbaa 8902mov dword ptr [rdx],eax00007fff`4803cbac 418900mov dword ptr [r8],eax00007fff`4803cbaf e8dcd8d8ff call CLRStub[MethodDescPrestub]@7fff47dca490 (00007fff`47dca490)00007fff`4803cbb4 cc int3;;; NEW CODEGEN ;;;00007fff`4802cc60 410fb7c1 movzxeax,r9w00007fff`4802cc64 85c0 testeax,eax00007fff`4802cc66 7505jne System_Private_CoreLib!System.Buffers.Text.Utf8Parser.TryParse(System.ReadOnlySpan`1<Byte>, Int32 ByRef, Int32 ByRef, Char)+0xffffffff`a9e199bd (00007fff`4802cc6d)00007fff`4802cc68 e97bc9ffff jmp CLRStub[MethodDescPrestub]@7fff480295e8 (00007fff`480295e8)00007fff`4802cc6d 410fb7c1 movzxeax,r9w00007fff`4802cc71 83c820 oreax,20h00007fff`4802cc74 83f867 cmpeax,67h00007fff`4802cc77 7f0c jg System_Private_CoreLib!System.Buffers.Text.Utf8Parser.TryParse(System.ReadOnlySpan`1<Byte>, Int32 ByRef, Int32 ByRef, Char)+0xffffffff`a9e199d5 (00007fff`4802cc85)00007fff`4802cc79 83f864 cmpeax,64h00007fff`4802cc7c 74ea je System_Private_CoreLib!System.Buffers.Text.Utf8Parser.TryParse(System.ReadOnlySpan`1<Byte>, Int32 ByRef, Int32 ByRef, Char)+0xffffffff`a9e199b8 (00007fff`4802cc68)00007fff`4802cc7e 83f867 cmpeax,67h00007fff`4802cc81 74e5 je System_Private_CoreLib!System.Buffers.Text.Utf8Parser.TryParse(System.ReadOnlySpan`1<Byte>, Int32 ByRef, Int32 ByRef, Char)+0xffffffff`a9e199b8 (00007fff`4802cc68)00007fff`4802cc83 eb16 jmp System_Private_CoreLib!System.Buffers.Text.Utf8Parser.TryParse(System.ReadOnlySpan`1<Byte>, Int32 ByRef, Int32 ByRef, Char)+0xffffffff`a9e199eb (00007fff`4802cc9b)00007fff`4802cc85 83f86e cmpeax,6Eh00007fff`4802cc88 7407je System_Private_CoreLib!System.Buffers.Text.Utf8Parser.TryParse(System.ReadOnlySpan`1<Byte>, Int32 ByRef, Int32 ByRef, Char)+0xffffffff`a9e199e1 (00007fff`4802cc91)00007fff`4802cc8a 83f878 cmpeax,78h00007fff`4802cc8d 7407je System_Private_CoreLib!System.Buffers.Text.Utf8Parser.TryParse(System.ReadOnlySpan`1<Byte>, Int32 ByRef, Int32 ByRef, Char)+0xffffffff`a9e199e6 (00007fff`4802cc96)00007fff`4802cc8f eb0a jmp System_Private_CoreLib!System.Buffers.Text.Utf8Parser.TryParse(System.ReadOnlySpan`1<Byte>, Int32 ByRef, Int32 ByRef, Char)+0xffffffff`a9e199eb (00007fff`4802cc9b)00007fff`4802cc91 e972c9ffff jmp CLRStub[MethodDescPrestub]@7fff48029608 (00007fff`48029608)00007fff`4802cc96 e9edc9ffff jmp CLRStub[MethodDescPrestub]@7fff48029688 (00007fff`48029688)00007fff`4802cc9b e9a0fdffff jmp CLRStub[MethodDescPrestub]@7fff4802ca40 (00007fff`4802ca40)

TryParse(..., out bool, ...)

Minor improvement here to avoid the range check when dereferencing source[4]. There's also less register shuffling in the error path. Avoiding this shuffling doesn't impact the success case, but I saw it as low-hanging fruit since it reduces the overall method codegen size by a little bit.

TryParse(..., out Guid, ...)

Similar to the integral types, the switch statement has been restructured to have the common case go through a fast path. Additionally, by changing the signature of the TryParseGuidCore method, we can avoid the stack spillage that would normally result on Win64 from passing so many parameters to the workhorse routine.

MethodToolchainMeanErrorStdDevMedianRatioRatioSD
ParseGuid_Defaultmaster5,881.1 ns58.94 ns55.14 ns5,876.8 ns1.000.00
ParseGuid_Defaulttryparse5,545.2 ns88.36 ns82.65 ns5,517.9 ns0.940.02
ParseGuid_Bracesmaster6,154.5 ns119.47 ns132.79 ns6,137.5 ns1.000.00
ParseGuid_Bracestryparse5,821.1 ns67.85 ns63.47 ns5,803.3 ns0.950.02
;;; OLD CODEGEN ;;;00007fff`3ccccdd0 56pushrsi00007fff`3ccccdd1 4883ec40 subrsp,40h00007fff`3ccccdd5 c5f877 vzeroupper00007fff`3ccccdd8 33c0 xoreax,eax00007fff`3ccccdda 4889442430mov qword ptr [rsp+30h],rax00007fff`3ccccddf 410fb7f1 movzxesi,r9w00007fff`3ccccde3 83fe42 cmpesi,42h00007fff`3ccccde6 7745ja System_Private_CoreLib!System.Buffers.Text.Utf8Parser.TryParse(System.ReadOnlySpan`1<Byte>, System.Guid ByRef, Int32 ByRef, Char)+0xffffffff`a0c9a5ad (00007fff`3cccce2d)00007fff`3ccccde8 85f6 testesi,esi00007fff`3ccccdea 0f8481000000 je System_Private_CoreLib!System.Buffers.Text.Utf8Parser.TryParse(System.ReadOnlySpan`1<Byte>, System.Guid ByRef, Int32 ByRef, Char)+0xffffffff`a0c9a5f1 (00007fff`3cccce71)00007fff`3ccccdf0 83fe42 cmpesi,42h00007fff`3ccccdf3 0f85c3000000 jne System_Private_CoreLib!System.Buffers.Text.Utf8Parser.TryParse(System.ReadOnlySpan`1<Byte>, System.Guid ByRef, Int32 ByRef, Char)+0xffffffff`a0c9a63c (00007fff`3ccccebc)00007fff`3ccccdf9 c5fa6f01 vmovdqu xmm0, xmmword ptr [rcx]00007fff`3ccccdfd c5fa7f442430 vmovdqu xmmword ptr [rsp+30h],xmm000007fff`3cccce03 4889542420mov qword ptr [rsp+20h],rdx00007fff`3cccce08 4c89442428 mov qword ptr [rsp+28h],r800007fff`3cccce0d 488d4c2430 learcx,[rsp+30h]00007fff`3cccce12 ba01000000 movedx,100007fff`3cccce17 41b87b000000 movr8d,7Bh00007fff`3cccce1d 41b97d000000 movr9d,7Dh00007fff`3cccce23 e888c7ffff call CLRStub[MethodDescPrestub]@7fff3ccc95b0 (00007fff`3ccc95b0)00007fff`3cccce28 e986000000 jmp System_Private_CoreLib!System.Buffers.Text.Utf8Parser.TryParse(System.ReadOnlySpan`1<Byte>, System.Guid ByRef, Int32 ByRef, Char)+0xffffffff`a0c9a633 (00007fff`3cccceb3)00007fff`3cccce2d 83fe44 cmpesi,44h00007fff`3cccce30 743f je System_Private_CoreLib!System.Buffers.Text.Utf8Parser.TryParse(System.ReadOnlySpan`1<Byte>, System.Guid ByRef, Int32 ByRef, Char)+0xffffffff`a0c9a5f1 (00007fff`3cccce71)00007fff`3cccce32 83fe4e cmpesi,4Eh00007fff`3cccce35 7468je System_Private_CoreLib!System.Buffers.Text.Utf8Parser.TryParse(System.ReadOnlySpan`1<Byte>, System.Guid ByRef, Int32 ByRef, Char)+0xffffffff`a0c9a61f (00007fff`3cccce9f)00007fff`3cccce37 83fe50 cmpesi,50h00007fff`3cccce3a 0f857c000000 jne System_Private_CoreLib!System.Buffers.Text.Utf8Parser.TryParse(System.ReadOnlySpan`1<Byte>, System.Guid ByRef, Int32 ByRef, Char)+0xffffffff`a0c9a63c (00007fff`3ccccebc)00007fff`3cccce40 c5fa6f01 vmovdqu xmm0, xmmword ptr [rcx]00007fff`3cccce44 c5fa7f442430 vmovdqu xmmword ptr [rsp+30h],xmm000007fff`3cccce4a 4889542420mov qword ptr [rsp+20h],rdx00007fff`3cccce4f 4c89442428 mov qword ptr [rsp+28h],r800007fff`3cccce54 488d4c2430 learcx,[rsp+30h]00007fff`3cccce59 ba01000000 movedx,100007fff`3cccce5e 41b828000000 movr8d,28h00007fff`3cccce64 41b929000000 movr9d,29h00007fff`3cccce6a e841c7ffff call CLRStub[MethodDescPrestub]@7fff3ccc95b0 (00007fff`3ccc95b0)00007fff`3cccce6f eb42 jmp System_Private_CoreLib!System.Buffers.Text.Utf8Parser.TryParse(System.ReadOnlySpan`1<Byte>, System.Guid ByRef, Int32 ByRef, Char)+0xffffffff`a0c9a633 (00007fff`3cccceb3)00007fff`3cccce71 c5fa6f01 vmovdqu xmm0, xmmword ptr [rcx]00007fff`3cccce75 c5fa7f442430 vmovdqu xmmword ptr [rsp+30h],xmm000007fff`3cccce7b 4889542420mov qword ptr [rsp+20h],rdx00007fff`3cccce80 4c89442428 mov qword ptr [rsp+28h],r800007fff`3cccce85 488d4c2430 learcx,[rsp+30h]00007fff`3cccce8a 33d2 xoredx,edx00007fff`3cccce8c 41b820000000 movr8d,20h00007fff`3cccce92 41b920000000 movr9d,20h00007fff`3cccce98 e813c7ffff call CLRStub[MethodDescPrestub]@7fff3ccc95b0 (00007fff`3ccc95b0)00007fff`3cccce9d eb14 jmp System_Private_CoreLib!System.Buffers.Text.Utf8Parser.TryParse(System.ReadOnlySpan`1<Byte>, System.Guid ByRef, Int32 ByRef, Char)+0xffffffff`a0c9a633 (00007fff`3cccceb3)00007fff`3cccce9f c5fa6f01 vmovdqu xmm0, xmmword ptr [rcx]00007fff`3ccccea3 c5fa7f442430 vmovdqu xmmword ptr [rsp+30h],xmm000007fff`3ccccea9 488d4c2430 learcx,[rsp+30h]00007fff`3cccceae e8f5c6ffff call CLRStub[MethodDescPrestub]@7fff3ccc95a8 (00007fff`3ccc95a8)00007fff`3cccceb3 0fb6c0 movzxeax,al00007fff`3cccceb6 4883c440 addrsp,40h00007fff`3cccceba 5e poprsi00007fff`3ccccebb c3 ret00007fff`3ccccebc c5f857c0 vxorps xmm0,xmm0,xmm000007fff`3ccccec0 c5fa7f02 vmovdqu xmmword ptr [rdx],xmm000007fff`3ccccec4 33c0 xoreax,eax00007fff`3ccccec6 418900mov dword ptr [r8],eax00007fff`3ccccec9 e8c2d5d8ff call CLRStub[MethodDescPrestub]@7fff3ca5a490 (00007fff`3ca5a490)00007fff`3ccccece cc int3;;; NEW CODEGEN ;;;00007fff`3cccccc0 410fb7c1 movzxeax,r9w00007fff`3cccccc4 85c0 testeax,eax00007fff`3cccccc6 7508jne System_Private_CoreLib!System.Buffers.Text.Utf8Parser.TryParse(System.ReadOnlySpan`1<Byte>, System.Guid ByRef, Int32 ByRef, Char)+0xffffffff`a0c9a370 (00007fff`3cccccd0)00007fff`3cccccc8 4533c9 xorr9d,r9d00007fff`3ccccccb e9a8d1ffff jmp CLRStub[MethodDescPrestub]@7fff3ccc9e78 (00007fff`3ccc9e78)00007fff`3cccccd0 450fb7c9 movzxr9d,r9w00007fff`3cccccd4 4183f944 cmpr9d,44h00007fff`3cccccd8 770e ja System_Private_CoreLib!System.Buffers.Text.Utf8Parser.TryParse(System.ReadOnlySpan`1<Byte>, System.Guid ByRef, Int32 ByRef, Char)+0xffffffff`a0c9a388 (00007fff`3ccccce8)00007fff`3cccccda 4183f942 cmpr9d,42h00007fff`3cccccde 7416je System_Private_CoreLib!System.Buffers.Text.Utf8Parser.TryParse(System.ReadOnlySpan`1<Byte>, System.Guid ByRef, Int32 ByRef, Char)+0xffffffff`a0c9a396 (00007fff`3cccccf6)00007fff`3ccccce0 4183f944 cmpr9d,44h00007fff`3ccccce4 74e2 je System_Private_CoreLib!System.Buffers.Text.Utf8Parser.TryParse(System.ReadOnlySpan`1<Byte>, System.Guid ByRef, Int32 ByRef, Char)+0xffffffff`a0c9a368 (00007fff`3cccccc8)00007fff`3ccccce6 eb29 jmp System_Private_CoreLib!System.Buffers.Text.Utf8Parser.TryParse(System.ReadOnlySpan`1<Byte>, System.Guid ByRef, Int32 ByRef, Char)+0xffffffff`a0c9a3b1 (00007fff`3ccccd11)00007fff`3ccccce8 4183f94e cmpr9d,4Eh00007fff`3cccccec 741e je System_Private_CoreLib!System.Buffers.Text.Utf8Parser.TryParse(System.ReadOnlySpan`1<Byte>, System.Guid ByRef, Int32 ByRef, Char)+0xffffffff`a0c9a3ac (00007fff`3ccccd0c)00007fff`3cccccee 4183f950 cmpr9d,50h00007fff`3cccccf2 740d je System_Private_CoreLib!System.Buffers.Text.Utf8Parser.TryParse(System.ReadOnlySpan`1<Byte>, System.Guid ByRef, Int32 ByRef, Char)+0xffffffff`a0c9a3a1 (00007fff`3ccccd01)00007fff`3cccccf4 eb1b jmp System_Private_CoreLib!System.Buffers.Text.Utf8Parser.TryParse(System.ReadOnlySpan`1<Byte>, System.Guid ByRef, Int32 ByRef, Char)+0xffffffff`a0c9a3b1 (00007fff`3ccccd11)00007fff`3cccccf6 41b97b7d0000 movr9d,7D7Bh00007fff`3cccccfc e977d1ffff jmp CLRStub[MethodDescPrestub]@7fff3ccc9e78 (00007fff`3ccc9e78)00007fff`3ccccd01 41b928290000 movr9d,2928h00007fff`3ccccd07 e96cd1ffff jmp CLRStub[MethodDescPrestub]@7fff3ccc9e78 (00007fff`3ccc9e78)00007fff`3ccccd0c e95fd1ffff jmp CLRStub[MethodDescPrestub]@7fff3ccc9e70 (00007fff`3ccc9e70)00007fff`3ccccd11 e91afdffff jmp CLRStub[MethodDescPrestub]@7fff3cccca30 (00007fff`3cccca30)

if (ends != default)
{
if (source[0] != begin)
if (source[0] != (byte)ends)

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.

The JIT could produce better codegen for this pattern. See #33504.

@GrabYourPitchforksGrabYourPitchforks added the tenet-performance Performance related issue label Mar 12, 2020
@stephentoub

Copy link
Copy Markdown
Member

How does this relate to #32843?

Can similar tweaks be made to the corresponding char-based APIs (e.g. int.Parse)?

@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.

another really nice improvement @GrabYourPitchforks !

LGTM, thanks for adding the comment that explains the bit shifting logic

@GrabYourPitchforks

Copy link
Copy Markdown
MemberAuthor

@stephentoub That PR was primarily concerned with improving the workhorse routine. From that investigation came the idea of improving the entry point. This PR focuses just on the fixed overhead of the entry points while leaving the workhorse routines largely unchanged.

I haven't tried applying these optimizations to the char overloads yet because their entry points generally look different than these entry points. But I imagine once we get back to the PR that optimizes the workhorse routines the optimizations there will be generally applicable to both char and byte.

@tannergoodingtannergooding 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.

Changes LGTM. Had a couple questions about the codegen

@GrabYourPitchforks

Copy link
Copy Markdown
MemberAuthor

CI failures seem to be unrelated infrastructure failures.

@GrabYourPitchforks
GrabYourPitchforks merged commit 099bc4b into dotnet:masterMar 13, 2020
@GrabYourPitchforks
GrabYourPitchforks deleted the tryparse_upd branch March 13, 2020 00:57
@ghostghost locked as resolved and limited conversation to collaborators Dec 10, 2020
Sign up for freeto subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

area-System.Memorytenet-performancePerformance related issue

Projects

None yet

Development

Successfully merging this pull request may close these issues.

7 participants

@GrabYourPitchforks@stephentoub@lpereira@adamsitnik@tannergooding@davidwrighton@Dotnet-GitSync-Bot
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Force GitHub README to respect dark mode\n(function() {\n var style = document.createElement('style');\n style.textContent = '\n .markdown-body {\n color-scheme: dark light;\n }\n .markdown-body pre { background: #161b22 !important; }\n .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; }\n .markdown-body table th, .markdown-body table td { border-color: #30363d !important; }\n .markdown-body img { background: #0d1117; }\n .markdown-body blockquote { border-left-color: #8b949e; }\n .markdown-body hr { border-color: #30363d; }\n ';\n document.head.appendChild(style);\n})();", "GitHub Dark Mode README Fix"); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Reduce fixed overhead of some Utf8Parser.TryParse methods - #33507

Merged
GrabYourPitchforks merged 3 commits into
dotnet:masterfrom
GrabYourPitchforks:tryparse_upd
Mar 13, 2020
Merged

Reduce fixed overhead of some Utf8Parser.TryParse methods#33507
GrabYourPitchforks merged 3 commits into
dotnet:masterfrom
GrabYourPitchforks:tryparse_upd

Conversation

@GrabYourPitchforks

Copy link
Copy Markdown
Member

This builds atop the work that Andy did as part of #33004. There are no behavioral changes here - only slight refactorings that produce better codegen at the entry points to some of the Utf8Parser.TryParse methods. In particular, this does not include many of the optimizations discussed at #32843.

Integral types

For the TryParse methods that work with integral types, reflowing the logic in this fashion moves the "default" behavior into a fast-path and reduces the total amount of logic in the switch statement. We're now able to tail-call into the workhorse methods without performing any register shuffling or stack spilling

MethodToolchainMeanErrorStdDevRatio
TryParseInt32master976.3 ns5.34 ns4.73 ns1.00
TryParseInt32tryparse913.9 ns8.03 ns6.71 ns0.94
;;; OLD CODEGEN ;;;00007fff`4803cb40 56pushrsi00007fff`4803cb41 4883ec20 subrsp,20h00007fff`4803cb45 410fb7f1 movzxesi,r9w00007fff`4803cb49 83fe4e cmpesi,4Eh00007fff`4803cb4c 772c ja System_Private_CoreLib!System.Buffers.Text.Utf8Parser.TryParse(System.ReadOnlySpan`1<Byte>, Int32 ByRef, Int32 ByRef, Char)+0xffffffff`a9e299ba (00007fff`4803cb7a)00007fff`4803cb4e 83fe44 cmpesi,44h00007fff`4803cb51 7713ja System_Private_CoreLib!System.Buffers.Text.Utf8Parser.TryParse(System.ReadOnlySpan`1<Byte>, Int32 ByRef, Int32 ByRef, Char)+0xffffffff`a9e299a6 (00007fff`4803cb66)00007fff`4803cb53 85f6 testesi,esi00007fff`4803cb55 7405je System_Private_CoreLib!System.Buffers.Text.Utf8Parser.TryParse(System.ReadOnlySpan`1<Byte>, Int32 ByRef, Int32 ByRef, Char)+0xffffffff`a9e2999c (00007fff`4803cb5c)00007fff`4803cb57 83fe44 cmpesi,44h00007fff`4803cb5a 754c jne System_Private_CoreLib!System.Buffers.Text.Utf8Parser.TryParse(System.ReadOnlySpan`1<Byte>, Int32 ByRef, Int32 ByRef, Char)+0xffffffff`a9e299e8 (00007fff`4803cba8)00007fff`4803cb5c 4883c420 addrsp,20h00007fff`4803cb60 5e poprsi00007fff`4803cb61 e97acaffff jmp CLRStub[MethodDescPrestub]@7fff480395e0 (00007fff`480395e0)00007fff`4803cb66 83fe47 cmpesi,47h00007fff`4803cb69 74f1 je System_Private_CoreLib!System.Buffers.Text.Utf8Parser.TryParse(System.ReadOnlySpan`1<Byte>, Int32 ByRef, Int32 ByRef, Char)+0xffffffff`a9e2999c (00007fff`4803cb5c)00007fff`4803cb6b 83fe4e cmpesi,4Eh00007fff`4803cb6e 7538jne System_Private_CoreLib!System.Buffers.Text.Utf8Parser.TryParse(System.ReadOnlySpan`1<Byte>, Int32 ByRef, Int32 ByRef, Char)+0xffffffff`a9e299e8 (00007fff`4803cba8)00007fff`4803cb70 4883c420 addrsp,20h00007fff`4803cb74 5e poprsi00007fff`4803cb75 e986caffff jmp CLRStub[MethodDescPrestub]@7fff48039600 (00007fff`48039600)00007fff`4803cb7a 83fe64 cmpesi,64h00007fff`4803cb7d 770c ja System_Private_CoreLib!System.Buffers.Text.Utf8Parser.TryParse(System.ReadOnlySpan`1<Byte>, Int32 ByRef, Int32 ByRef, Char)+0xffffffff`a9e299cb (00007fff`4803cb8b)00007fff`4803cb7f 83fe58 cmpesi,58h00007fff`4803cb82 7416je System_Private_CoreLib!System.Buffers.Text.Utf8Parser.TryParse(System.ReadOnlySpan`1<Byte>, Int32 ByRef, Int32 ByRef, Char)+0xffffffff`a9e299da (00007fff`4803cb9a)00007fff`4803cb84 83fe64 cmpesi,64h00007fff`4803cb87 751f jne System_Private_CoreLib!System.Buffers.Text.Utf8Parser.TryParse(System.ReadOnlySpan`1<Byte>, Int32 ByRef, Int32 ByRef, Char)+0xffffffff`a9e299e8 (00007fff`4803cba8)00007fff`4803cb89 ebd1 jmp System_Private_CoreLib!System.Buffers.Text.Utf8Parser.TryParse(System.ReadOnlySpan`1<Byte>, Int32 ByRef, Int32 ByRef, Char)+0xffffffff`a9e2999c (00007fff`4803cb5c)00007fff`4803cb8b 83fe67 cmpesi,67h00007fff`4803cb8e 74cc je System_Private_CoreLib!System.Buffers.Text.Utf8Parser.TryParse(System.ReadOnlySpan`1<Byte>, Int32 ByRef, Int32 ByRef, Char)+0xffffffff`a9e2999c (00007fff`4803cb5c)00007fff`4803cb90 83fe6e cmpesi,6Eh00007fff`4803cb93 74db je System_Private_CoreLib!System.Buffers.Text.Utf8Parser.TryParse(System.ReadOnlySpan`1<Byte>, Int32 ByRef, Int32 ByRef, Char)+0xffffffff`a9e299b0 (00007fff`4803cb70)00007fff`4803cb95 83fe78 cmpesi,78h00007fff`4803cb98 750e jne System_Private_CoreLib!System.Buffers.Text.Utf8Parser.TryParse(System.ReadOnlySpan`1<Byte>, Int32 ByRef, Int32 ByRef, Char)+0xffffffff`a9e299e8 (00007fff`4803cba8)00007fff`4803cb9a 33c0 xoreax,eax00007fff`4803cb9c 8902mov dword ptr [rdx],eax00007fff`4803cb9e 4883c420 addrsp,20h00007fff`4803cba2 5e poprsi00007fff`4803cba3 e9d8caffff jmp CLRStub[MethodDescPrestub]@7fff48039680 (00007fff`48039680)00007fff`4803cba8 33c0 xoreax,eax00007fff`4803cbaa 8902mov dword ptr [rdx],eax00007fff`4803cbac 418900mov dword ptr [r8],eax00007fff`4803cbaf e8dcd8d8ff call CLRStub[MethodDescPrestub]@7fff47dca490 (00007fff`47dca490)00007fff`4803cbb4 cc int3;;; NEW CODEGEN ;;;00007fff`4802cc60 410fb7c1 movzxeax,r9w00007fff`4802cc64 85c0 testeax,eax00007fff`4802cc66 7505jne System_Private_CoreLib!System.Buffers.Text.Utf8Parser.TryParse(System.ReadOnlySpan`1<Byte>, Int32 ByRef, Int32 ByRef, Char)+0xffffffff`a9e199bd (00007fff`4802cc6d)00007fff`4802cc68 e97bc9ffff jmp CLRStub[MethodDescPrestub]@7fff480295e8 (00007fff`480295e8)00007fff`4802cc6d 410fb7c1 movzxeax,r9w00007fff`4802cc71 83c820 oreax,20h00007fff`4802cc74 83f867 cmpeax,67h00007fff`4802cc77 7f0c jg System_Private_CoreLib!System.Buffers.Text.Utf8Parser.TryParse(System.ReadOnlySpan`1<Byte>, Int32 ByRef, Int32 ByRef, Char)+0xffffffff`a9e199d5 (00007fff`4802cc85)00007fff`4802cc79 83f864 cmpeax,64h00007fff`4802cc7c 74ea je System_Private_CoreLib!System.Buffers.Text.Utf8Parser.TryParse(System.ReadOnlySpan`1<Byte>, Int32 ByRef, Int32 ByRef, Char)+0xffffffff`a9e199b8 (00007fff`4802cc68)00007fff`4802cc7e 83f867 cmpeax,67h00007fff`4802cc81 74e5 je System_Private_CoreLib!System.Buffers.Text.Utf8Parser.TryParse(System.ReadOnlySpan`1<Byte>, Int32 ByRef, Int32 ByRef, Char)+0xffffffff`a9e199b8 (00007fff`4802cc68)00007fff`4802cc83 eb16 jmp System_Private_CoreLib!System.Buffers.Text.Utf8Parser.TryParse(System.ReadOnlySpan`1<Byte>, Int32 ByRef, Int32 ByRef, Char)+0xffffffff`a9e199eb (00007fff`4802cc9b)00007fff`4802cc85 83f86e cmpeax,6Eh00007fff`4802cc88 7407je System_Private_CoreLib!System.Buffers.Text.Utf8Parser.TryParse(System.ReadOnlySpan`1<Byte>, Int32 ByRef, Int32 ByRef, Char)+0xffffffff`a9e199e1 (00007fff`4802cc91)00007fff`4802cc8a 83f878 cmpeax,78h00007fff`4802cc8d 7407je System_Private_CoreLib!System.Buffers.Text.Utf8Parser.TryParse(System.ReadOnlySpan`1<Byte>, Int32 ByRef, Int32 ByRef, Char)+0xffffffff`a9e199e6 (00007fff`4802cc96)00007fff`4802cc8f eb0a jmp System_Private_CoreLib!System.Buffers.Text.Utf8Parser.TryParse(System.ReadOnlySpan`1<Byte>, Int32 ByRef, Int32 ByRef, Char)+0xffffffff`a9e199eb (00007fff`4802cc9b)00007fff`4802cc91 e972c9ffff jmp CLRStub[MethodDescPrestub]@7fff48029608 (00007fff`48029608)00007fff`4802cc96 e9edc9ffff jmp CLRStub[MethodDescPrestub]@7fff48029688 (00007fff`48029688)00007fff`4802cc9b e9a0fdffff jmp CLRStub[MethodDescPrestub]@7fff4802ca40 (00007fff`4802ca40)

TryParse(..., out bool, ...)

Minor improvement here to avoid the range check when dereferencing source[4]. There's also less register shuffling in the error path. Avoiding this shuffling doesn't impact the success case, but I saw it as low-hanging fruit since it reduces the overall method codegen size by a little bit.

TryParse(..., out Guid, ...)

Similar to the integral types, the switch statement has been restructured to have the common case go through a fast path. Additionally, by changing the signature of the TryParseGuidCore method, we can avoid the stack spillage that would normally result on Win64 from passing so many parameters to the workhorse routine.

MethodToolchainMeanErrorStdDevMedianRatioRatioSD
ParseGuid_Defaultmaster5,881.1 ns58.94 ns55.14 ns5,876.8 ns1.000.00
ParseGuid_Defaulttryparse5,545.2 ns88.36 ns82.65 ns5,517.9 ns0.940.02
ParseGuid_Bracesmaster6,154.5 ns119.47 ns132.79 ns6,137.5 ns1.000.00
ParseGuid_Bracestryparse5,821.1 ns67.85 ns63.47 ns5,803.3 ns0.950.02
;;; OLD CODEGEN ;;;00007fff`3ccccdd0 56pushrsi00007fff`3ccccdd1 4883ec40 subrsp,40h00007fff`3ccccdd5 c5f877 vzeroupper00007fff`3ccccdd8 33c0 xoreax,eax00007fff`3ccccdda 4889442430mov qword ptr [rsp+30h],rax00007fff`3ccccddf 410fb7f1 movzxesi,r9w00007fff`3ccccde3 83fe42 cmpesi,42h00007fff`3ccccde6 7745ja System_Private_CoreLib!System.Buffers.Text.Utf8Parser.TryParse(System.ReadOnlySpan`1<Byte>, System.Guid ByRef, Int32 ByRef, Char)+0xffffffff`a0c9a5ad (00007fff`3cccce2d)00007fff`3ccccde8 85f6 testesi,esi00007fff`3ccccdea 0f8481000000 je System_Private_CoreLib!System.Buffers.Text.Utf8Parser.TryParse(System.ReadOnlySpan`1<Byte>, System.Guid ByRef, Int32 ByRef, Char)+0xffffffff`a0c9a5f1 (00007fff`3cccce71)00007fff`3ccccdf0 83fe42 cmpesi,42h00007fff`3ccccdf3 0f85c3000000 jne System_Private_CoreLib!System.Buffers.Text.Utf8Parser.TryParse(System.ReadOnlySpan`1<Byte>, System.Guid ByRef, Int32 ByRef, Char)+0xffffffff`a0c9a63c (00007fff`3ccccebc)00007fff`3ccccdf9 c5fa6f01 vmovdqu xmm0, xmmword ptr [rcx]00007fff`3ccccdfd c5fa7f442430 vmovdqu xmmword ptr [rsp+30h],xmm000007fff`3cccce03 4889542420mov qword ptr [rsp+20h],rdx00007fff`3cccce08 4c89442428 mov qword ptr [rsp+28h],r800007fff`3cccce0d 488d4c2430 learcx,[rsp+30h]00007fff`3cccce12 ba01000000 movedx,100007fff`3cccce17 41b87b000000 movr8d,7Bh00007fff`3cccce1d 41b97d000000 movr9d,7Dh00007fff`3cccce23 e888c7ffff call CLRStub[MethodDescPrestub]@7fff3ccc95b0 (00007fff`3ccc95b0)00007fff`3cccce28 e986000000 jmp System_Private_CoreLib!System.Buffers.Text.Utf8Parser.TryParse(System.ReadOnlySpan`1<Byte>, System.Guid ByRef, Int32 ByRef, Char)+0xffffffff`a0c9a633 (00007fff`3cccceb3)00007fff`3cccce2d 83fe44 cmpesi,44h00007fff`3cccce30 743f je System_Private_CoreLib!System.Buffers.Text.Utf8Parser.TryParse(System.ReadOnlySpan`1<Byte>, System.Guid ByRef, Int32 ByRef, Char)+0xffffffff`a0c9a5f1 (00007fff`3cccce71)00007fff`3cccce32 83fe4e cmpesi,4Eh00007fff`3cccce35 7468je System_Private_CoreLib!System.Buffers.Text.Utf8Parser.TryParse(System.ReadOnlySpan`1<Byte>, System.Guid ByRef, Int32 ByRef, Char)+0xffffffff`a0c9a61f (00007fff`3cccce9f)00007fff`3cccce37 83fe50 cmpesi,50h00007fff`3cccce3a 0f857c000000 jne System_Private_CoreLib!System.Buffers.Text.Utf8Parser.TryParse(System.ReadOnlySpan`1<Byte>, System.Guid ByRef, Int32 ByRef, Char)+0xffffffff`a0c9a63c (00007fff`3ccccebc)00007fff`3cccce40 c5fa6f01 vmovdqu xmm0, xmmword ptr [rcx]00007fff`3cccce44 c5fa7f442430 vmovdqu xmmword ptr [rsp+30h],xmm000007fff`3cccce4a 4889542420mov qword ptr [rsp+20h],rdx00007fff`3cccce4f 4c89442428 mov qword ptr [rsp+28h],r800007fff`3cccce54 488d4c2430 learcx,[rsp+30h]00007fff`3cccce59 ba01000000 movedx,100007fff`3cccce5e 41b828000000 movr8d,28h00007fff`3cccce64 41b929000000 movr9d,29h00007fff`3cccce6a e841c7ffff call CLRStub[MethodDescPrestub]@7fff3ccc95b0 (00007fff`3ccc95b0)00007fff`3cccce6f eb42 jmp System_Private_CoreLib!System.Buffers.Text.Utf8Parser.TryParse(System.ReadOnlySpan`1<Byte>, System.Guid ByRef, Int32 ByRef, Char)+0xffffffff`a0c9a633 (00007fff`3cccceb3)00007fff`3cccce71 c5fa6f01 vmovdqu xmm0, xmmword ptr [rcx]00007fff`3cccce75 c5fa7f442430 vmovdqu xmmword ptr [rsp+30h],xmm000007fff`3cccce7b 4889542420mov qword ptr [rsp+20h],rdx00007fff`3cccce80 4c89442428 mov qword ptr [rsp+28h],r800007fff`3cccce85 488d4c2430 learcx,[rsp+30h]00007fff`3cccce8a 33d2 xoredx,edx00007fff`3cccce8c 41b820000000 movr8d,20h00007fff`3cccce92 41b920000000 movr9d,20h00007fff`3cccce98 e813c7ffff call CLRStub[MethodDescPrestub]@7fff3ccc95b0 (00007fff`3ccc95b0)00007fff`3cccce9d eb14 jmp System_Private_CoreLib!System.Buffers.Text.Utf8Parser.TryParse(System.ReadOnlySpan`1<Byte>, System.Guid ByRef, Int32 ByRef, Char)+0xffffffff`a0c9a633 (00007fff`3cccceb3)00007fff`3cccce9f c5fa6f01 vmovdqu xmm0, xmmword ptr [rcx]00007fff`3ccccea3 c5fa7f442430 vmovdqu xmmword ptr [rsp+30h],xmm000007fff`3ccccea9 488d4c2430 learcx,[rsp+30h]00007fff`3cccceae e8f5c6ffff call CLRStub[MethodDescPrestub]@7fff3ccc95a8 (00007fff`3ccc95a8)00007fff`3cccceb3 0fb6c0 movzxeax,al00007fff`3cccceb6 4883c440 addrsp,40h00007fff`3cccceba 5e poprsi00007fff`3ccccebb c3 ret00007fff`3ccccebc c5f857c0 vxorps xmm0,xmm0,xmm000007fff`3ccccec0 c5fa7f02 vmovdqu xmmword ptr [rdx],xmm000007fff`3ccccec4 33c0 xoreax,eax00007fff`3ccccec6 418900mov dword ptr [r8],eax00007fff`3ccccec9 e8c2d5d8ff call CLRStub[MethodDescPrestub]@7fff3ca5a490 (00007fff`3ca5a490)00007fff`3ccccece cc int3;;; NEW CODEGEN ;;;00007fff`3cccccc0 410fb7c1 movzxeax,r9w00007fff`3cccccc4 85c0 testeax,eax00007fff`3cccccc6 7508jne System_Private_CoreLib!System.Buffers.Text.Utf8Parser.TryParse(System.ReadOnlySpan`1<Byte>, System.Guid ByRef, Int32 ByRef, Char)+0xffffffff`a0c9a370 (00007fff`3cccccd0)00007fff`3cccccc8 4533c9 xorr9d,r9d00007fff`3ccccccb e9a8d1ffff jmp CLRStub[MethodDescPrestub]@7fff3ccc9e78 (00007fff`3ccc9e78)00007fff`3cccccd0 450fb7c9 movzxr9d,r9w00007fff`3cccccd4 4183f944 cmpr9d,44h00007fff`3cccccd8 770e ja System_Private_CoreLib!System.Buffers.Text.Utf8Parser.TryParse(System.ReadOnlySpan`1<Byte>, System.Guid ByRef, Int32 ByRef, Char)+0xffffffff`a0c9a388 (00007fff`3ccccce8)00007fff`3cccccda 4183f942 cmpr9d,42h00007fff`3cccccde 7416je System_Private_CoreLib!System.Buffers.Text.Utf8Parser.TryParse(System.ReadOnlySpan`1<Byte>, System.Guid ByRef, Int32 ByRef, Char)+0xffffffff`a0c9a396 (00007fff`3cccccf6)00007fff`3ccccce0 4183f944 cmpr9d,44h00007fff`3ccccce4 74e2 je System_Private_CoreLib!System.Buffers.Text.Utf8Parser.TryParse(System.ReadOnlySpan`1<Byte>, System.Guid ByRef, Int32 ByRef, Char)+0xffffffff`a0c9a368 (00007fff`3cccccc8)00007fff`3ccccce6 eb29 jmp System_Private_CoreLib!System.Buffers.Text.Utf8Parser.TryParse(System.ReadOnlySpan`1<Byte>, System.Guid ByRef, Int32 ByRef, Char)+0xffffffff`a0c9a3b1 (00007fff`3ccccd11)00007fff`3ccccce8 4183f94e cmpr9d,4Eh00007fff`3cccccec 741e je System_Private_CoreLib!System.Buffers.Text.Utf8Parser.TryParse(System.ReadOnlySpan`1<Byte>, System.Guid ByRef, Int32 ByRef, Char)+0xffffffff`a0c9a3ac (00007fff`3ccccd0c)00007fff`3cccccee 4183f950 cmpr9d,50h00007fff`3cccccf2 740d je System_Private_CoreLib!System.Buffers.Text.Utf8Parser.TryParse(System.ReadOnlySpan`1<Byte>, System.Guid ByRef, Int32 ByRef, Char)+0xffffffff`a0c9a3a1 (00007fff`3ccccd01)00007fff`3cccccf4 eb1b jmp System_Private_CoreLib!System.Buffers.Text.Utf8Parser.TryParse(System.ReadOnlySpan`1<Byte>, System.Guid ByRef, Int32 ByRef, Char)+0xffffffff`a0c9a3b1 (00007fff`3ccccd11)00007fff`3cccccf6 41b97b7d0000 movr9d,7D7Bh00007fff`3cccccfc e977d1ffff jmp CLRStub[MethodDescPrestub]@7fff3ccc9e78 (00007fff`3ccc9e78)00007fff`3ccccd01 41b928290000 movr9d,2928h00007fff`3ccccd07 e96cd1ffff jmp CLRStub[MethodDescPrestub]@7fff3ccc9e78 (00007fff`3ccc9e78)00007fff`3ccccd0c e95fd1ffff jmp CLRStub[MethodDescPrestub]@7fff3ccc9e70 (00007fff`3ccc9e70)00007fff`3ccccd11 e91afdffff jmp CLRStub[MethodDescPrestub]@7fff3cccca30 (00007fff`3cccca30)

if (ends != default)
{
if (source[0] != begin)
if (source[0] != (byte)ends)

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.

The JIT could produce better codegen for this pattern. See #33504.

@GrabYourPitchforksGrabYourPitchforks added the tenet-performance Performance related issue label Mar 12, 2020
@stephentoub

Copy link
Copy Markdown
Member

How does this relate to #32843?

Can similar tweaks be made to the corresponding char-based APIs (e.g. int.Parse)?

@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.

another really nice improvement @GrabYourPitchforks !

LGTM, thanks for adding the comment that explains the bit shifting logic

@GrabYourPitchforks

Copy link
Copy Markdown
MemberAuthor

@stephentoub That PR was primarily concerned with improving the workhorse routine. From that investigation came the idea of improving the entry point. This PR focuses just on the fixed overhead of the entry points while leaving the workhorse routines largely unchanged.

I haven't tried applying these optimizations to the char overloads yet because their entry points generally look different than these entry points. But I imagine once we get back to the PR that optimizes the workhorse routines the optimizations there will be generally applicable to both char and byte.

@tannergoodingtannergooding 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.

Changes LGTM. Had a couple questions about the codegen

@GrabYourPitchforks

Copy link
Copy Markdown
MemberAuthor

CI failures seem to be unrelated infrastructure failures.

@GrabYourPitchforks
GrabYourPitchforks merged commit 099bc4b into dotnet:masterMar 13, 2020
@GrabYourPitchforks
GrabYourPitchforks deleted the tryparse_upd branch March 13, 2020 00:57
@ghostghost locked as resolved and limited conversation to collaborators Dec 10, 2020
Sign up for freeto subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

area-System.Memorytenet-performancePerformance related issue

Projects

None yet

Development

Successfully merging this pull request may close these issues.

7 participants

@GrabYourPitchforks@stephentoub@lpereira@adamsitnik@tannergooding@davidwrighton@Dotnet-GitSync-Bot
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Highlight search terms from Google/DuckDuckGo/Bing referrer\n(function() {\n var ref = document.referrer;\n var terms = [];\n \n if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) {\n var url = new URL(ref);\n var q = url.searchParams.get('q') || url.searchParams.get('p');\n if (q) {\n terms = q.split(/\\s+/).filter(function(t) { return t.length > 2; });\n }\n }\n \n if (terms.length === 0) return;\n \n var style = document.createElement('style');\n style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }';\n document.head.appendChild(style);\n \n function highlight(node) {\n if (node.nodeType === 3) { // text node\n var text = node.textContent;\n var found = false;\n terms.forEach(function(term) {\n var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\') + ')', 'gi');\n if (regex.test(text)) {\n found = true;\n var frag = document.createDocumentFragment();\n var parts = text.split(regex);\n parts.forEach(function(part, i) {\n if (i % 2 === 0) {\n frag.appendChild(document.createTextNode(part));\n } else {\n var span = document.createElement('span');\n span.className = 'userscript-highlight';\n span.textContent = part;\n frag.appendChild(span);\n }\n });\n node.parentNode.replaceChild(frag, node);\n }\n });\n } else if (node.nodeType === 1 && node.childNodes) { // element\n var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT'];\n if (!skipTags.includes(node.tagName)) {\n Array.from(node.childNodes).forEach(highlight);\n }\n }\n }\n \n highlight(document.body);\n \n // Re-highlight on dynamic content\n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1 || node.nodeType === 3) highlight(node);\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Highlight Search Terms"); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Reduce fixed overhead of some Utf8Parser.TryParse methods - #33507

Merged
GrabYourPitchforks merged 3 commits into
dotnet:masterfrom
GrabYourPitchforks:tryparse_upd
Mar 13, 2020
Merged

Reduce fixed overhead of some Utf8Parser.TryParse methods#33507
GrabYourPitchforks merged 3 commits into
dotnet:masterfrom
GrabYourPitchforks:tryparse_upd

Conversation

@GrabYourPitchforks

Copy link
Copy Markdown
Member

This builds atop the work that Andy did as part of #33004. There are no behavioral changes here - only slight refactorings that produce better codegen at the entry points to some of the Utf8Parser.TryParse methods. In particular, this does not include many of the optimizations discussed at #32843.

Integral types

For the TryParse methods that work with integral types, reflowing the logic in this fashion moves the "default" behavior into a fast-path and reduces the total amount of logic in the switch statement. We're now able to tail-call into the workhorse methods without performing any register shuffling or stack spilling

MethodToolchainMeanErrorStdDevRatio
TryParseInt32master976.3 ns5.34 ns4.73 ns1.00
TryParseInt32tryparse913.9 ns8.03 ns6.71 ns0.94
;;; OLD CODEGEN ;;;00007fff`4803cb40 56pushrsi00007fff`4803cb41 4883ec20 subrsp,20h00007fff`4803cb45 410fb7f1 movzxesi,r9w00007fff`4803cb49 83fe4e cmpesi,4Eh00007fff`4803cb4c 772c ja System_Private_CoreLib!System.Buffers.Text.Utf8Parser.TryParse(System.ReadOnlySpan`1<Byte>, Int32 ByRef, Int32 ByRef, Char)+0xffffffff`a9e299ba (00007fff`4803cb7a)00007fff`4803cb4e 83fe44 cmpesi,44h00007fff`4803cb51 7713ja System_Private_CoreLib!System.Buffers.Text.Utf8Parser.TryParse(System.ReadOnlySpan`1<Byte>, Int32 ByRef, Int32 ByRef, Char)+0xffffffff`a9e299a6 (00007fff`4803cb66)00007fff`4803cb53 85f6 testesi,esi00007fff`4803cb55 7405je System_Private_CoreLib!System.Buffers.Text.Utf8Parser.TryParse(System.ReadOnlySpan`1<Byte>, Int32 ByRef, Int32 ByRef, Char)+0xffffffff`a9e2999c (00007fff`4803cb5c)00007fff`4803cb57 83fe44 cmpesi,44h00007fff`4803cb5a 754c jne System_Private_CoreLib!System.Buffers.Text.Utf8Parser.TryParse(System.ReadOnlySpan`1<Byte>, Int32 ByRef, Int32 ByRef, Char)+0xffffffff`a9e299e8 (00007fff`4803cba8)00007fff`4803cb5c 4883c420 addrsp,20h00007fff`4803cb60 5e poprsi00007fff`4803cb61 e97acaffff jmp CLRStub[MethodDescPrestub]@7fff480395e0 (00007fff`480395e0)00007fff`4803cb66 83fe47 cmpesi,47h00007fff`4803cb69 74f1 je System_Private_CoreLib!System.Buffers.Text.Utf8Parser.TryParse(System.ReadOnlySpan`1<Byte>, Int32 ByRef, Int32 ByRef, Char)+0xffffffff`a9e2999c (00007fff`4803cb5c)00007fff`4803cb6b 83fe4e cmpesi,4Eh00007fff`4803cb6e 7538jne System_Private_CoreLib!System.Buffers.Text.Utf8Parser.TryParse(System.ReadOnlySpan`1<Byte>, Int32 ByRef, Int32 ByRef, Char)+0xffffffff`a9e299e8 (00007fff`4803cba8)00007fff`4803cb70 4883c420 addrsp,20h00007fff`4803cb74 5e poprsi00007fff`4803cb75 e986caffff jmp CLRStub[MethodDescPrestub]@7fff48039600 (00007fff`48039600)00007fff`4803cb7a 83fe64 cmpesi,64h00007fff`4803cb7d 770c ja System_Private_CoreLib!System.Buffers.Text.Utf8Parser.TryParse(System.ReadOnlySpan`1<Byte>, Int32 ByRef, Int32 ByRef, Char)+0xffffffff`a9e299cb (00007fff`4803cb8b)00007fff`4803cb7f 83fe58 cmpesi,58h00007fff`4803cb82 7416je System_Private_CoreLib!System.Buffers.Text.Utf8Parser.TryParse(System.ReadOnlySpan`1<Byte>, Int32 ByRef, Int32 ByRef, Char)+0xffffffff`a9e299da (00007fff`4803cb9a)00007fff`4803cb84 83fe64 cmpesi,64h00007fff`4803cb87 751f jne System_Private_CoreLib!System.Buffers.Text.Utf8Parser.TryParse(System.ReadOnlySpan`1<Byte>, Int32 ByRef, Int32 ByRef, Char)+0xffffffff`a9e299e8 (00007fff`4803cba8)00007fff`4803cb89 ebd1 jmp System_Private_CoreLib!System.Buffers.Text.Utf8Parser.TryParse(System.ReadOnlySpan`1<Byte>, Int32 ByRef, Int32 ByRef, Char)+0xffffffff`a9e2999c (00007fff`4803cb5c)00007fff`4803cb8b 83fe67 cmpesi,67h00007fff`4803cb8e 74cc je System_Private_CoreLib!System.Buffers.Text.Utf8Parser.TryParse(System.ReadOnlySpan`1<Byte>, Int32 ByRef, Int32 ByRef, Char)+0xffffffff`a9e2999c (00007fff`4803cb5c)00007fff`4803cb90 83fe6e cmpesi,6Eh00007fff`4803cb93 74db je System_Private_CoreLib!System.Buffers.Text.Utf8Parser.TryParse(System.ReadOnlySpan`1<Byte>, Int32 ByRef, Int32 ByRef, Char)+0xffffffff`a9e299b0 (00007fff`4803cb70)00007fff`4803cb95 83fe78 cmpesi,78h00007fff`4803cb98 750e jne System_Private_CoreLib!System.Buffers.Text.Utf8Parser.TryParse(System.ReadOnlySpan`1<Byte>, Int32 ByRef, Int32 ByRef, Char)+0xffffffff`a9e299e8 (00007fff`4803cba8)00007fff`4803cb9a 33c0 xoreax,eax00007fff`4803cb9c 8902mov dword ptr [rdx],eax00007fff`4803cb9e 4883c420 addrsp,20h00007fff`4803cba2 5e poprsi00007fff`4803cba3 e9d8caffff jmp CLRStub[MethodDescPrestub]@7fff48039680 (00007fff`48039680)00007fff`4803cba8 33c0 xoreax,eax00007fff`4803cbaa 8902mov dword ptr [rdx],eax00007fff`4803cbac 418900mov dword ptr [r8],eax00007fff`4803cbaf e8dcd8d8ff call CLRStub[MethodDescPrestub]@7fff47dca490 (00007fff`47dca490)00007fff`4803cbb4 cc int3;;; NEW CODEGEN ;;;00007fff`4802cc60 410fb7c1 movzxeax,r9w00007fff`4802cc64 85c0 testeax,eax00007fff`4802cc66 7505jne System_Private_CoreLib!System.Buffers.Text.Utf8Parser.TryParse(System.ReadOnlySpan`1<Byte>, Int32 ByRef, Int32 ByRef, Char)+0xffffffff`a9e199bd (00007fff`4802cc6d)00007fff`4802cc68 e97bc9ffff jmp CLRStub[MethodDescPrestub]@7fff480295e8 (00007fff`480295e8)00007fff`4802cc6d 410fb7c1 movzxeax,r9w00007fff`4802cc71 83c820 oreax,20h00007fff`4802cc74 83f867 cmpeax,67h00007fff`4802cc77 7f0c jg System_Private_CoreLib!System.Buffers.Text.Utf8Parser.TryParse(System.ReadOnlySpan`1<Byte>, Int32 ByRef, Int32 ByRef, Char)+0xffffffff`a9e199d5 (00007fff`4802cc85)00007fff`4802cc79 83f864 cmpeax,64h00007fff`4802cc7c 74ea je System_Private_CoreLib!System.Buffers.Text.Utf8Parser.TryParse(System.ReadOnlySpan`1<Byte>, Int32 ByRef, Int32 ByRef, Char)+0xffffffff`a9e199b8 (00007fff`4802cc68)00007fff`4802cc7e 83f867 cmpeax,67h00007fff`4802cc81 74e5 je System_Private_CoreLib!System.Buffers.Text.Utf8Parser.TryParse(System.ReadOnlySpan`1<Byte>, Int32 ByRef, Int32 ByRef, Char)+0xffffffff`a9e199b8 (00007fff`4802cc68)00007fff`4802cc83 eb16 jmp System_Private_CoreLib!System.Buffers.Text.Utf8Parser.TryParse(System.ReadOnlySpan`1<Byte>, Int32 ByRef, Int32 ByRef, Char)+0xffffffff`a9e199eb (00007fff`4802cc9b)00007fff`4802cc85 83f86e cmpeax,6Eh00007fff`4802cc88 7407je System_Private_CoreLib!System.Buffers.Text.Utf8Parser.TryParse(System.ReadOnlySpan`1<Byte>, Int32 ByRef, Int32 ByRef, Char)+0xffffffff`a9e199e1 (00007fff`4802cc91)00007fff`4802cc8a 83f878 cmpeax,78h00007fff`4802cc8d 7407je System_Private_CoreLib!System.Buffers.Text.Utf8Parser.TryParse(System.ReadOnlySpan`1<Byte>, Int32 ByRef, Int32 ByRef, Char)+0xffffffff`a9e199e6 (00007fff`4802cc96)00007fff`4802cc8f eb0a jmp System_Private_CoreLib!System.Buffers.Text.Utf8Parser.TryParse(System.ReadOnlySpan`1<Byte>, Int32 ByRef, Int32 ByRef, Char)+0xffffffff`a9e199eb (00007fff`4802cc9b)00007fff`4802cc91 e972c9ffff jmp CLRStub[MethodDescPrestub]@7fff48029608 (00007fff`48029608)00007fff`4802cc96 e9edc9ffff jmp CLRStub[MethodDescPrestub]@7fff48029688 (00007fff`48029688)00007fff`4802cc9b e9a0fdffff jmp CLRStub[MethodDescPrestub]@7fff4802ca40 (00007fff`4802ca40)

TryParse(..., out bool, ...)

Minor improvement here to avoid the range check when dereferencing source[4]. There's also less register shuffling in the error path. Avoiding this shuffling doesn't impact the success case, but I saw it as low-hanging fruit since it reduces the overall method codegen size by a little bit.

TryParse(..., out Guid, ...)

Similar to the integral types, the switch statement has been restructured to have the common case go through a fast path. Additionally, by changing the signature of the TryParseGuidCore method, we can avoid the stack spillage that would normally result on Win64 from passing so many parameters to the workhorse routine.

MethodToolchainMeanErrorStdDevMedianRatioRatioSD
ParseGuid_Defaultmaster5,881.1 ns58.94 ns55.14 ns5,876.8 ns1.000.00
ParseGuid_Defaulttryparse5,545.2 ns88.36 ns82.65 ns5,517.9 ns0.940.02
ParseGuid_Bracesmaster6,154.5 ns119.47 ns132.79 ns6,137.5 ns1.000.00
ParseGuid_Bracestryparse5,821.1 ns67.85 ns63.47 ns5,803.3 ns0.950.02
;;; OLD CODEGEN ;;;00007fff`3ccccdd0 56pushrsi00007fff`3ccccdd1 4883ec40 subrsp,40h00007fff`3ccccdd5 c5f877 vzeroupper00007fff`3ccccdd8 33c0 xoreax,eax00007fff`3ccccdda 4889442430mov qword ptr [rsp+30h],rax00007fff`3ccccddf 410fb7f1 movzxesi,r9w00007fff`3ccccde3 83fe42 cmpesi,42h00007fff`3ccccde6 7745ja System_Private_CoreLib!System.Buffers.Text.Utf8Parser.TryParse(System.ReadOnlySpan`1<Byte>, System.Guid ByRef, Int32 ByRef, Char)+0xffffffff`a0c9a5ad (00007fff`3cccce2d)00007fff`3ccccde8 85f6 testesi,esi00007fff`3ccccdea 0f8481000000 je System_Private_CoreLib!System.Buffers.Text.Utf8Parser.TryParse(System.ReadOnlySpan`1<Byte>, System.Guid ByRef, Int32 ByRef, Char)+0xffffffff`a0c9a5f1 (00007fff`3cccce71)00007fff`3ccccdf0 83fe42 cmpesi,42h00007fff`3ccccdf3 0f85c3000000 jne System_Private_CoreLib!System.Buffers.Text.Utf8Parser.TryParse(System.ReadOnlySpan`1<Byte>, System.Guid ByRef, Int32 ByRef, Char)+0xffffffff`a0c9a63c (00007fff`3ccccebc)00007fff`3ccccdf9 c5fa6f01 vmovdqu xmm0, xmmword ptr [rcx]00007fff`3ccccdfd c5fa7f442430 vmovdqu xmmword ptr [rsp+30h],xmm000007fff`3cccce03 4889542420mov qword ptr [rsp+20h],rdx00007fff`3cccce08 4c89442428 mov qword ptr [rsp+28h],r800007fff`3cccce0d 488d4c2430 learcx,[rsp+30h]00007fff`3cccce12 ba01000000 movedx,100007fff`3cccce17 41b87b000000 movr8d,7Bh00007fff`3cccce1d 41b97d000000 movr9d,7Dh00007fff`3cccce23 e888c7ffff call CLRStub[MethodDescPrestub]@7fff3ccc95b0 (00007fff`3ccc95b0)00007fff`3cccce28 e986000000 jmp System_Private_CoreLib!System.Buffers.Text.Utf8Parser.TryParse(System.ReadOnlySpan`1<Byte>, System.Guid ByRef, Int32 ByRef, Char)+0xffffffff`a0c9a633 (00007fff`3cccceb3)00007fff`3cccce2d 83fe44 cmpesi,44h00007fff`3cccce30 743f je System_Private_CoreLib!System.Buffers.Text.Utf8Parser.TryParse(System.ReadOnlySpan`1<Byte>, System.Guid ByRef, Int32 ByRef, Char)+0xffffffff`a0c9a5f1 (00007fff`3cccce71)00007fff`3cccce32 83fe4e cmpesi,4Eh00007fff`3cccce35 7468je System_Private_CoreLib!System.Buffers.Text.Utf8Parser.TryParse(System.ReadOnlySpan`1<Byte>, System.Guid ByRef, Int32 ByRef, Char)+0xffffffff`a0c9a61f (00007fff`3cccce9f)00007fff`3cccce37 83fe50 cmpesi,50h00007fff`3cccce3a 0f857c000000 jne System_Private_CoreLib!System.Buffers.Text.Utf8Parser.TryParse(System.ReadOnlySpan`1<Byte>, System.Guid ByRef, Int32 ByRef, Char)+0xffffffff`a0c9a63c (00007fff`3ccccebc)00007fff`3cccce40 c5fa6f01 vmovdqu xmm0, xmmword ptr [rcx]00007fff`3cccce44 c5fa7f442430 vmovdqu xmmword ptr [rsp+30h],xmm000007fff`3cccce4a 4889542420mov qword ptr [rsp+20h],rdx00007fff`3cccce4f 4c89442428 mov qword ptr [rsp+28h],r800007fff`3cccce54 488d4c2430 learcx,[rsp+30h]00007fff`3cccce59 ba01000000 movedx,100007fff`3cccce5e 41b828000000 movr8d,28h00007fff`3cccce64 41b929000000 movr9d,29h00007fff`3cccce6a e841c7ffff call CLRStub[MethodDescPrestub]@7fff3ccc95b0 (00007fff`3ccc95b0)00007fff`3cccce6f eb42 jmp System_Private_CoreLib!System.Buffers.Text.Utf8Parser.TryParse(System.ReadOnlySpan`1<Byte>, System.Guid ByRef, Int32 ByRef, Char)+0xffffffff`a0c9a633 (00007fff`3cccceb3)00007fff`3cccce71 c5fa6f01 vmovdqu xmm0, xmmword ptr [rcx]00007fff`3cccce75 c5fa7f442430 vmovdqu xmmword ptr [rsp+30h],xmm000007fff`3cccce7b 4889542420mov qword ptr [rsp+20h],rdx00007fff`3cccce80 4c89442428 mov qword ptr [rsp+28h],r800007fff`3cccce85 488d4c2430 learcx,[rsp+30h]00007fff`3cccce8a 33d2 xoredx,edx00007fff`3cccce8c 41b820000000 movr8d,20h00007fff`3cccce92 41b920000000 movr9d,20h00007fff`3cccce98 e813c7ffff call CLRStub[MethodDescPrestub]@7fff3ccc95b0 (00007fff`3ccc95b0)00007fff`3cccce9d eb14 jmp System_Private_CoreLib!System.Buffers.Text.Utf8Parser.TryParse(System.ReadOnlySpan`1<Byte>, System.Guid ByRef, Int32 ByRef, Char)+0xffffffff`a0c9a633 (00007fff`3cccceb3)00007fff`3cccce9f c5fa6f01 vmovdqu xmm0, xmmword ptr [rcx]00007fff`3ccccea3 c5fa7f442430 vmovdqu xmmword ptr [rsp+30h],xmm000007fff`3ccccea9 488d4c2430 learcx,[rsp+30h]00007fff`3cccceae e8f5c6ffff call CLRStub[MethodDescPrestub]@7fff3ccc95a8 (00007fff`3ccc95a8)00007fff`3cccceb3 0fb6c0 movzxeax,al00007fff`3cccceb6 4883c440 addrsp,40h00007fff`3cccceba 5e poprsi00007fff`3ccccebb c3 ret00007fff`3ccccebc c5f857c0 vxorps xmm0,xmm0,xmm000007fff`3ccccec0 c5fa7f02 vmovdqu xmmword ptr [rdx],xmm000007fff`3ccccec4 33c0 xoreax,eax00007fff`3ccccec6 418900mov dword ptr [r8],eax00007fff`3ccccec9 e8c2d5d8ff call CLRStub[MethodDescPrestub]@7fff3ca5a490 (00007fff`3ca5a490)00007fff`3ccccece cc int3;;; NEW CODEGEN ;;;00007fff`3cccccc0 410fb7c1 movzxeax,r9w00007fff`3cccccc4 85c0 testeax,eax00007fff`3cccccc6 7508jne System_Private_CoreLib!System.Buffers.Text.Utf8Parser.TryParse(System.ReadOnlySpan`1<Byte>, System.Guid ByRef, Int32 ByRef, Char)+0xffffffff`a0c9a370 (00007fff`3cccccd0)00007fff`3cccccc8 4533c9 xorr9d,r9d00007fff`3ccccccb e9a8d1ffff jmp CLRStub[MethodDescPrestub]@7fff3ccc9e78 (00007fff`3ccc9e78)00007fff`3cccccd0 450fb7c9 movzxr9d,r9w00007fff`3cccccd4 4183f944 cmpr9d,44h00007fff`3cccccd8 770e ja System_Private_CoreLib!System.Buffers.Text.Utf8Parser.TryParse(System.ReadOnlySpan`1<Byte>, System.Guid ByRef, Int32 ByRef, Char)+0xffffffff`a0c9a388 (00007fff`3ccccce8)00007fff`3cccccda 4183f942 cmpr9d,42h00007fff`3cccccde 7416je System_Private_CoreLib!System.Buffers.Text.Utf8Parser.TryParse(System.ReadOnlySpan`1<Byte>, System.Guid ByRef, Int32 ByRef, Char)+0xffffffff`a0c9a396 (00007fff`3cccccf6)00007fff`3ccccce0 4183f944 cmpr9d,44h00007fff`3ccccce4 74e2 je System_Private_CoreLib!System.Buffers.Text.Utf8Parser.TryParse(System.ReadOnlySpan`1<Byte>, System.Guid ByRef, Int32 ByRef, Char)+0xffffffff`a0c9a368 (00007fff`3cccccc8)00007fff`3ccccce6 eb29 jmp System_Private_CoreLib!System.Buffers.Text.Utf8Parser.TryParse(System.ReadOnlySpan`1<Byte>, System.Guid ByRef, Int32 ByRef, Char)+0xffffffff`a0c9a3b1 (00007fff`3ccccd11)00007fff`3ccccce8 4183f94e cmpr9d,4Eh00007fff`3cccccec 741e je System_Private_CoreLib!System.Buffers.Text.Utf8Parser.TryParse(System.ReadOnlySpan`1<Byte>, System.Guid ByRef, Int32 ByRef, Char)+0xffffffff`a0c9a3ac (00007fff`3ccccd0c)00007fff`3cccccee 4183f950 cmpr9d,50h00007fff`3cccccf2 740d je System_Private_CoreLib!System.Buffers.Text.Utf8Parser.TryParse(System.ReadOnlySpan`1<Byte>, System.Guid ByRef, Int32 ByRef, Char)+0xffffffff`a0c9a3a1 (00007fff`3ccccd01)00007fff`3cccccf4 eb1b jmp System_Private_CoreLib!System.Buffers.Text.Utf8Parser.TryParse(System.ReadOnlySpan`1<Byte>, System.Guid ByRef, Int32 ByRef, Char)+0xffffffff`a0c9a3b1 (00007fff`3ccccd11)00007fff`3cccccf6 41b97b7d0000 movr9d,7D7Bh00007fff`3cccccfc e977d1ffff jmp CLRStub[MethodDescPrestub]@7fff3ccc9e78 (00007fff`3ccc9e78)00007fff`3ccccd01 41b928290000 movr9d,2928h00007fff`3ccccd07 e96cd1ffff jmp CLRStub[MethodDescPrestub]@7fff3ccc9e78 (00007fff`3ccc9e78)00007fff`3ccccd0c e95fd1ffff jmp CLRStub[MethodDescPrestub]@7fff3ccc9e70 (00007fff`3ccc9e70)00007fff`3ccccd11 e91afdffff jmp CLRStub[MethodDescPrestub]@7fff3cccca30 (00007fff`3cccca30)

if (ends != default)
{
if (source[0] != begin)
if (source[0] != (byte)ends)

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.

The JIT could produce better codegen for this pattern. See #33504.

@GrabYourPitchforksGrabYourPitchforks added the tenet-performance Performance related issue label Mar 12, 2020
@stephentoub

Copy link
Copy Markdown
Member

How does this relate to #32843?

Can similar tweaks be made to the corresponding char-based APIs (e.g. int.Parse)?

@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.

another really nice improvement @GrabYourPitchforks !

LGTM, thanks for adding the comment that explains the bit shifting logic

@GrabYourPitchforks

Copy link
Copy Markdown
MemberAuthor

@stephentoub That PR was primarily concerned with improving the workhorse routine. From that investigation came the idea of improving the entry point. This PR focuses just on the fixed overhead of the entry points while leaving the workhorse routines largely unchanged.

I haven't tried applying these optimizations to the char overloads yet because their entry points generally look different than these entry points. But I imagine once we get back to the PR that optimizes the workhorse routines the optimizations there will be generally applicable to both char and byte.

@tannergoodingtannergooding 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.

Changes LGTM. Had a couple questions about the codegen

@GrabYourPitchforks

Copy link
Copy Markdown
MemberAuthor

CI failures seem to be unrelated infrastructure failures.

@GrabYourPitchforks
GrabYourPitchforks merged commit 099bc4b into dotnet:masterMar 13, 2020
@GrabYourPitchforks
GrabYourPitchforks deleted the tryparse_upd branch March 13, 2020 00:57
@ghostghost locked as resolved and limited conversation to collaborators Dec 10, 2020
Sign up for freeto subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

area-System.Memorytenet-performancePerformance related issue

Projects

None yet

Development

Successfully merging this pull request may close these issues.

7 participants

@GrabYourPitchforks@stephentoub@lpereira@adamsitnik@tannergooding@davidwrighton@Dotnet-GitSync-Bot
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Strip utm_, fbclid, gclid, etc. from all links on page\n(function() {\n var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content',\n 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid',\n 'ref', 'ref_src', 'source', 'medium', 'campaign'];\n \n function cleanUrl(url) {\n try {\n var u = new URL(url, window.location.origin);\n var changed = false;\n trackingParams.forEach(function(p) {\n if (u.searchParams.has(p)) {\n u.searchParams.delete(p);\n changed = true;\n }\n });\n return changed ? u.toString() : url;\n } catch (e) {\n return url;\n }\n }\n \n function cleanLinks() {\n document.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n \n cleanLinks();\n \n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1) {\n if (node.tagName === 'A') cleanLinks();\n node.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Remove Tracking Parameters from Links"); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + '
Skip to content

Reduce fixed overhead of some Utf8Parser.TryParse methods - #33507

Merged
GrabYourPitchforks merged 3 commits into
dotnet:masterfrom
GrabYourPitchforks:tryparse_upd
Mar 13, 2020
Merged

Reduce fixed overhead of some Utf8Parser.TryParse methods#33507
GrabYourPitchforks merged 3 commits into
dotnet:masterfrom
GrabYourPitchforks:tryparse_upd

Conversation

@GrabYourPitchforks

Copy link
Copy Markdown
Member

This builds atop the work that Andy did as part of #33004. There are no behavioral changes here - only slight refactorings that produce better codegen at the entry points to some of the Utf8Parser.TryParse methods. In particular, this does not include many of the optimizations discussed at #32843.

Integral types

For the TryParse methods that work with integral types, reflowing the logic in this fashion moves the "default" behavior into a fast-path and reduces the total amount of logic in the switch statement. We're now able to tail-call into the workhorse methods without performing any register shuffling or stack spilling

MethodToolchainMeanErrorStdDevRatio
TryParseInt32master976.3 ns5.34 ns4.73 ns1.00
TryParseInt32tryparse913.9 ns8.03 ns6.71 ns0.94
;;; OLD CODEGEN ;;;00007fff`4803cb40 56pushrsi00007fff`4803cb41 4883ec20 subrsp,20h00007fff`4803cb45 410fb7f1 movzxesi,r9w00007fff`4803cb49 83fe4e cmpesi,4Eh00007fff`4803cb4c 772c ja System_Private_CoreLib!System.Buffers.Text.Utf8Parser.TryParse(System.ReadOnlySpan`1<Byte>, Int32 ByRef, Int32 ByRef, Char)+0xffffffff`a9e299ba (00007fff`4803cb7a)00007fff`4803cb4e 83fe44 cmpesi,44h00007fff`4803cb51 7713ja System_Private_CoreLib!System.Buffers.Text.Utf8Parser.TryParse(System.ReadOnlySpan`1<Byte>, Int32 ByRef, Int32 ByRef, Char)+0xffffffff`a9e299a6 (00007fff`4803cb66)00007fff`4803cb53 85f6 testesi,esi00007fff`4803cb55 7405je System_Private_CoreLib!System.Buffers.Text.Utf8Parser.TryParse(System.ReadOnlySpan`1<Byte>, Int32 ByRef, Int32 ByRef, Char)+0xffffffff`a9e2999c (00007fff`4803cb5c)00007fff`4803cb57 83fe44 cmpesi,44h00007fff`4803cb5a 754c jne System_Private_CoreLib!System.Buffers.Text.Utf8Parser.TryParse(System.ReadOnlySpan`1<Byte>, Int32 ByRef, Int32 ByRef, Char)+0xffffffff`a9e299e8 (00007fff`4803cba8)00007fff`4803cb5c 4883c420 addrsp,20h00007fff`4803cb60 5e poprsi00007fff`4803cb61 e97acaffff jmp CLRStub[MethodDescPrestub]@7fff480395e0 (00007fff`480395e0)00007fff`4803cb66 83fe47 cmpesi,47h00007fff`4803cb69 74f1 je System_Private_CoreLib!System.Buffers.Text.Utf8Parser.TryParse(System.ReadOnlySpan`1<Byte>, Int32 ByRef, Int32 ByRef, Char)+0xffffffff`a9e2999c (00007fff`4803cb5c)00007fff`4803cb6b 83fe4e cmpesi,4Eh00007fff`4803cb6e 7538jne System_Private_CoreLib!System.Buffers.Text.Utf8Parser.TryParse(System.ReadOnlySpan`1<Byte>, Int32 ByRef, Int32 ByRef, Char)+0xffffffff`a9e299e8 (00007fff`4803cba8)00007fff`4803cb70 4883c420 addrsp,20h00007fff`4803cb74 5e poprsi00007fff`4803cb75 e986caffff jmp CLRStub[MethodDescPrestub]@7fff48039600 (00007fff`48039600)00007fff`4803cb7a 83fe64 cmpesi,64h00007fff`4803cb7d 770c ja System_Private_CoreLib!System.Buffers.Text.Utf8Parser.TryParse(System.ReadOnlySpan`1<Byte>, Int32 ByRef, Int32 ByRef, Char)+0xffffffff`a9e299cb (00007fff`4803cb8b)00007fff`4803cb7f 83fe58 cmpesi,58h00007fff`4803cb82 7416je System_Private_CoreLib!System.Buffers.Text.Utf8Parser.TryParse(System.ReadOnlySpan`1<Byte>, Int32 ByRef, Int32 ByRef, Char)+0xffffffff`a9e299da (00007fff`4803cb9a)00007fff`4803cb84 83fe64 cmpesi,64h00007fff`4803cb87 751f jne System_Private_CoreLib!System.Buffers.Text.Utf8Parser.TryParse(System.ReadOnlySpan`1<Byte>, Int32 ByRef, Int32 ByRef, Char)+0xffffffff`a9e299e8 (00007fff`4803cba8)00007fff`4803cb89 ebd1 jmp System_Private_CoreLib!System.Buffers.Text.Utf8Parser.TryParse(System.ReadOnlySpan`1<Byte>, Int32 ByRef, Int32 ByRef, Char)+0xffffffff`a9e2999c (00007fff`4803cb5c)00007fff`4803cb8b 83fe67 cmpesi,67h00007fff`4803cb8e 74cc je System_Private_CoreLib!System.Buffers.Text.Utf8Parser.TryParse(System.ReadOnlySpan`1<Byte>, Int32 ByRef, Int32 ByRef, Char)+0xffffffff`a9e2999c (00007fff`4803cb5c)00007fff`4803cb90 83fe6e cmpesi,6Eh00007fff`4803cb93 74db je System_Private_CoreLib!System.Buffers.Text.Utf8Parser.TryParse(System.ReadOnlySpan`1<Byte>, Int32 ByRef, Int32 ByRef, Char)+0xffffffff`a9e299b0 (00007fff`4803cb70)00007fff`4803cb95 83fe78 cmpesi,78h00007fff`4803cb98 750e jne System_Private_CoreLib!System.Buffers.Text.Utf8Parser.TryParse(System.ReadOnlySpan`1<Byte>, Int32 ByRef, Int32 ByRef, Char)+0xffffffff`a9e299e8 (00007fff`4803cba8)00007fff`4803cb9a 33c0 xoreax,eax00007fff`4803cb9c 8902mov dword ptr [rdx],eax00007fff`4803cb9e 4883c420 addrsp,20h00007fff`4803cba2 5e poprsi00007fff`4803cba3 e9d8caffff jmp CLRStub[MethodDescPrestub]@7fff48039680 (00007fff`48039680)00007fff`4803cba8 33c0 xoreax,eax00007fff`4803cbaa 8902mov dword ptr [rdx],eax00007fff`4803cbac 418900mov dword ptr [r8],eax00007fff`4803cbaf e8dcd8d8ff call CLRStub[MethodDescPrestub]@7fff47dca490 (00007fff`47dca490)00007fff`4803cbb4 cc int3;;; NEW CODEGEN ;;;00007fff`4802cc60 410fb7c1 movzxeax,r9w00007fff`4802cc64 85c0 testeax,eax00007fff`4802cc66 7505jne System_Private_CoreLib!System.Buffers.Text.Utf8Parser.TryParse(System.ReadOnlySpan`1<Byte>, Int32 ByRef, Int32 ByRef, Char)+0xffffffff`a9e199bd (00007fff`4802cc6d)00007fff`4802cc68 e97bc9ffff jmp CLRStub[MethodDescPrestub]@7fff480295e8 (00007fff`480295e8)00007fff`4802cc6d 410fb7c1 movzxeax,r9w00007fff`4802cc71 83c820 oreax,20h00007fff`4802cc74 83f867 cmpeax,67h00007fff`4802cc77 7f0c jg System_Private_CoreLib!System.Buffers.Text.Utf8Parser.TryParse(System.ReadOnlySpan`1<Byte>, Int32 ByRef, Int32 ByRef, Char)+0xffffffff`a9e199d5 (00007fff`4802cc85)00007fff`4802cc79 83f864 cmpeax,64h00007fff`4802cc7c 74ea je System_Private_CoreLib!System.Buffers.Text.Utf8Parser.TryParse(System.ReadOnlySpan`1<Byte>, Int32 ByRef, Int32 ByRef, Char)+0xffffffff`a9e199b8 (00007fff`4802cc68)00007fff`4802cc7e 83f867 cmpeax,67h00007fff`4802cc81 74e5 je System_Private_CoreLib!System.Buffers.Text.Utf8Parser.TryParse(System.ReadOnlySpan`1<Byte>, Int32 ByRef, Int32 ByRef, Char)+0xffffffff`a9e199b8 (00007fff`4802cc68)00007fff`4802cc83 eb16 jmp System_Private_CoreLib!System.Buffers.Text.Utf8Parser.TryParse(System.ReadOnlySpan`1<Byte>, Int32 ByRef, Int32 ByRef, Char)+0xffffffff`a9e199eb (00007fff`4802cc9b)00007fff`4802cc85 83f86e cmpeax,6Eh00007fff`4802cc88 7407je System_Private_CoreLib!System.Buffers.Text.Utf8Parser.TryParse(System.ReadOnlySpan`1<Byte>, Int32 ByRef, Int32 ByRef, Char)+0xffffffff`a9e199e1 (00007fff`4802cc91)00007fff`4802cc8a 83f878 cmpeax,78h00007fff`4802cc8d 7407je System_Private_CoreLib!System.Buffers.Text.Utf8Parser.TryParse(System.ReadOnlySpan`1<Byte>, Int32 ByRef, Int32 ByRef, Char)+0xffffffff`a9e199e6 (00007fff`4802cc96)00007fff`4802cc8f eb0a jmp System_Private_CoreLib!System.Buffers.Text.Utf8Parser.TryParse(System.ReadOnlySpan`1<Byte>, Int32 ByRef, Int32 ByRef, Char)+0xffffffff`a9e199eb (00007fff`4802cc9b)00007fff`4802cc91 e972c9ffff jmp CLRStub[MethodDescPrestub]@7fff48029608 (00007fff`48029608)00007fff`4802cc96 e9edc9ffff jmp CLRStub[MethodDescPrestub]@7fff48029688 (00007fff`48029688)00007fff`4802cc9b e9a0fdffff jmp CLRStub[MethodDescPrestub]@7fff4802ca40 (00007fff`4802ca40)

TryParse(..., out bool, ...)

Minor improvement here to avoid the range check when dereferencing source[4]. There's also less register shuffling in the error path. Avoiding this shuffling doesn't impact the success case, but I saw it as low-hanging fruit since it reduces the overall method codegen size by a little bit.

TryParse(..., out Guid, ...)

Similar to the integral types, the switch statement has been restructured to have the common case go through a fast path. Additionally, by changing the signature of the TryParseGuidCore method, we can avoid the stack spillage that would normally result on Win64 from passing so many parameters to the workhorse routine.

MethodToolchainMeanErrorStdDevMedianRatioRatioSD
ParseGuid_Defaultmaster5,881.1 ns58.94 ns55.14 ns5,876.8 ns1.000.00
ParseGuid_Defaulttryparse5,545.2 ns88.36 ns82.65 ns5,517.9 ns0.940.02
ParseGuid_Bracesmaster6,154.5 ns119.47 ns132.79 ns6,137.5 ns1.000.00
ParseGuid_Bracestryparse5,821.1 ns67.85 ns63.47 ns5,803.3 ns0.950.02
;;; OLD CODEGEN ;;;00007fff`3ccccdd0 56pushrsi00007fff`3ccccdd1 4883ec40 subrsp,40h00007fff`3ccccdd5 c5f877 vzeroupper00007fff`3ccccdd8 33c0 xoreax,eax00007fff`3ccccdda 4889442430mov qword ptr [rsp+30h],rax00007fff`3ccccddf 410fb7f1 movzxesi,r9w00007fff`3ccccde3 83fe42 cmpesi,42h00007fff`3ccccde6 7745ja System_Private_CoreLib!System.Buffers.Text.Utf8Parser.TryParse(System.ReadOnlySpan`1<Byte>, System.Guid ByRef, Int32 ByRef, Char)+0xffffffff`a0c9a5ad (00007fff`3cccce2d)00007fff`3ccccde8 85f6 testesi,esi00007fff`3ccccdea 0f8481000000 je System_Private_CoreLib!System.Buffers.Text.Utf8Parser.TryParse(System.ReadOnlySpan`1<Byte>, System.Guid ByRef, Int32 ByRef, Char)+0xffffffff`a0c9a5f1 (00007fff`3cccce71)00007fff`3ccccdf0 83fe42 cmpesi,42h00007fff`3ccccdf3 0f85c3000000 jne System_Private_CoreLib!System.Buffers.Text.Utf8Parser.TryParse(System.ReadOnlySpan`1<Byte>, System.Guid ByRef, Int32 ByRef, Char)+0xffffffff`a0c9a63c (00007fff`3ccccebc)00007fff`3ccccdf9 c5fa6f01 vmovdqu xmm0, xmmword ptr [rcx]00007fff`3ccccdfd c5fa7f442430 vmovdqu xmmword ptr [rsp+30h],xmm000007fff`3cccce03 4889542420mov qword ptr [rsp+20h],rdx00007fff`3cccce08 4c89442428 mov qword ptr [rsp+28h],r800007fff`3cccce0d 488d4c2430 learcx,[rsp+30h]00007fff`3cccce12 ba01000000 movedx,100007fff`3cccce17 41b87b000000 movr8d,7Bh00007fff`3cccce1d 41b97d000000 movr9d,7Dh00007fff`3cccce23 e888c7ffff call CLRStub[MethodDescPrestub]@7fff3ccc95b0 (00007fff`3ccc95b0)00007fff`3cccce28 e986000000 jmp System_Private_CoreLib!System.Buffers.Text.Utf8Parser.TryParse(System.ReadOnlySpan`1<Byte>, System.Guid ByRef, Int32 ByRef, Char)+0xffffffff`a0c9a633 (00007fff`3cccceb3)00007fff`3cccce2d 83fe44 cmpesi,44h00007fff`3cccce30 743f je System_Private_CoreLib!System.Buffers.Text.Utf8Parser.TryParse(System.ReadOnlySpan`1<Byte>, System.Guid ByRef, Int32 ByRef, Char)+0xffffffff`a0c9a5f1 (00007fff`3cccce71)00007fff`3cccce32 83fe4e cmpesi,4Eh00007fff`3cccce35 7468je System_Private_CoreLib!System.Buffers.Text.Utf8Parser.TryParse(System.ReadOnlySpan`1<Byte>, System.Guid ByRef, Int32 ByRef, Char)+0xffffffff`a0c9a61f (00007fff`3cccce9f)00007fff`3cccce37 83fe50 cmpesi,50h00007fff`3cccce3a 0f857c000000 jne System_Private_CoreLib!System.Buffers.Text.Utf8Parser.TryParse(System.ReadOnlySpan`1<Byte>, System.Guid ByRef, Int32 ByRef, Char)+0xffffffff`a0c9a63c (00007fff`3ccccebc)00007fff`3cccce40 c5fa6f01 vmovdqu xmm0, xmmword ptr [rcx]00007fff`3cccce44 c5fa7f442430 vmovdqu xmmword ptr [rsp+30h],xmm000007fff`3cccce4a 4889542420mov qword ptr [rsp+20h],rdx00007fff`3cccce4f 4c89442428 mov qword ptr [rsp+28h],r800007fff`3cccce54 488d4c2430 learcx,[rsp+30h]00007fff`3cccce59 ba01000000 movedx,100007fff`3cccce5e 41b828000000 movr8d,28h00007fff`3cccce64 41b929000000 movr9d,29h00007fff`3cccce6a e841c7ffff call CLRStub[MethodDescPrestub]@7fff3ccc95b0 (00007fff`3ccc95b0)00007fff`3cccce6f eb42 jmp System_Private_CoreLib!System.Buffers.Text.Utf8Parser.TryParse(System.ReadOnlySpan`1<Byte>, System.Guid ByRef, Int32 ByRef, Char)+0xffffffff`a0c9a633 (00007fff`3cccceb3)00007fff`3cccce71 c5fa6f01 vmovdqu xmm0, xmmword ptr [rcx]00007fff`3cccce75 c5fa7f442430 vmovdqu xmmword ptr [rsp+30h],xmm000007fff`3cccce7b 4889542420mov qword ptr [rsp+20h],rdx00007fff`3cccce80 4c89442428 mov qword ptr [rsp+28h],r800007fff`3cccce85 488d4c2430 learcx,[rsp+30h]00007fff`3cccce8a 33d2 xoredx,edx00007fff`3cccce8c 41b820000000 movr8d,20h00007fff`3cccce92 41b920000000 movr9d,20h00007fff`3cccce98 e813c7ffff call CLRStub[MethodDescPrestub]@7fff3ccc95b0 (00007fff`3ccc95b0)00007fff`3cccce9d eb14 jmp System_Private_CoreLib!System.Buffers.Text.Utf8Parser.TryParse(System.ReadOnlySpan`1<Byte>, System.Guid ByRef, Int32 ByRef, Char)+0xffffffff`a0c9a633 (00007fff`3cccceb3)00007fff`3cccce9f c5fa6f01 vmovdqu xmm0, xmmword ptr [rcx]00007fff`3ccccea3 c5fa7f442430 vmovdqu xmmword ptr [rsp+30h],xmm000007fff`3ccccea9 488d4c2430 learcx,[rsp+30h]00007fff`3cccceae e8f5c6ffff call CLRStub[MethodDescPrestub]@7fff3ccc95a8 (00007fff`3ccc95a8)00007fff`3cccceb3 0fb6c0 movzxeax,al00007fff`3cccceb6 4883c440 addrsp,40h00007fff`3cccceba 5e poprsi00007fff`3ccccebb c3 ret00007fff`3ccccebc c5f857c0 vxorps xmm0,xmm0,xmm000007fff`3ccccec0 c5fa7f02 vmovdqu xmmword ptr [rdx],xmm000007fff`3ccccec4 33c0 xoreax,eax00007fff`3ccccec6 418900mov dword ptr [r8],eax00007fff`3ccccec9 e8c2d5d8ff call CLRStub[MethodDescPrestub]@7fff3ca5a490 (00007fff`3ca5a490)00007fff`3ccccece cc int3;;; NEW CODEGEN ;;;00007fff`3cccccc0 410fb7c1 movzxeax,r9w00007fff`3cccccc4 85c0 testeax,eax00007fff`3cccccc6 7508jne System_Private_CoreLib!System.Buffers.Text.Utf8Parser.TryParse(System.ReadOnlySpan`1<Byte>, System.Guid ByRef, Int32 ByRef, Char)+0xffffffff`a0c9a370 (00007fff`3cccccd0)00007fff`3cccccc8 4533c9 xorr9d,r9d00007fff`3ccccccb e9a8d1ffff jmp CLRStub[MethodDescPrestub]@7fff3ccc9e78 (00007fff`3ccc9e78)00007fff`3cccccd0 450fb7c9 movzxr9d,r9w00007fff`3cccccd4 4183f944 cmpr9d,44h00007fff`3cccccd8 770e ja System_Private_CoreLib!System.Buffers.Text.Utf8Parser.TryParse(System.ReadOnlySpan`1<Byte>, System.Guid ByRef, Int32 ByRef, Char)+0xffffffff`a0c9a388 (00007fff`3ccccce8)00007fff`3cccccda 4183f942 cmpr9d,42h00007fff`3cccccde 7416je System_Private_CoreLib!System.Buffers.Text.Utf8Parser.TryParse(System.ReadOnlySpan`1<Byte>, System.Guid ByRef, Int32 ByRef, Char)+0xffffffff`a0c9a396 (00007fff`3cccccf6)00007fff`3ccccce0 4183f944 cmpr9d,44h00007fff`3ccccce4 74e2 je System_Private_CoreLib!System.Buffers.Text.Utf8Parser.TryParse(System.ReadOnlySpan`1<Byte>, System.Guid ByRef, Int32 ByRef, Char)+0xffffffff`a0c9a368 (00007fff`3cccccc8)00007fff`3ccccce6 eb29 jmp System_Private_CoreLib!System.Buffers.Text.Utf8Parser.TryParse(System.ReadOnlySpan`1<Byte>, System.Guid ByRef, Int32 ByRef, Char)+0xffffffff`a0c9a3b1 (00007fff`3ccccd11)00007fff`3ccccce8 4183f94e cmpr9d,4Eh00007fff`3cccccec 741e je System_Private_CoreLib!System.Buffers.Text.Utf8Parser.TryParse(System.ReadOnlySpan`1<Byte>, System.Guid ByRef, Int32 ByRef, Char)+0xffffffff`a0c9a3ac (00007fff`3ccccd0c)00007fff`3cccccee 4183f950 cmpr9d,50h00007fff`3cccccf2 740d je System_Private_CoreLib!System.Buffers.Text.Utf8Parser.TryParse(System.ReadOnlySpan`1<Byte>, System.Guid ByRef, Int32 ByRef, Char)+0xffffffff`a0c9a3a1 (00007fff`3ccccd01)00007fff`3cccccf4 eb1b jmp System_Private_CoreLib!System.Buffers.Text.Utf8Parser.TryParse(System.ReadOnlySpan`1<Byte>, System.Guid ByRef, Int32 ByRef, Char)+0xffffffff`a0c9a3b1 (00007fff`3ccccd11)00007fff`3cccccf6 41b97b7d0000 movr9d,7D7Bh00007fff`3cccccfc e977d1ffff jmp CLRStub[MethodDescPrestub]@7fff3ccc9e78 (00007fff`3ccc9e78)00007fff`3ccccd01 41b928290000 movr9d,2928h00007fff`3ccccd07 e96cd1ffff jmp CLRStub[MethodDescPrestub]@7fff3ccc9e78 (00007fff`3ccc9e78)00007fff`3ccccd0c e95fd1ffff jmp CLRStub[MethodDescPrestub]@7fff3ccc9e70 (00007fff`3ccc9e70)00007fff`3ccccd11 e91afdffff jmp CLRStub[MethodDescPrestub]@7fff3cccca30 (00007fff`3cccca30)

if (ends != default)
{
if (source[0] != begin)
if (source[0] != (byte)ends)

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.

The JIT could produce better codegen for this pattern. See #33504.

@GrabYourPitchforksGrabYourPitchforks added the tenet-performance Performance related issue label Mar 12, 2020
@stephentoub

Copy link
Copy Markdown
Member

How does this relate to #32843?

Can similar tweaks be made to the corresponding char-based APIs (e.g. int.Parse)?

@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.

another really nice improvement @GrabYourPitchforks !

LGTM, thanks for adding the comment that explains the bit shifting logic

@GrabYourPitchforks

Copy link
Copy Markdown
MemberAuthor

@stephentoub That PR was primarily concerned with improving the workhorse routine. From that investigation came the idea of improving the entry point. This PR focuses just on the fixed overhead of the entry points while leaving the workhorse routines largely unchanged.

I haven't tried applying these optimizations to the char overloads yet because their entry points generally look different than these entry points. But I imagine once we get back to the PR that optimizes the workhorse routines the optimizations there will be generally applicable to both char and byte.

@tannergoodingtannergooding 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.

Changes LGTM. Had a couple questions about the codegen

@GrabYourPitchforks

Copy link
Copy Markdown
MemberAuthor

CI failures seem to be unrelated infrastructure failures.

@GrabYourPitchforks
GrabYourPitchforks merged commit 099bc4b into dotnet:masterMar 13, 2020
@GrabYourPitchforks
GrabYourPitchforks deleted the tryparse_upd branch March 13, 2020 00:57
@ghostghost locked as resolved and limited conversation to collaborators Dec 10, 2020
Sign up for freeto subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

area-System.Memorytenet-performancePerformance related issue

Projects

None yet

Development

Successfully merging this pull request may close these issues.

7 participants

@GrabYourPitchforks@stephentoub@lpereira@adamsitnik@tannergooding@davidwrighton@Dotnet-GitSync-Bot
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Auto-enable theater mode on YouTube\n(function() {\n function tryTheater() {\n var btn = document.querySelector('button[aria-label=\"Theater mode\"], ytd-player #player button[title=\"Theater mode\"]');\n if (btn && !btn.classList.contains('activated')) {\n btn.click();\n }\n }\n \n // Try immediately\n tryTheater();\n \n // Try after navigation (SPA)\n var lastUrl = location.href;\n setInterval(function() {\n if (location.href !== lastUrl) {\n lastUrl = location.href;\n setTimeout(tryTheater, 500);\n }\n }, 1000);\n \n // Also try on player load\n var observer = new MutationObserver(tryTheater);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "YouTube Theater Mode Default"); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Reduce fixed overhead of some Utf8Parser.TryParse methods - #33507

Merged
GrabYourPitchforks merged 3 commits into
dotnet:masterfrom
GrabYourPitchforks:tryparse_upd
Mar 13, 2020
Merged

Reduce fixed overhead of some Utf8Parser.TryParse methods#33507
GrabYourPitchforks merged 3 commits into
dotnet:masterfrom
GrabYourPitchforks:tryparse_upd

Conversation

@GrabYourPitchforks

Copy link
Copy Markdown
Member

This builds atop the work that Andy did as part of #33004. There are no behavioral changes here - only slight refactorings that produce better codegen at the entry points to some of the Utf8Parser.TryParse methods. In particular, this does not include many of the optimizations discussed at #32843.

Integral types

For the TryParse methods that work with integral types, reflowing the logic in this fashion moves the "default" behavior into a fast-path and reduces the total amount of logic in the switch statement. We're now able to tail-call into the workhorse methods without performing any register shuffling or stack spilling

MethodToolchainMeanErrorStdDevRatio
TryParseInt32master976.3 ns5.34 ns4.73 ns1.00
TryParseInt32tryparse913.9 ns8.03 ns6.71 ns0.94
;;; OLD CODEGEN ;;;00007fff`4803cb40 56pushrsi00007fff`4803cb41 4883ec20 subrsp,20h00007fff`4803cb45 410fb7f1 movzxesi,r9w00007fff`4803cb49 83fe4e cmpesi,4Eh00007fff`4803cb4c 772c ja System_Private_CoreLib!System.Buffers.Text.Utf8Parser.TryParse(System.ReadOnlySpan`1<Byte>, Int32 ByRef, Int32 ByRef, Char)+0xffffffff`a9e299ba (00007fff`4803cb7a)00007fff`4803cb4e 83fe44 cmpesi,44h00007fff`4803cb51 7713ja System_Private_CoreLib!System.Buffers.Text.Utf8Parser.TryParse(System.ReadOnlySpan`1<Byte>, Int32 ByRef, Int32 ByRef, Char)+0xffffffff`a9e299a6 (00007fff`4803cb66)00007fff`4803cb53 85f6 testesi,esi00007fff`4803cb55 7405je System_Private_CoreLib!System.Buffers.Text.Utf8Parser.TryParse(System.ReadOnlySpan`1<Byte>, Int32 ByRef, Int32 ByRef, Char)+0xffffffff`a9e2999c (00007fff`4803cb5c)00007fff`4803cb57 83fe44 cmpesi,44h00007fff`4803cb5a 754c jne System_Private_CoreLib!System.Buffers.Text.Utf8Parser.TryParse(System.ReadOnlySpan`1<Byte>, Int32 ByRef, Int32 ByRef, Char)+0xffffffff`a9e299e8 (00007fff`4803cba8)00007fff`4803cb5c 4883c420 addrsp,20h00007fff`4803cb60 5e poprsi00007fff`4803cb61 e97acaffff jmp CLRStub[MethodDescPrestub]@7fff480395e0 (00007fff`480395e0)00007fff`4803cb66 83fe47 cmpesi,47h00007fff`4803cb69 74f1 je System_Private_CoreLib!System.Buffers.Text.Utf8Parser.TryParse(System.ReadOnlySpan`1<Byte>, Int32 ByRef, Int32 ByRef, Char)+0xffffffff`a9e2999c (00007fff`4803cb5c)00007fff`4803cb6b 83fe4e cmpesi,4Eh00007fff`4803cb6e 7538jne System_Private_CoreLib!System.Buffers.Text.Utf8Parser.TryParse(System.ReadOnlySpan`1<Byte>, Int32 ByRef, Int32 ByRef, Char)+0xffffffff`a9e299e8 (00007fff`4803cba8)00007fff`4803cb70 4883c420 addrsp,20h00007fff`4803cb74 5e poprsi00007fff`4803cb75 e986caffff jmp CLRStub[MethodDescPrestub]@7fff48039600 (00007fff`48039600)00007fff`4803cb7a 83fe64 cmpesi,64h00007fff`4803cb7d 770c ja System_Private_CoreLib!System.Buffers.Text.Utf8Parser.TryParse(System.ReadOnlySpan`1<Byte>, Int32 ByRef, Int32 ByRef, Char)+0xffffffff`a9e299cb (00007fff`4803cb8b)00007fff`4803cb7f 83fe58 cmpesi,58h00007fff`4803cb82 7416je System_Private_CoreLib!System.Buffers.Text.Utf8Parser.TryParse(System.ReadOnlySpan`1<Byte>, Int32 ByRef, Int32 ByRef, Char)+0xffffffff`a9e299da (00007fff`4803cb9a)00007fff`4803cb84 83fe64 cmpesi,64h00007fff`4803cb87 751f jne System_Private_CoreLib!System.Buffers.Text.Utf8Parser.TryParse(System.ReadOnlySpan`1<Byte>, Int32 ByRef, Int32 ByRef, Char)+0xffffffff`a9e299e8 (00007fff`4803cba8)00007fff`4803cb89 ebd1 jmp System_Private_CoreLib!System.Buffers.Text.Utf8Parser.TryParse(System.ReadOnlySpan`1<Byte>, Int32 ByRef, Int32 ByRef, Char)+0xffffffff`a9e2999c (00007fff`4803cb5c)00007fff`4803cb8b 83fe67 cmpesi,67h00007fff`4803cb8e 74cc je System_Private_CoreLib!System.Buffers.Text.Utf8Parser.TryParse(System.ReadOnlySpan`1<Byte>, Int32 ByRef, Int32 ByRef, Char)+0xffffffff`a9e2999c (00007fff`4803cb5c)00007fff`4803cb90 83fe6e cmpesi,6Eh00007fff`4803cb93 74db je System_Private_CoreLib!System.Buffers.Text.Utf8Parser.TryParse(System.ReadOnlySpan`1<Byte>, Int32 ByRef, Int32 ByRef, Char)+0xffffffff`a9e299b0 (00007fff`4803cb70)00007fff`4803cb95 83fe78 cmpesi,78h00007fff`4803cb98 750e jne System_Private_CoreLib!System.Buffers.Text.Utf8Parser.TryParse(System.ReadOnlySpan`1<Byte>, Int32 ByRef, Int32 ByRef, Char)+0xffffffff`a9e299e8 (00007fff`4803cba8)00007fff`4803cb9a 33c0 xoreax,eax00007fff`4803cb9c 8902mov dword ptr [rdx],eax00007fff`4803cb9e 4883c420 addrsp,20h00007fff`4803cba2 5e poprsi00007fff`4803cba3 e9d8caffff jmp CLRStub[MethodDescPrestub]@7fff48039680 (00007fff`48039680)00007fff`4803cba8 33c0 xoreax,eax00007fff`4803cbaa 8902mov dword ptr [rdx],eax00007fff`4803cbac 418900mov dword ptr [r8],eax00007fff`4803cbaf e8dcd8d8ff call CLRStub[MethodDescPrestub]@7fff47dca490 (00007fff`47dca490)00007fff`4803cbb4 cc int3;;; NEW CODEGEN ;;;00007fff`4802cc60 410fb7c1 movzxeax,r9w00007fff`4802cc64 85c0 testeax,eax00007fff`4802cc66 7505jne System_Private_CoreLib!System.Buffers.Text.Utf8Parser.TryParse(System.ReadOnlySpan`1<Byte>, Int32 ByRef, Int32 ByRef, Char)+0xffffffff`a9e199bd (00007fff`4802cc6d)00007fff`4802cc68 e97bc9ffff jmp CLRStub[MethodDescPrestub]@7fff480295e8 (00007fff`480295e8)00007fff`4802cc6d 410fb7c1 movzxeax,r9w00007fff`4802cc71 83c820 oreax,20h00007fff`4802cc74 83f867 cmpeax,67h00007fff`4802cc77 7f0c jg System_Private_CoreLib!System.Buffers.Text.Utf8Parser.TryParse(System.ReadOnlySpan`1<Byte>, Int32 ByRef, Int32 ByRef, Char)+0xffffffff`a9e199d5 (00007fff`4802cc85)00007fff`4802cc79 83f864 cmpeax,64h00007fff`4802cc7c 74ea je System_Private_CoreLib!System.Buffers.Text.Utf8Parser.TryParse(System.ReadOnlySpan`1<Byte>, Int32 ByRef, Int32 ByRef, Char)+0xffffffff`a9e199b8 (00007fff`4802cc68)00007fff`4802cc7e 83f867 cmpeax,67h00007fff`4802cc81 74e5 je System_Private_CoreLib!System.Buffers.Text.Utf8Parser.TryParse(System.ReadOnlySpan`1<Byte>, Int32 ByRef, Int32 ByRef, Char)+0xffffffff`a9e199b8 (00007fff`4802cc68)00007fff`4802cc83 eb16 jmp System_Private_CoreLib!System.Buffers.Text.Utf8Parser.TryParse(System.ReadOnlySpan`1<Byte>, Int32 ByRef, Int32 ByRef, Char)+0xffffffff`a9e199eb (00007fff`4802cc9b)00007fff`4802cc85 83f86e cmpeax,6Eh00007fff`4802cc88 7407je System_Private_CoreLib!System.Buffers.Text.Utf8Parser.TryParse(System.ReadOnlySpan`1<Byte>, Int32 ByRef, Int32 ByRef, Char)+0xffffffff`a9e199e1 (00007fff`4802cc91)00007fff`4802cc8a 83f878 cmpeax,78h00007fff`4802cc8d 7407je System_Private_CoreLib!System.Buffers.Text.Utf8Parser.TryParse(System.ReadOnlySpan`1<Byte>, Int32 ByRef, Int32 ByRef, Char)+0xffffffff`a9e199e6 (00007fff`4802cc96)00007fff`4802cc8f eb0a jmp System_Private_CoreLib!System.Buffers.Text.Utf8Parser.TryParse(System.ReadOnlySpan`1<Byte>, Int32 ByRef, Int32 ByRef, Char)+0xffffffff`a9e199eb (00007fff`4802cc9b)00007fff`4802cc91 e972c9ffff jmp CLRStub[MethodDescPrestub]@7fff48029608 (00007fff`48029608)00007fff`4802cc96 e9edc9ffff jmp CLRStub[MethodDescPrestub]@7fff48029688 (00007fff`48029688)00007fff`4802cc9b e9a0fdffff jmp CLRStub[MethodDescPrestub]@7fff4802ca40 (00007fff`4802ca40)

TryParse(..., out bool, ...)

Minor improvement here to avoid the range check when dereferencing source[4]. There's also less register shuffling in the error path. Avoiding this shuffling doesn't impact the success case, but I saw it as low-hanging fruit since it reduces the overall method codegen size by a little bit.

TryParse(..., out Guid, ...)

Similar to the integral types, the switch statement has been restructured to have the common case go through a fast path. Additionally, by changing the signature of the TryParseGuidCore method, we can avoid the stack spillage that would normally result on Win64 from passing so many parameters to the workhorse routine.

MethodToolchainMeanErrorStdDevMedianRatioRatioSD
ParseGuid_Defaultmaster5,881.1 ns58.94 ns55.14 ns5,876.8 ns1.000.00
ParseGuid_Defaulttryparse5,545.2 ns88.36 ns82.65 ns5,517.9 ns0.940.02
ParseGuid_Bracesmaster6,154.5 ns119.47 ns132.79 ns6,137.5 ns1.000.00
ParseGuid_Bracestryparse5,821.1 ns67.85 ns63.47 ns5,803.3 ns0.950.02
;;; OLD CODEGEN ;;;00007fff`3ccccdd0 56pushrsi00007fff`3ccccdd1 4883ec40 subrsp,40h00007fff`3ccccdd5 c5f877 vzeroupper00007fff`3ccccdd8 33c0 xoreax,eax00007fff`3ccccdda 4889442430mov qword ptr [rsp+30h],rax00007fff`3ccccddf 410fb7f1 movzxesi,r9w00007fff`3ccccde3 83fe42 cmpesi,42h00007fff`3ccccde6 7745ja System_Private_CoreLib!System.Buffers.Text.Utf8Parser.TryParse(System.ReadOnlySpan`1<Byte>, System.Guid ByRef, Int32 ByRef, Char)+0xffffffff`a0c9a5ad (00007fff`3cccce2d)00007fff`3ccccde8 85f6 testesi,esi00007fff`3ccccdea 0f8481000000 je System_Private_CoreLib!System.Buffers.Text.Utf8Parser.TryParse(System.ReadOnlySpan`1<Byte>, System.Guid ByRef, Int32 ByRef, Char)+0xffffffff`a0c9a5f1 (00007fff`3cccce71)00007fff`3ccccdf0 83fe42 cmpesi,42h00007fff`3ccccdf3 0f85c3000000 jne System_Private_CoreLib!System.Buffers.Text.Utf8Parser.TryParse(System.ReadOnlySpan`1<Byte>, System.Guid ByRef, Int32 ByRef, Char)+0xffffffff`a0c9a63c (00007fff`3ccccebc)00007fff`3ccccdf9 c5fa6f01 vmovdqu xmm0, xmmword ptr [rcx]00007fff`3ccccdfd c5fa7f442430 vmovdqu xmmword ptr [rsp+30h],xmm000007fff`3cccce03 4889542420mov qword ptr [rsp+20h],rdx00007fff`3cccce08 4c89442428 mov qword ptr [rsp+28h],r800007fff`3cccce0d 488d4c2430 learcx,[rsp+30h]00007fff`3cccce12 ba01000000 movedx,100007fff`3cccce17 41b87b000000 movr8d,7Bh00007fff`3cccce1d 41b97d000000 movr9d,7Dh00007fff`3cccce23 e888c7ffff call CLRStub[MethodDescPrestub]@7fff3ccc95b0 (00007fff`3ccc95b0)00007fff`3cccce28 e986000000 jmp System_Private_CoreLib!System.Buffers.Text.Utf8Parser.TryParse(System.ReadOnlySpan`1<Byte>, System.Guid ByRef, Int32 ByRef, Char)+0xffffffff`a0c9a633 (00007fff`3cccceb3)00007fff`3cccce2d 83fe44 cmpesi,44h00007fff`3cccce30 743f je System_Private_CoreLib!System.Buffers.Text.Utf8Parser.TryParse(System.ReadOnlySpan`1<Byte>, System.Guid ByRef, Int32 ByRef, Char)+0xffffffff`a0c9a5f1 (00007fff`3cccce71)00007fff`3cccce32 83fe4e cmpesi,4Eh00007fff`3cccce35 7468je System_Private_CoreLib!System.Buffers.Text.Utf8Parser.TryParse(System.ReadOnlySpan`1<Byte>, System.Guid ByRef, Int32 ByRef, Char)+0xffffffff`a0c9a61f (00007fff`3cccce9f)00007fff`3cccce37 83fe50 cmpesi,50h00007fff`3cccce3a 0f857c000000 jne System_Private_CoreLib!System.Buffers.Text.Utf8Parser.TryParse(System.ReadOnlySpan`1<Byte>, System.Guid ByRef, Int32 ByRef, Char)+0xffffffff`a0c9a63c (00007fff`3ccccebc)00007fff`3cccce40 c5fa6f01 vmovdqu xmm0, xmmword ptr [rcx]00007fff`3cccce44 c5fa7f442430 vmovdqu xmmword ptr [rsp+30h],xmm000007fff`3cccce4a 4889542420mov qword ptr [rsp+20h],rdx00007fff`3cccce4f 4c89442428 mov qword ptr [rsp+28h],r800007fff`3cccce54 488d4c2430 learcx,[rsp+30h]00007fff`3cccce59 ba01000000 movedx,100007fff`3cccce5e 41b828000000 movr8d,28h00007fff`3cccce64 41b929000000 movr9d,29h00007fff`3cccce6a e841c7ffff call CLRStub[MethodDescPrestub]@7fff3ccc95b0 (00007fff`3ccc95b0)00007fff`3cccce6f eb42 jmp System_Private_CoreLib!System.Buffers.Text.Utf8Parser.TryParse(System.ReadOnlySpan`1<Byte>, System.Guid ByRef, Int32 ByRef, Char)+0xffffffff`a0c9a633 (00007fff`3cccceb3)00007fff`3cccce71 c5fa6f01 vmovdqu xmm0, xmmword ptr [rcx]00007fff`3cccce75 c5fa7f442430 vmovdqu xmmword ptr [rsp+30h],xmm000007fff`3cccce7b 4889542420mov qword ptr [rsp+20h],rdx00007fff`3cccce80 4c89442428 mov qword ptr [rsp+28h],r800007fff`3cccce85 488d4c2430 learcx,[rsp+30h]00007fff`3cccce8a 33d2 xoredx,edx00007fff`3cccce8c 41b820000000 movr8d,20h00007fff`3cccce92 41b920000000 movr9d,20h00007fff`3cccce98 e813c7ffff call CLRStub[MethodDescPrestub]@7fff3ccc95b0 (00007fff`3ccc95b0)00007fff`3cccce9d eb14 jmp System_Private_CoreLib!System.Buffers.Text.Utf8Parser.TryParse(System.ReadOnlySpan`1<Byte>, System.Guid ByRef, Int32 ByRef, Char)+0xffffffff`a0c9a633 (00007fff`3cccceb3)00007fff`3cccce9f c5fa6f01 vmovdqu xmm0, xmmword ptr [rcx]00007fff`3ccccea3 c5fa7f442430 vmovdqu xmmword ptr [rsp+30h],xmm000007fff`3ccccea9 488d4c2430 learcx,[rsp+30h]00007fff`3cccceae e8f5c6ffff call CLRStub[MethodDescPrestub]@7fff3ccc95a8 (00007fff`3ccc95a8)00007fff`3cccceb3 0fb6c0 movzxeax,al00007fff`3cccceb6 4883c440 addrsp,40h00007fff`3cccceba 5e poprsi00007fff`3ccccebb c3 ret00007fff`3ccccebc c5f857c0 vxorps xmm0,xmm0,xmm000007fff`3ccccec0 c5fa7f02 vmovdqu xmmword ptr [rdx],xmm000007fff`3ccccec4 33c0 xoreax,eax00007fff`3ccccec6 418900mov dword ptr [r8],eax00007fff`3ccccec9 e8c2d5d8ff call CLRStub[MethodDescPrestub]@7fff3ca5a490 (00007fff`3ca5a490)00007fff`3ccccece cc int3;;; NEW CODEGEN ;;;00007fff`3cccccc0 410fb7c1 movzxeax,r9w00007fff`3cccccc4 85c0 testeax,eax00007fff`3cccccc6 7508jne System_Private_CoreLib!System.Buffers.Text.Utf8Parser.TryParse(System.ReadOnlySpan`1<Byte>, System.Guid ByRef, Int32 ByRef, Char)+0xffffffff`a0c9a370 (00007fff`3cccccd0)00007fff`3cccccc8 4533c9 xorr9d,r9d00007fff`3ccccccb e9a8d1ffff jmp CLRStub[MethodDescPrestub]@7fff3ccc9e78 (00007fff`3ccc9e78)00007fff`3cccccd0 450fb7c9 movzxr9d,r9w00007fff`3cccccd4 4183f944 cmpr9d,44h00007fff`3cccccd8 770e ja System_Private_CoreLib!System.Buffers.Text.Utf8Parser.TryParse(System.ReadOnlySpan`1<Byte>, System.Guid ByRef, Int32 ByRef, Char)+0xffffffff`a0c9a388 (00007fff`3ccccce8)00007fff`3cccccda 4183f942 cmpr9d,42h00007fff`3cccccde 7416je System_Private_CoreLib!System.Buffers.Text.Utf8Parser.TryParse(System.ReadOnlySpan`1<Byte>, System.Guid ByRef, Int32 ByRef, Char)+0xffffffff`a0c9a396 (00007fff`3cccccf6)00007fff`3ccccce0 4183f944 cmpr9d,44h00007fff`3ccccce4 74e2 je System_Private_CoreLib!System.Buffers.Text.Utf8Parser.TryParse(System.ReadOnlySpan`1<Byte>, System.Guid ByRef, Int32 ByRef, Char)+0xffffffff`a0c9a368 (00007fff`3cccccc8)00007fff`3ccccce6 eb29 jmp System_Private_CoreLib!System.Buffers.Text.Utf8Parser.TryParse(System.ReadOnlySpan`1<Byte>, System.Guid ByRef, Int32 ByRef, Char)+0xffffffff`a0c9a3b1 (00007fff`3ccccd11)00007fff`3ccccce8 4183f94e cmpr9d,4Eh00007fff`3cccccec 741e je System_Private_CoreLib!System.Buffers.Text.Utf8Parser.TryParse(System.ReadOnlySpan`1<Byte>, System.Guid ByRef, Int32 ByRef, Char)+0xffffffff`a0c9a3ac (00007fff`3ccccd0c)00007fff`3cccccee 4183f950 cmpr9d,50h00007fff`3cccccf2 740d je System_Private_CoreLib!System.Buffers.Text.Utf8Parser.TryParse(System.ReadOnlySpan`1<Byte>, System.Guid ByRef, Int32 ByRef, Char)+0xffffffff`a0c9a3a1 (00007fff`3ccccd01)00007fff`3cccccf4 eb1b jmp System_Private_CoreLib!System.Buffers.Text.Utf8Parser.TryParse(System.ReadOnlySpan`1<Byte>, System.Guid ByRef, Int32 ByRef, Char)+0xffffffff`a0c9a3b1 (00007fff`3ccccd11)00007fff`3cccccf6 41b97b7d0000 movr9d,7D7Bh00007fff`3cccccfc e977d1ffff jmp CLRStub[MethodDescPrestub]@7fff3ccc9e78 (00007fff`3ccc9e78)00007fff`3ccccd01 41b928290000 movr9d,2928h00007fff`3ccccd07 e96cd1ffff jmp CLRStub[MethodDescPrestub]@7fff3ccc9e78 (00007fff`3ccc9e78)00007fff`3ccccd0c e95fd1ffff jmp CLRStub[MethodDescPrestub]@7fff3ccc9e70 (00007fff`3ccc9e70)00007fff`3ccccd11 e91afdffff jmp CLRStub[MethodDescPrestub]@7fff3cccca30 (00007fff`3cccca30)

if (ends != default)
{
if (source[0] != begin)
if (source[0] != (byte)ends)

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.

The JIT could produce better codegen for this pattern. See #33504.

@GrabYourPitchforksGrabYourPitchforks added the tenet-performance Performance related issue label Mar 12, 2020
@stephentoub

Copy link
Copy Markdown
Member

How does this relate to #32843?

Can similar tweaks be made to the corresponding char-based APIs (e.g. int.Parse)?

@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.

another really nice improvement @GrabYourPitchforks !

LGTM, thanks for adding the comment that explains the bit shifting logic

@GrabYourPitchforks

Copy link
Copy Markdown
MemberAuthor

@stephentoub That PR was primarily concerned with improving the workhorse routine. From that investigation came the idea of improving the entry point. This PR focuses just on the fixed overhead of the entry points while leaving the workhorse routines largely unchanged.

I haven't tried applying these optimizations to the char overloads yet because their entry points generally look different than these entry points. But I imagine once we get back to the PR that optimizes the workhorse routines the optimizations there will be generally applicable to both char and byte.

@tannergoodingtannergooding 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.

Changes LGTM. Had a couple questions about the codegen

@GrabYourPitchforks

Copy link
Copy Markdown
MemberAuthor

CI failures seem to be unrelated infrastructure failures.

@GrabYourPitchforks
GrabYourPitchforks merged commit 099bc4b into dotnet:masterMar 13, 2020
@GrabYourPitchforks
GrabYourPitchforks deleted the tryparse_upd branch March 13, 2020 00:57
@ghostghost locked as resolved and limited conversation to collaborators Dec 10, 2020
Sign up for freeto subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

area-System.Memorytenet-performancePerformance related issue

Projects

None yet

Development

Successfully merging this pull request may close these issues.

7 participants

@GrabYourPitchforks@stephentoub@lpereira@adamsitnik@tannergooding@davidwrighton@Dotnet-GitSync-Bot
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Remove or un-stick sticky/fixed headers that block content\n(function() {\n function unstick() {\n document.querySelectorAll('header, nav, [role=\"banner\"], .header, .navbar, .sticky, .fixed-top, [style*=\"position: fixed\"], [style*=\"position:sticky\"]').forEach(function(el) {\n if (el.style.position === 'fixed' || el.style.position === 'sticky' || \n getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') {\n el.style.position = 'static';\n el.style.top = 'auto';\n el.style.zIndex = 'auto';\n }\n });\n }\n \n unstick();\n \n var observer = new MutationObserver(unstick);\n observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] });\n})();", "Kill Sticky Headers"); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Reduce fixed overhead of some Utf8Parser.TryParse methods - #33507

Merged
GrabYourPitchforks merged 3 commits into
dotnet:masterfrom
GrabYourPitchforks:tryparse_upd
Mar 13, 2020
Merged

Reduce fixed overhead of some Utf8Parser.TryParse methods#33507
GrabYourPitchforks merged 3 commits into
dotnet:masterfrom
GrabYourPitchforks:tryparse_upd

Conversation

@GrabYourPitchforks

Copy link
Copy Markdown
Member

This builds atop the work that Andy did as part of #33004. There are no behavioral changes here - only slight refactorings that produce better codegen at the entry points to some of the Utf8Parser.TryParse methods. In particular, this does not include many of the optimizations discussed at #32843.

Integral types

For the TryParse methods that work with integral types, reflowing the logic in this fashion moves the "default" behavior into a fast-path and reduces the total amount of logic in the switch statement. We're now able to tail-call into the workhorse methods without performing any register shuffling or stack spilling

MethodToolchainMeanErrorStdDevRatio
TryParseInt32master976.3 ns5.34 ns4.73 ns1.00
TryParseInt32tryparse913.9 ns8.03 ns6.71 ns0.94
;;; OLD CODEGEN ;;;00007fff`4803cb40 56pushrsi00007fff`4803cb41 4883ec20 subrsp,20h00007fff`4803cb45 410fb7f1 movzxesi,r9w00007fff`4803cb49 83fe4e cmpesi,4Eh00007fff`4803cb4c 772c ja System_Private_CoreLib!System.Buffers.Text.Utf8Parser.TryParse(System.ReadOnlySpan`1<Byte>, Int32 ByRef, Int32 ByRef, Char)+0xffffffff`a9e299ba (00007fff`4803cb7a)00007fff`4803cb4e 83fe44 cmpesi,44h00007fff`4803cb51 7713ja System_Private_CoreLib!System.Buffers.Text.Utf8Parser.TryParse(System.ReadOnlySpan`1<Byte>, Int32 ByRef, Int32 ByRef, Char)+0xffffffff`a9e299a6 (00007fff`4803cb66)00007fff`4803cb53 85f6 testesi,esi00007fff`4803cb55 7405je System_Private_CoreLib!System.Buffers.Text.Utf8Parser.TryParse(System.ReadOnlySpan`1<Byte>, Int32 ByRef, Int32 ByRef, Char)+0xffffffff`a9e2999c (00007fff`4803cb5c)00007fff`4803cb57 83fe44 cmpesi,44h00007fff`4803cb5a 754c jne System_Private_CoreLib!System.Buffers.Text.Utf8Parser.TryParse(System.ReadOnlySpan`1<Byte>, Int32 ByRef, Int32 ByRef, Char)+0xffffffff`a9e299e8 (00007fff`4803cba8)00007fff`4803cb5c 4883c420 addrsp,20h00007fff`4803cb60 5e poprsi00007fff`4803cb61 e97acaffff jmp CLRStub[MethodDescPrestub]@7fff480395e0 (00007fff`480395e0)00007fff`4803cb66 83fe47 cmpesi,47h00007fff`4803cb69 74f1 je System_Private_CoreLib!System.Buffers.Text.Utf8Parser.TryParse(System.ReadOnlySpan`1<Byte>, Int32 ByRef, Int32 ByRef, Char)+0xffffffff`a9e2999c (00007fff`4803cb5c)00007fff`4803cb6b 83fe4e cmpesi,4Eh00007fff`4803cb6e 7538jne System_Private_CoreLib!System.Buffers.Text.Utf8Parser.TryParse(System.ReadOnlySpan`1<Byte>, Int32 ByRef, Int32 ByRef, Char)+0xffffffff`a9e299e8 (00007fff`4803cba8)00007fff`4803cb70 4883c420 addrsp,20h00007fff`4803cb74 5e poprsi00007fff`4803cb75 e986caffff jmp CLRStub[MethodDescPrestub]@7fff48039600 (00007fff`48039600)00007fff`4803cb7a 83fe64 cmpesi,64h00007fff`4803cb7d 770c ja System_Private_CoreLib!System.Buffers.Text.Utf8Parser.TryParse(System.ReadOnlySpan`1<Byte>, Int32 ByRef, Int32 ByRef, Char)+0xffffffff`a9e299cb (00007fff`4803cb8b)00007fff`4803cb7f 83fe58 cmpesi,58h00007fff`4803cb82 7416je System_Private_CoreLib!System.Buffers.Text.Utf8Parser.TryParse(System.ReadOnlySpan`1<Byte>, Int32 ByRef, Int32 ByRef, Char)+0xffffffff`a9e299da (00007fff`4803cb9a)00007fff`4803cb84 83fe64 cmpesi,64h00007fff`4803cb87 751f jne System_Private_CoreLib!System.Buffers.Text.Utf8Parser.TryParse(System.ReadOnlySpan`1<Byte>, Int32 ByRef, Int32 ByRef, Char)+0xffffffff`a9e299e8 (00007fff`4803cba8)00007fff`4803cb89 ebd1 jmp System_Private_CoreLib!System.Buffers.Text.Utf8Parser.TryParse(System.ReadOnlySpan`1<Byte>, Int32 ByRef, Int32 ByRef, Char)+0xffffffff`a9e2999c (00007fff`4803cb5c)00007fff`4803cb8b 83fe67 cmpesi,67h00007fff`4803cb8e 74cc je System_Private_CoreLib!System.Buffers.Text.Utf8Parser.TryParse(System.ReadOnlySpan`1<Byte>, Int32 ByRef, Int32 ByRef, Char)+0xffffffff`a9e2999c (00007fff`4803cb5c)00007fff`4803cb90 83fe6e cmpesi,6Eh00007fff`4803cb93 74db je System_Private_CoreLib!System.Buffers.Text.Utf8Parser.TryParse(System.ReadOnlySpan`1<Byte>, Int32 ByRef, Int32 ByRef, Char)+0xffffffff`a9e299b0 (00007fff`4803cb70)00007fff`4803cb95 83fe78 cmpesi,78h00007fff`4803cb98 750e jne System_Private_CoreLib!System.Buffers.Text.Utf8Parser.TryParse(System.ReadOnlySpan`1<Byte>, Int32 ByRef, Int32 ByRef, Char)+0xffffffff`a9e299e8 (00007fff`4803cba8)00007fff`4803cb9a 33c0 xoreax,eax00007fff`4803cb9c 8902mov dword ptr [rdx],eax00007fff`4803cb9e 4883c420 addrsp,20h00007fff`4803cba2 5e poprsi00007fff`4803cba3 e9d8caffff jmp CLRStub[MethodDescPrestub]@7fff48039680 (00007fff`48039680)00007fff`4803cba8 33c0 xoreax,eax00007fff`4803cbaa 8902mov dword ptr [rdx],eax00007fff`4803cbac 418900mov dword ptr [r8],eax00007fff`4803cbaf e8dcd8d8ff call CLRStub[MethodDescPrestub]@7fff47dca490 (00007fff`47dca490)00007fff`4803cbb4 cc int3;;; NEW CODEGEN ;;;00007fff`4802cc60 410fb7c1 movzxeax,r9w00007fff`4802cc64 85c0 testeax,eax00007fff`4802cc66 7505jne System_Private_CoreLib!System.Buffers.Text.Utf8Parser.TryParse(System.ReadOnlySpan`1<Byte>, Int32 ByRef, Int32 ByRef, Char)+0xffffffff`a9e199bd (00007fff`4802cc6d)00007fff`4802cc68 e97bc9ffff jmp CLRStub[MethodDescPrestub]@7fff480295e8 (00007fff`480295e8)00007fff`4802cc6d 410fb7c1 movzxeax,r9w00007fff`4802cc71 83c820 oreax,20h00007fff`4802cc74 83f867 cmpeax,67h00007fff`4802cc77 7f0c jg System_Private_CoreLib!System.Buffers.Text.Utf8Parser.TryParse(System.ReadOnlySpan`1<Byte>, Int32 ByRef, Int32 ByRef, Char)+0xffffffff`a9e199d5 (00007fff`4802cc85)00007fff`4802cc79 83f864 cmpeax,64h00007fff`4802cc7c 74ea je System_Private_CoreLib!System.Buffers.Text.Utf8Parser.TryParse(System.ReadOnlySpan`1<Byte>, Int32 ByRef, Int32 ByRef, Char)+0xffffffff`a9e199b8 (00007fff`4802cc68)00007fff`4802cc7e 83f867 cmpeax,67h00007fff`4802cc81 74e5 je System_Private_CoreLib!System.Buffers.Text.Utf8Parser.TryParse(System.ReadOnlySpan`1<Byte>, Int32 ByRef, Int32 ByRef, Char)+0xffffffff`a9e199b8 (00007fff`4802cc68)00007fff`4802cc83 eb16 jmp System_Private_CoreLib!System.Buffers.Text.Utf8Parser.TryParse(System.ReadOnlySpan`1<Byte>, Int32 ByRef, Int32 ByRef, Char)+0xffffffff`a9e199eb (00007fff`4802cc9b)00007fff`4802cc85 83f86e cmpeax,6Eh00007fff`4802cc88 7407je System_Private_CoreLib!System.Buffers.Text.Utf8Parser.TryParse(System.ReadOnlySpan`1<Byte>, Int32 ByRef, Int32 ByRef, Char)+0xffffffff`a9e199e1 (00007fff`4802cc91)00007fff`4802cc8a 83f878 cmpeax,78h00007fff`4802cc8d 7407je System_Private_CoreLib!System.Buffers.Text.Utf8Parser.TryParse(System.ReadOnlySpan`1<Byte>, Int32 ByRef, Int32 ByRef, Char)+0xffffffff`a9e199e6 (00007fff`4802cc96)00007fff`4802cc8f eb0a jmp System_Private_CoreLib!System.Buffers.Text.Utf8Parser.TryParse(System.ReadOnlySpan`1<Byte>, Int32 ByRef, Int32 ByRef, Char)+0xffffffff`a9e199eb (00007fff`4802cc9b)00007fff`4802cc91 e972c9ffff jmp CLRStub[MethodDescPrestub]@7fff48029608 (00007fff`48029608)00007fff`4802cc96 e9edc9ffff jmp CLRStub[MethodDescPrestub]@7fff48029688 (00007fff`48029688)00007fff`4802cc9b e9a0fdffff jmp CLRStub[MethodDescPrestub]@7fff4802ca40 (00007fff`4802ca40)

TryParse(..., out bool, ...)

Minor improvement here to avoid the range check when dereferencing source[4]. There's also less register shuffling in the error path. Avoiding this shuffling doesn't impact the success case, but I saw it as low-hanging fruit since it reduces the overall method codegen size by a little bit.

TryParse(..., out Guid, ...)

Similar to the integral types, the switch statement has been restructured to have the common case go through a fast path. Additionally, by changing the signature of the TryParseGuidCore method, we can avoid the stack spillage that would normally result on Win64 from passing so many parameters to the workhorse routine.

MethodToolchainMeanErrorStdDevMedianRatioRatioSD
ParseGuid_Defaultmaster5,881.1 ns58.94 ns55.14 ns5,876.8 ns1.000.00
ParseGuid_Defaulttryparse5,545.2 ns88.36 ns82.65 ns5,517.9 ns0.940.02
ParseGuid_Bracesmaster6,154.5 ns119.47 ns132.79 ns6,137.5 ns1.000.00
ParseGuid_Bracestryparse5,821.1 ns67.85 ns63.47 ns5,803.3 ns0.950.02
;;; OLD CODEGEN ;;;00007fff`3ccccdd0 56pushrsi00007fff`3ccccdd1 4883ec40 subrsp,40h00007fff`3ccccdd5 c5f877 vzeroupper00007fff`3ccccdd8 33c0 xoreax,eax00007fff`3ccccdda 4889442430mov qword ptr [rsp+30h],rax00007fff`3ccccddf 410fb7f1 movzxesi,r9w00007fff`3ccccde3 83fe42 cmpesi,42h00007fff`3ccccde6 7745ja System_Private_CoreLib!System.Buffers.Text.Utf8Parser.TryParse(System.ReadOnlySpan`1<Byte>, System.Guid ByRef, Int32 ByRef, Char)+0xffffffff`a0c9a5ad (00007fff`3cccce2d)00007fff`3ccccde8 85f6 testesi,esi00007fff`3ccccdea 0f8481000000 je System_Private_CoreLib!System.Buffers.Text.Utf8Parser.TryParse(System.ReadOnlySpan`1<Byte>, System.Guid ByRef, Int32 ByRef, Char)+0xffffffff`a0c9a5f1 (00007fff`3cccce71)00007fff`3ccccdf0 83fe42 cmpesi,42h00007fff`3ccccdf3 0f85c3000000 jne System_Private_CoreLib!System.Buffers.Text.Utf8Parser.TryParse(System.ReadOnlySpan`1<Byte>, System.Guid ByRef, Int32 ByRef, Char)+0xffffffff`a0c9a63c (00007fff`3ccccebc)00007fff`3ccccdf9 c5fa6f01 vmovdqu xmm0, xmmword ptr [rcx]00007fff`3ccccdfd c5fa7f442430 vmovdqu xmmword ptr [rsp+30h],xmm000007fff`3cccce03 4889542420mov qword ptr [rsp+20h],rdx00007fff`3cccce08 4c89442428 mov qword ptr [rsp+28h],r800007fff`3cccce0d 488d4c2430 learcx,[rsp+30h]00007fff`3cccce12 ba01000000 movedx,100007fff`3cccce17 41b87b000000 movr8d,7Bh00007fff`3cccce1d 41b97d000000 movr9d,7Dh00007fff`3cccce23 e888c7ffff call CLRStub[MethodDescPrestub]@7fff3ccc95b0 (00007fff`3ccc95b0)00007fff`3cccce28 e986000000 jmp System_Private_CoreLib!System.Buffers.Text.Utf8Parser.TryParse(System.ReadOnlySpan`1<Byte>, System.Guid ByRef, Int32 ByRef, Char)+0xffffffff`a0c9a633 (00007fff`3cccceb3)00007fff`3cccce2d 83fe44 cmpesi,44h00007fff`3cccce30 743f je System_Private_CoreLib!System.Buffers.Text.Utf8Parser.TryParse(System.ReadOnlySpan`1<Byte>, System.Guid ByRef, Int32 ByRef, Char)+0xffffffff`a0c9a5f1 (00007fff`3cccce71)00007fff`3cccce32 83fe4e cmpesi,4Eh00007fff`3cccce35 7468je System_Private_CoreLib!System.Buffers.Text.Utf8Parser.TryParse(System.ReadOnlySpan`1<Byte>, System.Guid ByRef, Int32 ByRef, Char)+0xffffffff`a0c9a61f (00007fff`3cccce9f)00007fff`3cccce37 83fe50 cmpesi,50h00007fff`3cccce3a 0f857c000000 jne System_Private_CoreLib!System.Buffers.Text.Utf8Parser.TryParse(System.ReadOnlySpan`1<Byte>, System.Guid ByRef, Int32 ByRef, Char)+0xffffffff`a0c9a63c (00007fff`3ccccebc)00007fff`3cccce40 c5fa6f01 vmovdqu xmm0, xmmword ptr [rcx]00007fff`3cccce44 c5fa7f442430 vmovdqu xmmword ptr [rsp+30h],xmm000007fff`3cccce4a 4889542420mov qword ptr [rsp+20h],rdx00007fff`3cccce4f 4c89442428 mov qword ptr [rsp+28h],r800007fff`3cccce54 488d4c2430 learcx,[rsp+30h]00007fff`3cccce59 ba01000000 movedx,100007fff`3cccce5e 41b828000000 movr8d,28h00007fff`3cccce64 41b929000000 movr9d,29h00007fff`3cccce6a e841c7ffff call CLRStub[MethodDescPrestub]@7fff3ccc95b0 (00007fff`3ccc95b0)00007fff`3cccce6f eb42 jmp System_Private_CoreLib!System.Buffers.Text.Utf8Parser.TryParse(System.ReadOnlySpan`1<Byte>, System.Guid ByRef, Int32 ByRef, Char)+0xffffffff`a0c9a633 (00007fff`3cccceb3)00007fff`3cccce71 c5fa6f01 vmovdqu xmm0, xmmword ptr [rcx]00007fff`3cccce75 c5fa7f442430 vmovdqu xmmword ptr [rsp+30h],xmm000007fff`3cccce7b 4889542420mov qword ptr [rsp+20h],rdx00007fff`3cccce80 4c89442428 mov qword ptr [rsp+28h],r800007fff`3cccce85 488d4c2430 learcx,[rsp+30h]00007fff`3cccce8a 33d2 xoredx,edx00007fff`3cccce8c 41b820000000 movr8d,20h00007fff`3cccce92 41b920000000 movr9d,20h00007fff`3cccce98 e813c7ffff call CLRStub[MethodDescPrestub]@7fff3ccc95b0 (00007fff`3ccc95b0)00007fff`3cccce9d eb14 jmp System_Private_CoreLib!System.Buffers.Text.Utf8Parser.TryParse(System.ReadOnlySpan`1<Byte>, System.Guid ByRef, Int32 ByRef, Char)+0xffffffff`a0c9a633 (00007fff`3cccceb3)00007fff`3cccce9f c5fa6f01 vmovdqu xmm0, xmmword ptr [rcx]00007fff`3ccccea3 c5fa7f442430 vmovdqu xmmword ptr [rsp+30h],xmm000007fff`3ccccea9 488d4c2430 learcx,[rsp+30h]00007fff`3cccceae e8f5c6ffff call CLRStub[MethodDescPrestub]@7fff3ccc95a8 (00007fff`3ccc95a8)00007fff`3cccceb3 0fb6c0 movzxeax,al00007fff`3cccceb6 4883c440 addrsp,40h00007fff`3cccceba 5e poprsi00007fff`3ccccebb c3 ret00007fff`3ccccebc c5f857c0 vxorps xmm0,xmm0,xmm000007fff`3ccccec0 c5fa7f02 vmovdqu xmmword ptr [rdx],xmm000007fff`3ccccec4 33c0 xoreax,eax00007fff`3ccccec6 418900mov dword ptr [r8],eax00007fff`3ccccec9 e8c2d5d8ff call CLRStub[MethodDescPrestub]@7fff3ca5a490 (00007fff`3ca5a490)00007fff`3ccccece cc int3;;; NEW CODEGEN ;;;00007fff`3cccccc0 410fb7c1 movzxeax,r9w00007fff`3cccccc4 85c0 testeax,eax00007fff`3cccccc6 7508jne System_Private_CoreLib!System.Buffers.Text.Utf8Parser.TryParse(System.ReadOnlySpan`1<Byte>, System.Guid ByRef, Int32 ByRef, Char)+0xffffffff`a0c9a370 (00007fff`3cccccd0)00007fff`3cccccc8 4533c9 xorr9d,r9d00007fff`3ccccccb e9a8d1ffff jmp CLRStub[MethodDescPrestub]@7fff3ccc9e78 (00007fff`3ccc9e78)00007fff`3cccccd0 450fb7c9 movzxr9d,r9w00007fff`3cccccd4 4183f944 cmpr9d,44h00007fff`3cccccd8 770e ja System_Private_CoreLib!System.Buffers.Text.Utf8Parser.TryParse(System.ReadOnlySpan`1<Byte>, System.Guid ByRef, Int32 ByRef, Char)+0xffffffff`a0c9a388 (00007fff`3ccccce8)00007fff`3cccccda 4183f942 cmpr9d,42h00007fff`3cccccde 7416je System_Private_CoreLib!System.Buffers.Text.Utf8Parser.TryParse(System.ReadOnlySpan`1<Byte>, System.Guid ByRef, Int32 ByRef, Char)+0xffffffff`a0c9a396 (00007fff`3cccccf6)00007fff`3ccccce0 4183f944 cmpr9d,44h00007fff`3ccccce4 74e2 je System_Private_CoreLib!System.Buffers.Text.Utf8Parser.TryParse(System.ReadOnlySpan`1<Byte>, System.Guid ByRef, Int32 ByRef, Char)+0xffffffff`a0c9a368 (00007fff`3cccccc8)00007fff`3ccccce6 eb29 jmp System_Private_CoreLib!System.Buffers.Text.Utf8Parser.TryParse(System.ReadOnlySpan`1<Byte>, System.Guid ByRef, Int32 ByRef, Char)+0xffffffff`a0c9a3b1 (00007fff`3ccccd11)00007fff`3ccccce8 4183f94e cmpr9d,4Eh00007fff`3cccccec 741e je System_Private_CoreLib!System.Buffers.Text.Utf8Parser.TryParse(System.ReadOnlySpan`1<Byte>, System.Guid ByRef, Int32 ByRef, Char)+0xffffffff`a0c9a3ac (00007fff`3ccccd0c)00007fff`3cccccee 4183f950 cmpr9d,50h00007fff`3cccccf2 740d je System_Private_CoreLib!System.Buffers.Text.Utf8Parser.TryParse(System.ReadOnlySpan`1<Byte>, System.Guid ByRef, Int32 ByRef, Char)+0xffffffff`a0c9a3a1 (00007fff`3ccccd01)00007fff`3cccccf4 eb1b jmp System_Private_CoreLib!System.Buffers.Text.Utf8Parser.TryParse(System.ReadOnlySpan`1<Byte>, System.Guid ByRef, Int32 ByRef, Char)+0xffffffff`a0c9a3b1 (00007fff`3ccccd11)00007fff`3cccccf6 41b97b7d0000 movr9d,7D7Bh00007fff`3cccccfc e977d1ffff jmp CLRStub[MethodDescPrestub]@7fff3ccc9e78 (00007fff`3ccc9e78)00007fff`3ccccd01 41b928290000 movr9d,2928h00007fff`3ccccd07 e96cd1ffff jmp CLRStub[MethodDescPrestub]@7fff3ccc9e78 (00007fff`3ccc9e78)00007fff`3ccccd0c e95fd1ffff jmp CLRStub[MethodDescPrestub]@7fff3ccc9e70 (00007fff`3ccc9e70)00007fff`3ccccd11 e91afdffff jmp CLRStub[MethodDescPrestub]@7fff3cccca30 (00007fff`3cccca30)

if (ends != default)
{
if (source[0] != begin)
if (source[0] != (byte)ends)

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.

The JIT could produce better codegen for this pattern. See #33504.

@GrabYourPitchforksGrabYourPitchforks added the tenet-performance Performance related issue label Mar 12, 2020
@stephentoub

Copy link
Copy Markdown
Member

How does this relate to #32843?

Can similar tweaks be made to the corresponding char-based APIs (e.g. int.Parse)?

@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.

another really nice improvement @GrabYourPitchforks !

LGTM, thanks for adding the comment that explains the bit shifting logic

@GrabYourPitchforks

Copy link
Copy Markdown
MemberAuthor

@stephentoub That PR was primarily concerned with improving the workhorse routine. From that investigation came the idea of improving the entry point. This PR focuses just on the fixed overhead of the entry points while leaving the workhorse routines largely unchanged.

I haven't tried applying these optimizations to the char overloads yet because their entry points generally look different than these entry points. But I imagine once we get back to the PR that optimizes the workhorse routines the optimizations there will be generally applicable to both char and byte.

@tannergoodingtannergooding 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.

Changes LGTM. Had a couple questions about the codegen

@GrabYourPitchforks

Copy link
Copy Markdown
MemberAuthor

CI failures seem to be unrelated infrastructure failures.

@GrabYourPitchforks
GrabYourPitchforks merged commit 099bc4b into dotnet:masterMar 13, 2020
@GrabYourPitchforks
GrabYourPitchforks deleted the tryparse_upd branch March 13, 2020 00:57
@ghostghost locked as resolved and limited conversation to collaborators Dec 10, 2020
Sign up for freeto subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

area-System.Memorytenet-performancePerformance related issue

Projects

None yet

Development

Successfully merging this pull request may close these issues.

7 participants

@GrabYourPitchforks@stephentoub@lpereira@adamsitnik@tannergooding@davidwrighton@Dotnet-GitSync-Bot
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Universal Dark Mode - works on any site\n(function() {\n var enabled = true;\n \n function applyDarkMode() {\n if (!enabled) return;\n \n // Create style element if it doesn't exist\n var style = document.getElementById('universal-dark-mode-style');\n if (!style) {\n style = document.createElement('style');\n style.id = 'universal-dark-mode-style';\n document.head.appendChild(style);\n }\n \n // Dark mode CSS - inverts colors but preserves images/video\n style.textContent = '\n /* Invert everything except media */\n html {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #1a1a2e !important;\n }\n \n /* Restore images, videos, iframes, canvas */\n img, video, iframe, canvas, svg, picture, [style*=\"background-image\"] {\n filter: invert(1) hue-rotate(180deg) !important;\n }\n \n /* Preserve specific elements that should not be inverted */\n .no-dark-mode, .no-dark-mode *,\n [data-theme=\"light\"], [data-theme=\"light\"],\n .ace_editor, .ace_editor *,\n .CodeMirror, .CodeMirror *,\n .monaco-editor, .monaco-editor *,\n .markdown-body pre, .markdown-body pre *,\n .highlight, .highlight *,\n pre code, pre code * {\n filter: none !important;\n }\n \n /* Fix common UI elements */\n .modal, .popup, .dropdown-menu, .tooltip, .popover {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #2d2d44 !important;\n border-color: #444 !important;\n }\n \n /* Scrollbars */\n ::-webkit-scrollbar { background: #1a1a2e !important; }\n ::-webkit-scrollbar-thumb { background: #444 !important; }\n ::-webkit-scrollbar-thumb:hover { background: #555 !important; }\n \n /* Selection */\n ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ';\n }\n \n function removeDarkMode() {\n var style = document.getElementById('universal-dark-mode-style');\n if (style) style.remove();\n }\n \n // Toggle with Alt+Shift+D\n document.addEventListener('keydown', function(e) {\n if (e.altKey && e.shiftKey && e.key === 'D') {\n e.preventDefault();\n enabled = !enabled;\n if (enabled) {\n applyDarkMode();\n console.log('[Universal Dark Mode] Enabled');\n } else {\n removeDarkMode();\n console.log('[Universal Dark Mode] Disabled');\n }\n }\n });\n \n // Apply on load\n applyDarkMode();\n \n // Re-apply on dynamic content\n var observer = new MutationObserver(function(mutations) {\n if (enabled && !document.getElementById('universal-dark-mode-style')) {\n applyDarkMode();\n }\n });\n observer.observe(document.head, { childList: true });\n \n console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle');\n})();", "Universal Dark Mode"); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })();
Skip to content

Reduce fixed overhead of some Utf8Parser.TryParse methods - #33507

Merged
GrabYourPitchforks merged 3 commits into
dotnet:masterfrom
GrabYourPitchforks:tryparse_upd
Mar 13, 2020
Merged

Reduce fixed overhead of some Utf8Parser.TryParse methods#33507
GrabYourPitchforks merged 3 commits into
dotnet:masterfrom
GrabYourPitchforks:tryparse_upd

Conversation

@GrabYourPitchforks

Copy link
Copy Markdown
Member

This builds atop the work that Andy did as part of #33004. There are no behavioral changes here - only slight refactorings that produce better codegen at the entry points to some of the Utf8Parser.TryParse methods. In particular, this does not include many of the optimizations discussed at #32843.

Integral types

For the TryParse methods that work with integral types, reflowing the logic in this fashion moves the "default" behavior into a fast-path and reduces the total amount of logic in the switch statement. We're now able to tail-call into the workhorse methods without performing any register shuffling or stack spilling

MethodToolchainMeanErrorStdDevRatio
TryParseInt32master976.3 ns5.34 ns4.73 ns1.00
TryParseInt32tryparse913.9 ns8.03 ns6.71 ns0.94
;;; OLD CODEGEN ;;;00007fff`4803cb40 56pushrsi00007fff`4803cb41 4883ec20 subrsp,20h00007fff`4803cb45 410fb7f1 movzxesi,r9w00007fff`4803cb49 83fe4e cmpesi,4Eh00007fff`4803cb4c 772c ja System_Private_CoreLib!System.Buffers.Text.Utf8Parser.TryParse(System.ReadOnlySpan`1<Byte>, Int32 ByRef, Int32 ByRef, Char)+0xffffffff`a9e299ba (00007fff`4803cb7a)00007fff`4803cb4e 83fe44 cmpesi,44h00007fff`4803cb51 7713ja System_Private_CoreLib!System.Buffers.Text.Utf8Parser.TryParse(System.ReadOnlySpan`1<Byte>, Int32 ByRef, Int32 ByRef, Char)+0xffffffff`a9e299a6 (00007fff`4803cb66)00007fff`4803cb53 85f6 testesi,esi00007fff`4803cb55 7405je System_Private_CoreLib!System.Buffers.Text.Utf8Parser.TryParse(System.ReadOnlySpan`1<Byte>, Int32 ByRef, Int32 ByRef, Char)+0xffffffff`a9e2999c (00007fff`4803cb5c)00007fff`4803cb57 83fe44 cmpesi,44h00007fff`4803cb5a 754c jne System_Private_CoreLib!System.Buffers.Text.Utf8Parser.TryParse(System.ReadOnlySpan`1<Byte>, Int32 ByRef, Int32 ByRef, Char)+0xffffffff`a9e299e8 (00007fff`4803cba8)00007fff`4803cb5c 4883c420 addrsp,20h00007fff`4803cb60 5e poprsi00007fff`4803cb61 e97acaffff jmp CLRStub[MethodDescPrestub]@7fff480395e0 (00007fff`480395e0)00007fff`4803cb66 83fe47 cmpesi,47h00007fff`4803cb69 74f1 je System_Private_CoreLib!System.Buffers.Text.Utf8Parser.TryParse(System.ReadOnlySpan`1<Byte>, Int32 ByRef, Int32 ByRef, Char)+0xffffffff`a9e2999c (00007fff`4803cb5c)00007fff`4803cb6b 83fe4e cmpesi,4Eh00007fff`4803cb6e 7538jne System_Private_CoreLib!System.Buffers.Text.Utf8Parser.TryParse(System.ReadOnlySpan`1<Byte>, Int32 ByRef, Int32 ByRef, Char)+0xffffffff`a9e299e8 (00007fff`4803cba8)00007fff`4803cb70 4883c420 addrsp,20h00007fff`4803cb74 5e poprsi00007fff`4803cb75 e986caffff jmp CLRStub[MethodDescPrestub]@7fff48039600 (00007fff`48039600)00007fff`4803cb7a 83fe64 cmpesi,64h00007fff`4803cb7d 770c ja System_Private_CoreLib!System.Buffers.Text.Utf8Parser.TryParse(System.ReadOnlySpan`1<Byte>, Int32 ByRef, Int32 ByRef, Char)+0xffffffff`a9e299cb (00007fff`4803cb8b)00007fff`4803cb7f 83fe58 cmpesi,58h00007fff`4803cb82 7416je System_Private_CoreLib!System.Buffers.Text.Utf8Parser.TryParse(System.ReadOnlySpan`1<Byte>, Int32 ByRef, Int32 ByRef, Char)+0xffffffff`a9e299da (00007fff`4803cb9a)00007fff`4803cb84 83fe64 cmpesi,64h00007fff`4803cb87 751f jne System_Private_CoreLib!System.Buffers.Text.Utf8Parser.TryParse(System.ReadOnlySpan`1<Byte>, Int32 ByRef, Int32 ByRef, Char)+0xffffffff`a9e299e8 (00007fff`4803cba8)00007fff`4803cb89 ebd1 jmp System_Private_CoreLib!System.Buffers.Text.Utf8Parser.TryParse(System.ReadOnlySpan`1<Byte>, Int32 ByRef, Int32 ByRef, Char)+0xffffffff`a9e2999c (00007fff`4803cb5c)00007fff`4803cb8b 83fe67 cmpesi,67h00007fff`4803cb8e 74cc je System_Private_CoreLib!System.Buffers.Text.Utf8Parser.TryParse(System.ReadOnlySpan`1<Byte>, Int32 ByRef, Int32 ByRef, Char)+0xffffffff`a9e2999c (00007fff`4803cb5c)00007fff`4803cb90 83fe6e cmpesi,6Eh00007fff`4803cb93 74db je System_Private_CoreLib!System.Buffers.Text.Utf8Parser.TryParse(System.ReadOnlySpan`1<Byte>, Int32 ByRef, Int32 ByRef, Char)+0xffffffff`a9e299b0 (00007fff`4803cb70)00007fff`4803cb95 83fe78 cmpesi,78h00007fff`4803cb98 750e jne System_Private_CoreLib!System.Buffers.Text.Utf8Parser.TryParse(System.ReadOnlySpan`1<Byte>, Int32 ByRef, Int32 ByRef, Char)+0xffffffff`a9e299e8 (00007fff`4803cba8)00007fff`4803cb9a 33c0 xoreax,eax00007fff`4803cb9c 8902mov dword ptr [rdx],eax00007fff`4803cb9e 4883c420 addrsp,20h00007fff`4803cba2 5e poprsi00007fff`4803cba3 e9d8caffff jmp CLRStub[MethodDescPrestub]@7fff48039680 (00007fff`48039680)00007fff`4803cba8 33c0 xoreax,eax00007fff`4803cbaa 8902mov dword ptr [rdx],eax00007fff`4803cbac 418900mov dword ptr [r8],eax00007fff`4803cbaf e8dcd8d8ff call CLRStub[MethodDescPrestub]@7fff47dca490 (00007fff`47dca490)00007fff`4803cbb4 cc int3;;; NEW CODEGEN ;;;00007fff`4802cc60 410fb7c1 movzxeax,r9w00007fff`4802cc64 85c0 testeax,eax00007fff`4802cc66 7505jne System_Private_CoreLib!System.Buffers.Text.Utf8Parser.TryParse(System.ReadOnlySpan`1<Byte>, Int32 ByRef, Int32 ByRef, Char)+0xffffffff`a9e199bd (00007fff`4802cc6d)00007fff`4802cc68 e97bc9ffff jmp CLRStub[MethodDescPrestub]@7fff480295e8 (00007fff`480295e8)00007fff`4802cc6d 410fb7c1 movzxeax,r9w00007fff`4802cc71 83c820 oreax,20h00007fff`4802cc74 83f867 cmpeax,67h00007fff`4802cc77 7f0c jg System_Private_CoreLib!System.Buffers.Text.Utf8Parser.TryParse(System.ReadOnlySpan`1<Byte>, Int32 ByRef, Int32 ByRef, Char)+0xffffffff`a9e199d5 (00007fff`4802cc85)00007fff`4802cc79 83f864 cmpeax,64h00007fff`4802cc7c 74ea je System_Private_CoreLib!System.Buffers.Text.Utf8Parser.TryParse(System.ReadOnlySpan`1<Byte>, Int32 ByRef, Int32 ByRef, Char)+0xffffffff`a9e199b8 (00007fff`4802cc68)00007fff`4802cc7e 83f867 cmpeax,67h00007fff`4802cc81 74e5 je System_Private_CoreLib!System.Buffers.Text.Utf8Parser.TryParse(System.ReadOnlySpan`1<Byte>, Int32 ByRef, Int32 ByRef, Char)+0xffffffff`a9e199b8 (00007fff`4802cc68)00007fff`4802cc83 eb16 jmp System_Private_CoreLib!System.Buffers.Text.Utf8Parser.TryParse(System.ReadOnlySpan`1<Byte>, Int32 ByRef, Int32 ByRef, Char)+0xffffffff`a9e199eb (00007fff`4802cc9b)00007fff`4802cc85 83f86e cmpeax,6Eh00007fff`4802cc88 7407je System_Private_CoreLib!System.Buffers.Text.Utf8Parser.TryParse(System.ReadOnlySpan`1<Byte>, Int32 ByRef, Int32 ByRef, Char)+0xffffffff`a9e199e1 (00007fff`4802cc91)00007fff`4802cc8a 83f878 cmpeax,78h00007fff`4802cc8d 7407je System_Private_CoreLib!System.Buffers.Text.Utf8Parser.TryParse(System.ReadOnlySpan`1<Byte>, Int32 ByRef, Int32 ByRef, Char)+0xffffffff`a9e199e6 (00007fff`4802cc96)00007fff`4802cc8f eb0a jmp System_Private_CoreLib!System.Buffers.Text.Utf8Parser.TryParse(System.ReadOnlySpan`1<Byte>, Int32 ByRef, Int32 ByRef, Char)+0xffffffff`a9e199eb (00007fff`4802cc9b)00007fff`4802cc91 e972c9ffff jmp CLRStub[MethodDescPrestub]@7fff48029608 (00007fff`48029608)00007fff`4802cc96 e9edc9ffff jmp CLRStub[MethodDescPrestub]@7fff48029688 (00007fff`48029688)00007fff`4802cc9b e9a0fdffff jmp CLRStub[MethodDescPrestub]@7fff4802ca40 (00007fff`4802ca40)

TryParse(..., out bool, ...)

Minor improvement here to avoid the range check when dereferencing source[4]. There's also less register shuffling in the error path. Avoiding this shuffling doesn't impact the success case, but I saw it as low-hanging fruit since it reduces the overall method codegen size by a little bit.

TryParse(..., out Guid, ...)

Similar to the integral types, the switch statement has been restructured to have the common case go through a fast path. Additionally, by changing the signature of the TryParseGuidCore method, we can avoid the stack spillage that would normally result on Win64 from passing so many parameters to the workhorse routine.

MethodToolchainMeanErrorStdDevMedianRatioRatioSD
ParseGuid_Defaultmaster5,881.1 ns58.94 ns55.14 ns5,876.8 ns1.000.00
ParseGuid_Defaulttryparse5,545.2 ns88.36 ns82.65 ns5,517.9 ns0.940.02
ParseGuid_Bracesmaster6,154.5 ns119.47 ns132.79 ns6,137.5 ns1.000.00
ParseGuid_Bracestryparse5,821.1 ns67.85 ns63.47 ns5,803.3 ns0.950.02
;;; OLD CODEGEN ;;;00007fff`3ccccdd0 56pushrsi00007fff`3ccccdd1 4883ec40 subrsp,40h00007fff`3ccccdd5 c5f877 vzeroupper00007fff`3ccccdd8 33c0 xoreax,eax00007fff`3ccccdda 4889442430mov qword ptr [rsp+30h],rax00007fff`3ccccddf 410fb7f1 movzxesi,r9w00007fff`3ccccde3 83fe42 cmpesi,42h00007fff`3ccccde6 7745ja System_Private_CoreLib!System.Buffers.Text.Utf8Parser.TryParse(System.ReadOnlySpan`1<Byte>, System.Guid ByRef, Int32 ByRef, Char)+0xffffffff`a0c9a5ad (00007fff`3cccce2d)00007fff`3ccccde8 85f6 testesi,esi00007fff`3ccccdea 0f8481000000 je System_Private_CoreLib!System.Buffers.Text.Utf8Parser.TryParse(System.ReadOnlySpan`1<Byte>, System.Guid ByRef, Int32 ByRef, Char)+0xffffffff`a0c9a5f1 (00007fff`3cccce71)00007fff`3ccccdf0 83fe42 cmpesi,42h00007fff`3ccccdf3 0f85c3000000 jne System_Private_CoreLib!System.Buffers.Text.Utf8Parser.TryParse(System.ReadOnlySpan`1<Byte>, System.Guid ByRef, Int32 ByRef, Char)+0xffffffff`a0c9a63c (00007fff`3ccccebc)00007fff`3ccccdf9 c5fa6f01 vmovdqu xmm0, xmmword ptr [rcx]00007fff`3ccccdfd c5fa7f442430 vmovdqu xmmword ptr [rsp+30h],xmm000007fff`3cccce03 4889542420mov qword ptr [rsp+20h],rdx00007fff`3cccce08 4c89442428 mov qword ptr [rsp+28h],r800007fff`3cccce0d 488d4c2430 learcx,[rsp+30h]00007fff`3cccce12 ba01000000 movedx,100007fff`3cccce17 41b87b000000 movr8d,7Bh00007fff`3cccce1d 41b97d000000 movr9d,7Dh00007fff`3cccce23 e888c7ffff call CLRStub[MethodDescPrestub]@7fff3ccc95b0 (00007fff`3ccc95b0)00007fff`3cccce28 e986000000 jmp System_Private_CoreLib!System.Buffers.Text.Utf8Parser.TryParse(System.ReadOnlySpan`1<Byte>, System.Guid ByRef, Int32 ByRef, Char)+0xffffffff`a0c9a633 (00007fff`3cccceb3)00007fff`3cccce2d 83fe44 cmpesi,44h00007fff`3cccce30 743f je System_Private_CoreLib!System.Buffers.Text.Utf8Parser.TryParse(System.ReadOnlySpan`1<Byte>, System.Guid ByRef, Int32 ByRef, Char)+0xffffffff`a0c9a5f1 (00007fff`3cccce71)00007fff`3cccce32 83fe4e cmpesi,4Eh00007fff`3cccce35 7468je System_Private_CoreLib!System.Buffers.Text.Utf8Parser.TryParse(System.ReadOnlySpan`1<Byte>, System.Guid ByRef, Int32 ByRef, Char)+0xffffffff`a0c9a61f (00007fff`3cccce9f)00007fff`3cccce37 83fe50 cmpesi,50h00007fff`3cccce3a 0f857c000000 jne System_Private_CoreLib!System.Buffers.Text.Utf8Parser.TryParse(System.ReadOnlySpan`1<Byte>, System.Guid ByRef, Int32 ByRef, Char)+0xffffffff`a0c9a63c (00007fff`3ccccebc)00007fff`3cccce40 c5fa6f01 vmovdqu xmm0, xmmword ptr [rcx]00007fff`3cccce44 c5fa7f442430 vmovdqu xmmword ptr [rsp+30h],xmm000007fff`3cccce4a 4889542420mov qword ptr [rsp+20h],rdx00007fff`3cccce4f 4c89442428 mov qword ptr [rsp+28h],r800007fff`3cccce54 488d4c2430 learcx,[rsp+30h]00007fff`3cccce59 ba01000000 movedx,100007fff`3cccce5e 41b828000000 movr8d,28h00007fff`3cccce64 41b929000000 movr9d,29h00007fff`3cccce6a e841c7ffff call CLRStub[MethodDescPrestub]@7fff3ccc95b0 (00007fff`3ccc95b0)00007fff`3cccce6f eb42 jmp System_Private_CoreLib!System.Buffers.Text.Utf8Parser.TryParse(System.ReadOnlySpan`1<Byte>, System.Guid ByRef, Int32 ByRef, Char)+0xffffffff`a0c9a633 (00007fff`3cccceb3)00007fff`3cccce71 c5fa6f01 vmovdqu xmm0, xmmword ptr [rcx]00007fff`3cccce75 c5fa7f442430 vmovdqu xmmword ptr [rsp+30h],xmm000007fff`3cccce7b 4889542420mov qword ptr [rsp+20h],rdx00007fff`3cccce80 4c89442428 mov qword ptr [rsp+28h],r800007fff`3cccce85 488d4c2430 learcx,[rsp+30h]00007fff`3cccce8a 33d2 xoredx,edx00007fff`3cccce8c 41b820000000 movr8d,20h00007fff`3cccce92 41b920000000 movr9d,20h00007fff`3cccce98 e813c7ffff call CLRStub[MethodDescPrestub]@7fff3ccc95b0 (00007fff`3ccc95b0)00007fff`3cccce9d eb14 jmp System_Private_CoreLib!System.Buffers.Text.Utf8Parser.TryParse(System.ReadOnlySpan`1<Byte>, System.Guid ByRef, Int32 ByRef, Char)+0xffffffff`a0c9a633 (00007fff`3cccceb3)00007fff`3cccce9f c5fa6f01 vmovdqu xmm0, xmmword ptr [rcx]00007fff`3ccccea3 c5fa7f442430 vmovdqu xmmword ptr [rsp+30h],xmm000007fff`3ccccea9 488d4c2430 learcx,[rsp+30h]00007fff`3cccceae e8f5c6ffff call CLRStub[MethodDescPrestub]@7fff3ccc95a8 (00007fff`3ccc95a8)00007fff`3cccceb3 0fb6c0 movzxeax,al00007fff`3cccceb6 4883c440 addrsp,40h00007fff`3cccceba 5e poprsi00007fff`3ccccebb c3 ret00007fff`3ccccebc c5f857c0 vxorps xmm0,xmm0,xmm000007fff`3ccccec0 c5fa7f02 vmovdqu xmmword ptr [rdx],xmm000007fff`3ccccec4 33c0 xoreax,eax00007fff`3ccccec6 418900mov dword ptr [r8],eax00007fff`3ccccec9 e8c2d5d8ff call CLRStub[MethodDescPrestub]@7fff3ca5a490 (00007fff`3ca5a490)00007fff`3ccccece cc int3;;; NEW CODEGEN ;;;00007fff`3cccccc0 410fb7c1 movzxeax,r9w00007fff`3cccccc4 85c0 testeax,eax00007fff`3cccccc6 7508jne System_Private_CoreLib!System.Buffers.Text.Utf8Parser.TryParse(System.ReadOnlySpan`1<Byte>, System.Guid ByRef, Int32 ByRef, Char)+0xffffffff`a0c9a370 (00007fff`3cccccd0)00007fff`3cccccc8 4533c9 xorr9d,r9d00007fff`3ccccccb e9a8d1ffff jmp CLRStub[MethodDescPrestub]@7fff3ccc9e78 (00007fff`3ccc9e78)00007fff`3cccccd0 450fb7c9 movzxr9d,r9w00007fff`3cccccd4 4183f944 cmpr9d,44h00007fff`3cccccd8 770e ja System_Private_CoreLib!System.Buffers.Text.Utf8Parser.TryParse(System.ReadOnlySpan`1<Byte>, System.Guid ByRef, Int32 ByRef, Char)+0xffffffff`a0c9a388 (00007fff`3ccccce8)00007fff`3cccccda 4183f942 cmpr9d,42h00007fff`3cccccde 7416je System_Private_CoreLib!System.Buffers.Text.Utf8Parser.TryParse(System.ReadOnlySpan`1<Byte>, System.Guid ByRef, Int32 ByRef, Char)+0xffffffff`a0c9a396 (00007fff`3cccccf6)00007fff`3ccccce0 4183f944 cmpr9d,44h00007fff`3ccccce4 74e2 je System_Private_CoreLib!System.Buffers.Text.Utf8Parser.TryParse(System.ReadOnlySpan`1<Byte>, System.Guid ByRef, Int32 ByRef, Char)+0xffffffff`a0c9a368 (00007fff`3cccccc8)00007fff`3ccccce6 eb29 jmp System_Private_CoreLib!System.Buffers.Text.Utf8Parser.TryParse(System.ReadOnlySpan`1<Byte>, System.Guid ByRef, Int32 ByRef, Char)+0xffffffff`a0c9a3b1 (00007fff`3ccccd11)00007fff`3ccccce8 4183f94e cmpr9d,4Eh00007fff`3cccccec 741e je System_Private_CoreLib!System.Buffers.Text.Utf8Parser.TryParse(System.ReadOnlySpan`1<Byte>, System.Guid ByRef, Int32 ByRef, Char)+0xffffffff`a0c9a3ac (00007fff`3ccccd0c)00007fff`3cccccee 4183f950 cmpr9d,50h00007fff`3cccccf2 740d je System_Private_CoreLib!System.Buffers.Text.Utf8Parser.TryParse(System.ReadOnlySpan`1<Byte>, System.Guid ByRef, Int32 ByRef, Char)+0xffffffff`a0c9a3a1 (00007fff`3ccccd01)00007fff`3cccccf4 eb1b jmp System_Private_CoreLib!System.Buffers.Text.Utf8Parser.TryParse(System.ReadOnlySpan`1<Byte>, System.Guid ByRef, Int32 ByRef, Char)+0xffffffff`a0c9a3b1 (00007fff`3ccccd11)00007fff`3cccccf6 41b97b7d0000 movr9d,7D7Bh00007fff`3cccccfc e977d1ffff jmp CLRStub[MethodDescPrestub]@7fff3ccc9e78 (00007fff`3ccc9e78)00007fff`3ccccd01 41b928290000 movr9d,2928h00007fff`3ccccd07 e96cd1ffff jmp CLRStub[MethodDescPrestub]@7fff3ccc9e78 (00007fff`3ccc9e78)00007fff`3ccccd0c e95fd1ffff jmp CLRStub[MethodDescPrestub]@7fff3ccc9e70 (00007fff`3ccc9e70)00007fff`3ccccd11 e91afdffff jmp CLRStub[MethodDescPrestub]@7fff3cccca30 (00007fff`3cccca30)

if (ends != default)
{
if (source[0] != begin)
if (source[0] != (byte)ends)

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.

The JIT could produce better codegen for this pattern. See #33504.

@GrabYourPitchforksGrabYourPitchforks added the tenet-performance Performance related issue label Mar 12, 2020
@stephentoub

Copy link
Copy Markdown
Member

How does this relate to #32843?

Can similar tweaks be made to the corresponding char-based APIs (e.g. int.Parse)?

@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.

another really nice improvement @GrabYourPitchforks !

LGTM, thanks for adding the comment that explains the bit shifting logic

@GrabYourPitchforks

Copy link
Copy Markdown
MemberAuthor

@stephentoub That PR was primarily concerned with improving the workhorse routine. From that investigation came the idea of improving the entry point. This PR focuses just on the fixed overhead of the entry points while leaving the workhorse routines largely unchanged.

I haven't tried applying these optimizations to the char overloads yet because their entry points generally look different than these entry points. But I imagine once we get back to the PR that optimizes the workhorse routines the optimizations there will be generally applicable to both char and byte.

@tannergoodingtannergooding 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.

Changes LGTM. Had a couple questions about the codegen

@GrabYourPitchforks

Copy link
Copy Markdown
MemberAuthor

CI failures seem to be unrelated infrastructure failures.

@GrabYourPitchforks
GrabYourPitchforks merged commit 099bc4b into dotnet:masterMar 13, 2020
@GrabYourPitchforks
GrabYourPitchforks deleted the tryparse_upd branch March 13, 2020 00:57
@ghostghost locked as resolved and limited conversation to collaborators Dec 10, 2020
Sign up for freeto subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

area-System.Memorytenet-performancePerformance related issue

Projects

None yet

Development

Successfully merging this pull request may close these issues.

7 participants

@GrabYourPitchforks@stephentoub@lpereira@adamsitnik@tannergooding@davidwrighton@Dotnet-GitSync-Bot