Reduce fixed overhead of some Utf8Parser.TryParse methods by GrabYourPitchforks · Pull Request #33507 · dotnet/runtime (original) (raw)

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

Method Toolchain Mean Error StdDev Ratio
TryParseInt32 master 976.3 ns 5.34 ns 4.73 ns 1.00
TryParseInt32 tryparse 913.9 ns 8.03 ns 6.71 ns 0.94

;;; OLD CODEGEN ;;; 00007fff4803cb40 56 push rsi 00007fff4803cb41 4883ec20 sub rsp, 20h 00007fff4803cb45 410fb7f1 movzx esi, r9w 00007fff4803cb49 83fe4e cmp esi, 4Eh 00007fff4803cb4c 772c ja System_Private_CoreLib!System.Buffers.Text.Utf8Parser.TryParse(System.ReadOnlySpan1, Int32 ByRef, Int32 ByRef, Char)+0xffffffffa9e299ba (00007fff4803cb7a) 00007fff4803cb4e 83fe44 cmp esi, 44h 00007fff4803cb51 7713 ja System_Private_CoreLib!System.Buffers.Text.Utf8Parser.TryParse(System.ReadOnlySpan1<Byte>, Int32 ByRef, Int32 ByRef, Char)+0xffffffffa9e299a6 (00007fff4803cb66) 00007fff4803cb53 85f6 test esi, esi 00007fff4803cb55 7405 je System_Private_CoreLib!System.Buffers.Text.Utf8Parser.TryParse(System.ReadOnlySpan1, Int32 ByRef, Int32 ByRef, Char)+0xffffffffa9e2999c (00007fff4803cb5c) 00007fff4803cb57 83fe44 cmp esi, 44h 00007fff4803cb5a 754c jne System_Private_CoreLib!System.Buffers.Text.Utf8Parser.TryParse(System.ReadOnlySpan1<Byte>, Int32 ByRef, Int32 ByRef, Char)+0xffffffffa9e299e8 (00007fff4803cba8) 00007fff4803cb5c 4883c420 add rsp, 20h 00007fff4803cb60 5e pop rsi 00007fff4803cb61 e97acaffff jmp CLRStub[MethodDescPrestub]@7fff480395e0 (00007fff480395e0) 00007fff4803cb66 83fe47 cmp esi, 47h 00007fff4803cb69 74f1 je System_Private_CoreLib!System.Buffers.Text.Utf8Parser.TryParse(System.ReadOnlySpan1, Int32 ByRef, Int32 ByRef, Char)+0xffffffffa9e2999c (00007fff4803cb5c) 00007fff4803cb6b 83fe4e cmp esi, 4Eh 00007fff4803cb6e 7538 jne System_Private_CoreLib!System.Buffers.Text.Utf8Parser.TryParse(System.ReadOnlySpan1<Byte>, Int32 ByRef, Int32 ByRef, Char)+0xffffffffa9e299e8 (00007fff4803cba8) 00007fff4803cb70 4883c420 add rsp, 20h 00007fff4803cb74 5e pop rsi 00007fff4803cb75 e986caffff jmp CLRStub[MethodDescPrestub]@7fff48039600 (00007fff48039600) 00007fff4803cb7a 83fe64 cmp esi, 64h 00007fff4803cb7d 770c ja System_Private_CoreLib!System.Buffers.Text.Utf8Parser.TryParse(System.ReadOnlySpan1, Int32 ByRef, Int32 ByRef, Char)+0xffffffffa9e299cb (00007fff4803cb8b) 00007fff4803cb7f 83fe58 cmp esi, 58h 00007fff4803cb82 7416 je System_Private_CoreLib!System.Buffers.Text.Utf8Parser.TryParse(System.ReadOnlySpan1<Byte>, Int32 ByRef, Int32 ByRef, Char)+0xffffffffa9e299da (00007fff4803cb9a) 00007fff4803cb84 83fe64 cmp esi, 64h 00007fff4803cb87 751f jne System_Private_CoreLib!System.Buffers.Text.Utf8Parser.TryParse(System.ReadOnlySpan1, Int32 ByRef, Int32 ByRef, Char)+0xffffffffa9e299e8 (00007fff4803cba8) 00007fff4803cb89 ebd1 jmp System_Private_CoreLib!System.Buffers.Text.Utf8Parser.TryParse(System.ReadOnlySpan1, Int32 ByRef, Int32 ByRef, Char)+0xffffffffa9e2999c (00007fff4803cb5c) 00007fff4803cb8b 83fe67 cmp esi, 67h 00007fff4803cb8e 74cc je System_Private_CoreLib!System.Buffers.Text.Utf8Parser.TryParse(System.ReadOnlySpan1<Byte>, Int32 ByRef, Int32 ByRef, Char)+0xffffffffa9e2999c (00007fff4803cb5c) 00007fff4803cb90 83fe6e cmp esi, 6Eh 00007fff4803cb93 74db je System_Private_CoreLib!System.Buffers.Text.Utf8Parser.TryParse(System.ReadOnlySpan1, Int32 ByRef, Int32 ByRef, Char)+0xffffffffa9e299b0 (00007fff4803cb70) 00007fff4803cb95 83fe78 cmp esi, 78h 00007fff4803cb98 750e jne System_Private_CoreLib!System.Buffers.Text.Utf8Parser.TryParse(System.ReadOnlySpan1<Byte>, Int32 ByRef, Int32 ByRef, Char)+0xffffffffa9e299e8 (00007fff4803cba8) 00007fff4803cb9a 33c0 xor eax, eax 00007fff4803cb9c 8902 mov dword ptr [rdx], eax 00007fff4803cb9e 4883c420 add rsp, 20h 00007fff4803cba2 5e pop rsi 00007fff4803cba3 e9d8caffff jmp CLRStub[MethodDescPrestub]@7fff48039680 (00007fff48039680) 00007fff4803cba8 33c0 xor eax, eax 00007fff4803cbaa 8902 mov dword ptr [rdx], eax 00007fff4803cbac 418900 mov dword ptr [r8], eax 00007fff4803cbaf e8dcd8d8ff call CLRStub[MethodDescPrestub]@7fff47dca490 (00007fff47dca490) 00007fff`4803cbb4 cc int 3

;;; NEW CODEGEN ;;; 00007fff4802cc60 410fb7c1 movzx eax, r9w 00007fff4802cc64 85c0 test eax, eax 00007fff4802cc66 7505 jne System_Private_CoreLib!System.Buffers.Text.Utf8Parser.TryParse(System.ReadOnlySpan1, Int32 ByRef, Int32 ByRef, Char)+0xffffffffa9e199bd (00007fff4802cc6d) 00007fff4802cc68 e97bc9ffff jmp CLRStub[MethodDescPrestub]@7fff480295e8 (00007fff480295e8) 00007fff4802cc6d 410fb7c1 movzx eax, r9w 00007fff4802cc71 83c820 or eax, 20h 00007fff4802cc74 83f867 cmp eax, 67h 00007fff4802cc77 7f0c jg System_Private_CoreLib!System.Buffers.Text.Utf8Parser.TryParse(System.ReadOnlySpan1<Byte>, Int32 ByRef, Int32 ByRef, Char)+0xffffffffa9e199d5 (00007fff4802cc85) 00007fff4802cc79 83f864 cmp eax, 64h 00007fff4802cc7c 74ea je System_Private_CoreLib!System.Buffers.Text.Utf8Parser.TryParse(System.ReadOnlySpan1, Int32 ByRef, Int32 ByRef, Char)+0xffffffffa9e199b8 (00007fff4802cc68) 00007fff4802cc7e 83f867 cmp eax, 67h 00007fff4802cc81 74e5 je System_Private_CoreLib!System.Buffers.Text.Utf8Parser.TryParse(System.ReadOnlySpan1<Byte>, Int32 ByRef, Int32 ByRef, Char)+0xffffffffa9e199b8 (00007fff4802cc68) 00007fff4802cc83 eb16 jmp System_Private_CoreLib!System.Buffers.Text.Utf8Parser.TryParse(System.ReadOnlySpan1<Byte>, Int32 ByRef, Int32 ByRef, Char)+0xffffffffa9e199eb (00007fff4802cc9b) 00007fff4802cc85 83f86e cmp eax, 6Eh 00007fff4802cc88 7407 je System_Private_CoreLib!System.Buffers.Text.Utf8Parser.TryParse(System.ReadOnlySpan1, Int32 ByRef, Int32 ByRef, Char)+0xffffffffa9e199e1 (00007fff4802cc91) 00007fff4802cc8a 83f878 cmp eax, 78h 00007fff4802cc8d 7407 je System_Private_CoreLib!System.Buffers.Text.Utf8Parser.TryParse(System.ReadOnlySpan1<Byte>, Int32 ByRef, Int32 ByRef, Char)+0xffffffffa9e199e6 (00007fff4802cc96) 00007fff4802cc8f eb0a jmp System_Private_CoreLib!System.Buffers.Text.Utf8Parser.TryParse(System.ReadOnlySpan1<Byte>, Int32 ByRef, Int32 ByRef, Char)+0xffffffffa9e199eb (00007fff4802cc9b) 00007fff4802cc91 e972c9ffff jmp CLRStub[MethodDescPrestub]@7fff48029608 (00007fff48029608) 00007fff4802cc96 e9edc9ffff jmp CLRStub[MethodDescPrestub]@7fff48029688 (00007fff48029688) 00007fff4802cc9b 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.

Method Toolchain Mean Error StdDev Median Ratio RatioSD
ParseGuid_Default master 5,881.1 ns 58.94 ns 55.14 ns 5,876.8 ns 1.00 0.00
ParseGuid_Default tryparse 5,545.2 ns 88.36 ns 82.65 ns 5,517.9 ns 0.94 0.02
ParseGuid_Braces master 6,154.5 ns 119.47 ns 132.79 ns 6,137.5 ns 1.00 0.00
ParseGuid_Braces tryparse 5,821.1 ns 67.85 ns 63.47 ns 5,803.3 ns 0.95 0.02

;;; OLD CODEGEN ;;; 00007fff3ccccdd0 56 push rsi 00007fff3ccccdd1 4883ec40 sub rsp, 40h 00007fff3ccccdd5 c5f877 vzeroupper 00007fff3ccccdd8 33c0 xor eax, eax 00007fff3ccccdda 4889442430 mov qword ptr [rsp+30h], rax 00007fff3ccccddf 410fb7f1 movzx esi, r9w 00007fff3ccccde3 83fe42 cmp esi, 42h 00007fff3ccccde6 7745 ja System_Private_CoreLib!System.Buffers.Text.Utf8Parser.TryParse(System.ReadOnlySpan1<Byte>, System.Guid ByRef, Int32 ByRef, Char)+0xffffffffa0c9a5ad (00007fff3cccce2d) 00007fff3ccccde8 85f6 test esi, esi 00007fff3ccccdea 0f8481000000 je System_Private_CoreLib!System.Buffers.Text.Utf8Parser.TryParse(System.ReadOnlySpan1, System.Guid ByRef, Int32 ByRef, Char)+0xffffffffa0c9a5f1 (00007fff3cccce71) 00007fff3ccccdf0 83fe42 cmp esi, 42h 00007fff3ccccdf3 0f85c3000000 jne System_Private_CoreLib!System.Buffers.Text.Utf8Parser.TryParse(System.ReadOnlySpan1<Byte>, System.Guid ByRef, Int32 ByRef, Char)+0xffffffffa0c9a63c (00007fff3ccccebc) 00007fff3ccccdf9 c5fa6f01 vmovdqu xmm0, xmmword ptr [rcx] 00007fff3ccccdfd c5fa7f442430 vmovdqu xmmword ptr [rsp+30h], xmm0 00007fff3cccce03 4889542420 mov qword ptr [rsp+20h], rdx 00007fff3cccce08 4c89442428 mov qword ptr [rsp+28h], r8 00007fff3cccce0d 488d4c2430 lea rcx, [rsp+30h] 00007fff3cccce12 ba01000000 mov edx, 1 00007fff3cccce17 41b87b000000 mov r8d, 7Bh 00007fff3cccce1d 41b97d000000 mov r9d, 7Dh 00007fff3cccce23 e888c7ffff call CLRStub[MethodDescPrestub]@7fff3ccc95b0 (00007fff3ccc95b0) 00007fff3cccce28 e986000000 jmp System_Private_CoreLib!System.Buffers.Text.Utf8Parser.TryParse(System.ReadOnlySpan1<Byte>, System.Guid ByRef, Int32 ByRef, Char)+0xffffffffa0c9a633 (00007fff3cccceb3) 00007fff3cccce2d 83fe44 cmp esi, 44h 00007fff3cccce30 743f je System_Private_CoreLib!System.Buffers.Text.Utf8Parser.TryParse(System.ReadOnlySpan1, System.Guid ByRef, Int32 ByRef, Char)+0xffffffffa0c9a5f1 (00007fff3cccce71) 00007fff3cccce32 83fe4e cmp esi, 4Eh 00007fff3cccce35 7468 je System_Private_CoreLib!System.Buffers.Text.Utf8Parser.TryParse(System.ReadOnlySpan1<Byte>, System.Guid ByRef, Int32 ByRef, Char)+0xffffffffa0c9a61f (00007fff3cccce9f) 00007fff3cccce37 83fe50 cmp esi, 50h 00007fff3cccce3a 0f857c000000 jne System_Private_CoreLib!System.Buffers.Text.Utf8Parser.TryParse(System.ReadOnlySpan1, System.Guid ByRef, Int32 ByRef, Char)+0xffffffffa0c9a63c (00007fff3ccccebc) 00007fff3cccce40 c5fa6f01 vmovdqu xmm0, xmmword ptr [rcx] 00007fff3cccce44 c5fa7f442430 vmovdqu xmmword ptr [rsp+30h], xmm0 00007fff3cccce4a 4889542420 mov qword ptr [rsp+20h], rdx 00007fff3cccce4f 4c89442428 mov qword ptr [rsp+28h], r8 00007fff3cccce54 488d4c2430 lea rcx, [rsp+30h] 00007fff3cccce59 ba01000000 mov edx, 1 00007fff3cccce5e 41b828000000 mov r8d, 28h 00007fff3cccce64 41b929000000 mov r9d, 29h 00007fff3cccce6a e841c7ffff call CLRStub[MethodDescPrestub]@7fff3ccc95b0 (00007fff3ccc95b0) 00007fff3cccce6f eb42 jmp System_Private_CoreLib!System.Buffers.Text.Utf8Parser.TryParse(System.ReadOnlySpan1, System.Guid ByRef, Int32 ByRef, Char)+0xffffffffa0c9a633 (00007fff3cccceb3) 00007fff3cccce71 c5fa6f01 vmovdqu xmm0, xmmword ptr [rcx] 00007fff3cccce75 c5fa7f442430 vmovdqu xmmword ptr [rsp+30h], xmm0 00007fff3cccce7b 4889542420 mov qword ptr [rsp+20h], rdx 00007fff3cccce80 4c89442428 mov qword ptr [rsp+28h], r8 00007fff3cccce85 488d4c2430 lea rcx, [rsp+30h] 00007fff3cccce8a 33d2 xor edx, edx 00007fff3cccce8c 41b820000000 mov r8d, 20h 00007fff3cccce92 41b920000000 mov r9d, 20h 00007fff3cccce98 e813c7ffff call CLRStub[MethodDescPrestub]@7fff3ccc95b0 (00007fff3ccc95b0) 00007fff3cccce9d eb14 jmp System_Private_CoreLib!System.Buffers.Text.Utf8Parser.TryParse(System.ReadOnlySpan1, System.Guid ByRef, Int32 ByRef, Char)+0xffffffffa0c9a633 (00007fff3cccceb3) 00007fff3cccce9f c5fa6f01 vmovdqu xmm0, xmmword ptr [rcx] 00007fff3ccccea3 c5fa7f442430 vmovdqu xmmword ptr [rsp+30h], xmm0 00007fff3ccccea9 488d4c2430 lea rcx, [rsp+30h] 00007fff3cccceae e8f5c6ffff call CLRStub[MethodDescPrestub]@7fff3ccc95a8 (00007fff3ccc95a8) 00007fff3cccceb3 0fb6c0 movzx eax, al 00007fff3cccceb6 4883c440 add rsp, 40h 00007fff3cccceba 5e pop rsi 00007fff3ccccebb c3 ret 00007fff3ccccebc c5f857c0 vxorps xmm0, xmm0, xmm0 00007fff3ccccec0 c5fa7f02 vmovdqu xmmword ptr [rdx], xmm0 00007fff3ccccec4 33c0 xor eax, eax 00007fff3ccccec6 418900 mov dword ptr [r8], eax 00007fff3ccccec9 e8c2d5d8ff call CLRStub[MethodDescPrestub]@7fff3ca5a490 (00007fff3ca5a490) 00007fff3ccccece cc int 3

;;; NEW CODEGEN ;;; 00007fff3cccccc0 410fb7c1 movzx eax, r9w 00007fff3cccccc4 85c0 test eax, eax 00007fff3cccccc6 7508 jne System_Private_CoreLib!System.Buffers.Text.Utf8Parser.TryParse(System.ReadOnlySpan1, System.Guid ByRef, Int32 ByRef, Char)+0xffffffffa0c9a370 (00007fff3cccccd0) 00007fff3cccccc8 4533c9 xor r9d, r9d 00007fff3ccccccb e9a8d1ffff jmp CLRStub[MethodDescPrestub]@7fff3ccc9e78 (00007fff3ccc9e78) 00007fff3cccccd0 450fb7c9 movzx r9d, r9w 00007fff3cccccd4 4183f944 cmp r9d, 44h 00007fff3cccccd8 770e ja System_Private_CoreLib!System.Buffers.Text.Utf8Parser.TryParse(System.ReadOnlySpan1<Byte>, System.Guid ByRef, Int32 ByRef, Char)+0xffffffffa0c9a388 (00007fff3ccccce8) 00007fff3cccccda 4183f942 cmp r9d, 42h 00007fff3cccccde 7416 je System_Private_CoreLib!System.Buffers.Text.Utf8Parser.TryParse(System.ReadOnlySpan1, System.Guid ByRef, Int32 ByRef, Char)+0xffffffffa0c9a396 (00007fff3cccccf6) 00007fff3ccccce0 4183f944 cmp r9d, 44h 00007fff3ccccce4 74e2 je System_Private_CoreLib!System.Buffers.Text.Utf8Parser.TryParse(System.ReadOnlySpan1<Byte>, System.Guid ByRef, Int32 ByRef, Char)+0xffffffffa0c9a368 (00007fff3cccccc8) 00007fff3ccccce6 eb29 jmp System_Private_CoreLib!System.Buffers.Text.Utf8Parser.TryParse(System.ReadOnlySpan1<Byte>, System.Guid ByRef, Int32 ByRef, Char)+0xffffffffa0c9a3b1 (00007fff3ccccd11) 00007fff3ccccce8 4183f94e cmp r9d, 4Eh 00007fff3cccccec 741e je System_Private_CoreLib!System.Buffers.Text.Utf8Parser.TryParse(System.ReadOnlySpan1, System.Guid ByRef, Int32 ByRef, Char)+0xffffffffa0c9a3ac (00007fff3ccccd0c) 00007fff3cccccee 4183f950 cmp r9d, 50h 00007fff3cccccf2 740d je System_Private_CoreLib!System.Buffers.Text.Utf8Parser.TryParse(System.ReadOnlySpan1<Byte>, System.Guid ByRef, Int32 ByRef, Char)+0xffffffffa0c9a3a1 (00007fff3ccccd01) 00007fff3cccccf4 eb1b jmp System_Private_CoreLib!System.Buffers.Text.Utf8Parser.TryParse(System.ReadOnlySpan1<Byte>, System.Guid ByRef, Int32 ByRef, Char)+0xffffffffa0c9a3b1 (00007fff3ccccd11) 00007fff3cccccf6 41b97b7d0000 mov r9d, 7D7Bh 00007fff3cccccfc e977d1ffff jmp CLRStub[MethodDescPrestub]@7fff3ccc9e78 (00007fff3ccc9e78) 00007fff3ccccd01 41b928290000 mov r9d, 2928h 00007fff3ccccd07 e96cd1ffff jmp CLRStub[MethodDescPrestub]@7fff3ccc9e78 (00007fff3ccc9e78) 00007fff3ccccd0c e95fd1ffff jmp CLRStub[MethodDescPrestub]@7fff3ccc9e70 (00007fff3ccc9e70) 00007fff3ccccd11 e91afdffff jmp CLRStub[MethodDescPrestub]@7fff3cccca30 (00007fff`3cccca30)