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 00007fff
4803cb41 4883ec20 sub rsp, 20h
00007fff4803cb45 410fb7f1 movzx esi, r9w 00007fff
4803cb49 83fe4e cmp esi, 4Eh
00007fff4803cb4c 772c ja System_Private_CoreLib!System.Buffers.Text.Utf8Parser.TryParse(System.ReadOnlySpan
1, Int32 ByRef, Int32 ByRef, Char)+0xffffffffa9e299ba (00007fff
4803cb7a)
00007fff4803cb4e 83fe44 cmp esi, 44h 00007fff
4803cb51 7713 ja System_Private_CoreLib!System.Buffers.Text.Utf8Parser.TryParse(System.ReadOnlySpan1<Byte>, Int32 ByRef, Int32 ByRef, Char)+0xffffffff
a9e299a6 (00007fff4803cb66) 00007fff
4803cb53 85f6 test esi, esi
00007fff4803cb55 7405 je System_Private_CoreLib!System.Buffers.Text.Utf8Parser.TryParse(System.ReadOnlySpan
1, Int32 ByRef, Int32 ByRef, Char)+0xffffffffa9e2999c (00007fff
4803cb5c)
00007fff4803cb57 83fe44 cmp esi, 44h 00007fff
4803cb5a 754c jne System_Private_CoreLib!System.Buffers.Text.Utf8Parser.TryParse(System.ReadOnlySpan1<Byte>, Int32 ByRef, Int32 ByRef, Char)+0xffffffff
a9e299e8 (00007fff4803cba8) 00007fff
4803cb5c 4883c420 add rsp, 20h
00007fff4803cb60 5e pop rsi 00007fff
4803cb61 e97acaffff jmp CLRStub[MethodDescPrestub]@7fff480395e0 (00007fff480395e0) 00007fff
4803cb66 83fe47 cmp esi, 47h
00007fff4803cb69 74f1 je System_Private_CoreLib!System.Buffers.Text.Utf8Parser.TryParse(System.ReadOnlySpan
1, Int32 ByRef, Int32 ByRef, Char)+0xffffffffa9e2999c (00007fff
4803cb5c)
00007fff4803cb6b 83fe4e cmp esi, 4Eh 00007fff
4803cb6e 7538 jne System_Private_CoreLib!System.Buffers.Text.Utf8Parser.TryParse(System.ReadOnlySpan1<Byte>, Int32 ByRef, Int32 ByRef, Char)+0xffffffff
a9e299e8 (00007fff4803cba8) 00007fff
4803cb70 4883c420 add rsp, 20h
00007fff4803cb74 5e pop rsi 00007fff
4803cb75 e986caffff jmp CLRStub[MethodDescPrestub]@7fff48039600 (00007fff48039600) 00007fff
4803cb7a 83fe64 cmp esi, 64h
00007fff4803cb7d 770c ja System_Private_CoreLib!System.Buffers.Text.Utf8Parser.TryParse(System.ReadOnlySpan
1, Int32 ByRef, Int32 ByRef, Char)+0xffffffffa9e299cb (00007fff
4803cb8b)
00007fff4803cb7f 83fe58 cmp esi, 58h 00007fff
4803cb82 7416 je System_Private_CoreLib!System.Buffers.Text.Utf8Parser.TryParse(System.ReadOnlySpan1<Byte>, Int32 ByRef, Int32 ByRef, Char)+0xffffffff
a9e299da (00007fff4803cb9a) 00007fff
4803cb84 83fe64 cmp esi, 64h
00007fff4803cb87 751f jne System_Private_CoreLib!System.Buffers.Text.Utf8Parser.TryParse(System.ReadOnlySpan
1, Int32 ByRef, Int32 ByRef, Char)+0xffffffffa9e299e8 (00007fff
4803cba8)
00007fff4803cb89 ebd1 jmp System_Private_CoreLib!System.Buffers.Text.Utf8Parser.TryParse(System.ReadOnlySpan
1, Int32 ByRef, Int32 ByRef, Char)+0xffffffffa9e2999c (00007fff
4803cb5c)
00007fff4803cb8b 83fe67 cmp esi, 67h 00007fff
4803cb8e 74cc je System_Private_CoreLib!System.Buffers.Text.Utf8Parser.TryParse(System.ReadOnlySpan1<Byte>, Int32 ByRef, Int32 ByRef, Char)+0xffffffff
a9e2999c (00007fff4803cb5c) 00007fff
4803cb90 83fe6e cmp esi, 6Eh
00007fff4803cb93 74db je System_Private_CoreLib!System.Buffers.Text.Utf8Parser.TryParse(System.ReadOnlySpan
1, Int32 ByRef, Int32 ByRef, Char)+0xffffffffa9e299b0 (00007fff
4803cb70)
00007fff4803cb95 83fe78 cmp esi, 78h 00007fff
4803cb98 750e jne System_Private_CoreLib!System.Buffers.Text.Utf8Parser.TryParse(System.ReadOnlySpan1<Byte>, Int32 ByRef, Int32 ByRef, Char)+0xffffffff
a9e299e8 (00007fff4803cba8) 00007fff
4803cb9a 33c0 xor eax, eax
00007fff4803cb9c 8902 mov dword ptr [rdx], eax 00007fff
4803cb9e 4883c420 add rsp, 20h
00007fff4803cba2 5e pop rsi 00007fff
4803cba3 e9d8caffff jmp CLRStub[MethodDescPrestub]@7fff48039680 (00007fff48039680) 00007fff
4803cba8 33c0 xor eax, eax
00007fff4803cbaa 8902 mov dword ptr [rdx], eax 00007fff
4803cbac 418900 mov dword ptr [r8], eax
00007fff4803cbaf e8dcd8d8ff call CLRStub[MethodDescPrestub]@7fff47dca490 (00007fff
47dca490)
00007fff`4803cbb4 cc int 3
;;; NEW CODEGEN ;;;
00007fff4802cc60 410fb7c1 movzx eax, r9w 00007fff
4802cc64 85c0 test eax, eax
00007fff4802cc66 7505 jne System_Private_CoreLib!System.Buffers.Text.Utf8Parser.TryParse(System.ReadOnlySpan
1, Int32 ByRef, Int32 ByRef, Char)+0xffffffffa9e199bd (00007fff
4802cc6d)
00007fff4802cc68 e97bc9ffff jmp CLRStub[MethodDescPrestub]@7fff480295e8 (00007fff
480295e8)
00007fff4802cc6d 410fb7c1 movzx eax, r9w 00007fff
4802cc71 83c820 or eax, 20h
00007fff4802cc74 83f867 cmp eax, 67h 00007fff
4802cc77 7f0c jg System_Private_CoreLib!System.Buffers.Text.Utf8Parser.TryParse(System.ReadOnlySpan1<Byte>, Int32 ByRef, Int32 ByRef, Char)+0xffffffff
a9e199d5 (00007fff4802cc85) 00007fff
4802cc79 83f864 cmp eax, 64h
00007fff4802cc7c 74ea je System_Private_CoreLib!System.Buffers.Text.Utf8Parser.TryParse(System.ReadOnlySpan
1, Int32 ByRef, Int32 ByRef, Char)+0xffffffffa9e199b8 (00007fff
4802cc68)
00007fff4802cc7e 83f867 cmp eax, 67h 00007fff
4802cc81 74e5 je System_Private_CoreLib!System.Buffers.Text.Utf8Parser.TryParse(System.ReadOnlySpan1<Byte>, Int32 ByRef, Int32 ByRef, Char)+0xffffffff
a9e199b8 (00007fff4802cc68) 00007fff
4802cc83 eb16 jmp System_Private_CoreLib!System.Buffers.Text.Utf8Parser.TryParse(System.ReadOnlySpan1<Byte>, Int32 ByRef, Int32 ByRef, Char)+0xffffffff
a9e199eb (00007fff4802cc9b) 00007fff
4802cc85 83f86e cmp eax, 6Eh
00007fff4802cc88 7407 je System_Private_CoreLib!System.Buffers.Text.Utf8Parser.TryParse(System.ReadOnlySpan
1, Int32 ByRef, Int32 ByRef, Char)+0xffffffffa9e199e1 (00007fff
4802cc91)
00007fff4802cc8a 83f878 cmp eax, 78h 00007fff
4802cc8d 7407 je System_Private_CoreLib!System.Buffers.Text.Utf8Parser.TryParse(System.ReadOnlySpan1<Byte>, Int32 ByRef, Int32 ByRef, Char)+0xffffffff
a9e199e6 (00007fff4802cc96) 00007fff
4802cc8f eb0a jmp System_Private_CoreLib!System.Buffers.Text.Utf8Parser.TryParse(System.ReadOnlySpan1<Byte>, Int32 ByRef, Int32 ByRef, Char)+0xffffffff
a9e199eb (00007fff4802cc9b) 00007fff
4802cc91 e972c9ffff jmp CLRStub[MethodDescPrestub]@7fff48029608 (00007fff48029608) 00007fff
4802cc96 e9edc9ffff jmp CLRStub[MethodDescPrestub]@7fff48029688 (00007fff48029688) 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.
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 00007fff
3ccccdd1 4883ec40 sub rsp, 40h
00007fff3ccccdd5 c5f877 vzeroupper 00007fff
3ccccdd8 33c0 xor eax, eax
00007fff3ccccdda 4889442430 mov qword ptr [rsp+30h], rax 00007fff
3ccccddf 410fb7f1 movzx esi, r9w
00007fff3ccccde3 83fe42 cmp esi, 42h 00007fff
3ccccde6 7745 ja System_Private_CoreLib!System.Buffers.Text.Utf8Parser.TryParse(System.ReadOnlySpan1<Byte>, System.Guid ByRef, Int32 ByRef, Char)+0xffffffff
a0c9a5ad (00007fff3cccce2d) 00007fff
3ccccde8 85f6 test esi, esi
00007fff3ccccdea 0f8481000000 je System_Private_CoreLib!System.Buffers.Text.Utf8Parser.TryParse(System.ReadOnlySpan
1, System.Guid ByRef, Int32 ByRef, Char)+0xffffffffa0c9a5f1 (00007fff
3cccce71)
00007fff3ccccdf0 83fe42 cmp esi, 42h 00007fff
3ccccdf3 0f85c3000000 jne System_Private_CoreLib!System.Buffers.Text.Utf8Parser.TryParse(System.ReadOnlySpan1<Byte>, System.Guid ByRef, Int32 ByRef, Char)+0xffffffff
a0c9a63c (00007fff3ccccebc) 00007fff
3ccccdf9 c5fa6f01 vmovdqu xmm0, xmmword ptr [rcx]
00007fff3ccccdfd c5fa7f442430 vmovdqu xmmword ptr [rsp+30h], xmm0 00007fff
3cccce03 4889542420 mov qword ptr [rsp+20h], rdx
00007fff3cccce08 4c89442428 mov qword ptr [rsp+28h], r8 00007fff
3cccce0d 488d4c2430 lea rcx, [rsp+30h]
00007fff3cccce12 ba01000000 mov edx, 1 00007fff
3cccce17 41b87b000000 mov r8d, 7Bh
00007fff3cccce1d 41b97d000000 mov r9d, 7Dh 00007fff
3cccce23 e888c7ffff call CLRStub[MethodDescPrestub]@7fff3ccc95b0 (00007fff3ccc95b0) 00007fff
3cccce28 e986000000 jmp System_Private_CoreLib!System.Buffers.Text.Utf8Parser.TryParse(System.ReadOnlySpan1<Byte>, System.Guid ByRef, Int32 ByRef, Char)+0xffffffff
a0c9a633 (00007fff3cccceb3) 00007fff
3cccce2d 83fe44 cmp esi, 44h
00007fff3cccce30 743f je System_Private_CoreLib!System.Buffers.Text.Utf8Parser.TryParse(System.ReadOnlySpan
1, System.Guid ByRef, Int32 ByRef, Char)+0xffffffffa0c9a5f1 (00007fff
3cccce71)
00007fff3cccce32 83fe4e cmp esi, 4Eh 00007fff
3cccce35 7468 je System_Private_CoreLib!System.Buffers.Text.Utf8Parser.TryParse(System.ReadOnlySpan1<Byte>, System.Guid ByRef, Int32 ByRef, Char)+0xffffffff
a0c9a61f (00007fff3cccce9f) 00007fff
3cccce37 83fe50 cmp esi, 50h
00007fff3cccce3a 0f857c000000 jne System_Private_CoreLib!System.Buffers.Text.Utf8Parser.TryParse(System.ReadOnlySpan
1, System.Guid ByRef, Int32 ByRef, Char)+0xffffffffa0c9a63c (00007fff
3ccccebc)
00007fff3cccce40 c5fa6f01 vmovdqu xmm0, xmmword ptr [rcx] 00007fff
3cccce44 c5fa7f442430 vmovdqu xmmword ptr [rsp+30h], xmm0
00007fff3cccce4a 4889542420 mov qword ptr [rsp+20h], rdx 00007fff
3cccce4f 4c89442428 mov qword ptr [rsp+28h], r8
00007fff3cccce54 488d4c2430 lea rcx, [rsp+30h] 00007fff
3cccce59 ba01000000 mov edx, 1
00007fff3cccce5e 41b828000000 mov r8d, 28h 00007fff
3cccce64 41b929000000 mov r9d, 29h
00007fff3cccce6a e841c7ffff call CLRStub[MethodDescPrestub]@7fff3ccc95b0 (00007fff
3ccc95b0)
00007fff3cccce6f eb42 jmp System_Private_CoreLib!System.Buffers.Text.Utf8Parser.TryParse(System.ReadOnlySpan
1, System.Guid ByRef, Int32 ByRef, Char)+0xffffffffa0c9a633 (00007fff
3cccceb3)
00007fff3cccce71 c5fa6f01 vmovdqu xmm0, xmmword ptr [rcx] 00007fff
3cccce75 c5fa7f442430 vmovdqu xmmword ptr [rsp+30h], xmm0
00007fff3cccce7b 4889542420 mov qword ptr [rsp+20h], rdx 00007fff
3cccce80 4c89442428 mov qword ptr [rsp+28h], r8
00007fff3cccce85 488d4c2430 lea rcx, [rsp+30h] 00007fff
3cccce8a 33d2 xor edx, edx
00007fff3cccce8c 41b820000000 mov r8d, 20h 00007fff
3cccce92 41b920000000 mov r9d, 20h
00007fff3cccce98 e813c7ffff call CLRStub[MethodDescPrestub]@7fff3ccc95b0 (00007fff
3ccc95b0)
00007fff3cccce9d eb14 jmp System_Private_CoreLib!System.Buffers.Text.Utf8Parser.TryParse(System.ReadOnlySpan
1, System.Guid ByRef, Int32 ByRef, Char)+0xffffffffa0c9a633 (00007fff
3cccceb3)
00007fff3cccce9f c5fa6f01 vmovdqu xmm0, xmmword ptr [rcx] 00007fff
3ccccea3 c5fa7f442430 vmovdqu xmmword ptr [rsp+30h], xmm0
00007fff3ccccea9 488d4c2430 lea rcx, [rsp+30h] 00007fff
3cccceae e8f5c6ffff call CLRStub[MethodDescPrestub]@7fff3ccc95a8 (00007fff3ccc95a8) 00007fff
3cccceb3 0fb6c0 movzx eax, al
00007fff3cccceb6 4883c440 add rsp, 40h 00007fff
3cccceba 5e pop rsi
00007fff3ccccebb c3 ret 00007fff
3ccccebc c5f857c0 vxorps xmm0, xmm0, xmm0
00007fff3ccccec0 c5fa7f02 vmovdqu xmmword ptr [rdx], xmm0 00007fff
3ccccec4 33c0 xor eax, eax
00007fff3ccccec6 418900 mov dword ptr [r8], eax 00007fff
3ccccec9 e8c2d5d8ff call CLRStub[MethodDescPrestub]@7fff3ca5a490 (00007fff3ca5a490) 00007fff
3ccccece cc int 3
;;; NEW CODEGEN ;;;
00007fff3cccccc0 410fb7c1 movzx eax, r9w 00007fff
3cccccc4 85c0 test eax, eax
00007fff3cccccc6 7508 jne System_Private_CoreLib!System.Buffers.Text.Utf8Parser.TryParse(System.ReadOnlySpan
1, System.Guid ByRef, Int32 ByRef, Char)+0xffffffffa0c9a370 (00007fff
3cccccd0)
00007fff3cccccc8 4533c9 xor r9d, r9d 00007fff
3ccccccb e9a8d1ffff jmp CLRStub[MethodDescPrestub]@7fff3ccc9e78 (00007fff3ccc9e78) 00007fff
3cccccd0 450fb7c9 movzx r9d, r9w
00007fff3cccccd4 4183f944 cmp r9d, 44h 00007fff
3cccccd8 770e ja System_Private_CoreLib!System.Buffers.Text.Utf8Parser.TryParse(System.ReadOnlySpan1<Byte>, System.Guid ByRef, Int32 ByRef, Char)+0xffffffff
a0c9a388 (00007fff3ccccce8) 00007fff
3cccccda 4183f942 cmp r9d, 42h
00007fff3cccccde 7416 je System_Private_CoreLib!System.Buffers.Text.Utf8Parser.TryParse(System.ReadOnlySpan
1, System.Guid ByRef, Int32 ByRef, Char)+0xffffffffa0c9a396 (00007fff
3cccccf6)
00007fff3ccccce0 4183f944 cmp r9d, 44h 00007fff
3ccccce4 74e2 je System_Private_CoreLib!System.Buffers.Text.Utf8Parser.TryParse(System.ReadOnlySpan1<Byte>, System.Guid ByRef, Int32 ByRef, Char)+0xffffffff
a0c9a368 (00007fff3cccccc8) 00007fff
3ccccce6 eb29 jmp System_Private_CoreLib!System.Buffers.Text.Utf8Parser.TryParse(System.ReadOnlySpan1<Byte>, System.Guid ByRef, Int32 ByRef, Char)+0xffffffff
a0c9a3b1 (00007fff3ccccd11) 00007fff
3ccccce8 4183f94e cmp r9d, 4Eh
00007fff3cccccec 741e je System_Private_CoreLib!System.Buffers.Text.Utf8Parser.TryParse(System.ReadOnlySpan
1, System.Guid ByRef, Int32 ByRef, Char)+0xffffffffa0c9a3ac (00007fff
3ccccd0c)
00007fff3cccccee 4183f950 cmp r9d, 50h 00007fff
3cccccf2 740d je System_Private_CoreLib!System.Buffers.Text.Utf8Parser.TryParse(System.ReadOnlySpan1<Byte>, System.Guid ByRef, Int32 ByRef, Char)+0xffffffff
a0c9a3a1 (00007fff3ccccd01) 00007fff
3cccccf4 eb1b jmp System_Private_CoreLib!System.Buffers.Text.Utf8Parser.TryParse(System.ReadOnlySpan1<Byte>, System.Guid ByRef, Int32 ByRef, Char)+0xffffffff
a0c9a3b1 (00007fff3ccccd11) 00007fff
3cccccf6 41b97b7d0000 mov r9d, 7D7Bh
00007fff3cccccfc e977d1ffff jmp CLRStub[MethodDescPrestub]@7fff3ccc9e78 (00007fff
3ccc9e78)
00007fff3ccccd01 41b928290000 mov r9d, 2928h 00007fff
3ccccd07 e96cd1ffff jmp CLRStub[MethodDescPrestub]@7fff3ccc9e78 (00007fff3ccc9e78) 00007fff
3ccccd0c e95fd1ffff jmp CLRStub[MethodDescPrestub]@7fff3ccc9e70 (00007fff3ccc9e70) 00007fff
3ccccd11 e91afdffff jmp CLRStub[MethodDescPrestub]@7fff3cccca30 (00007fff`3cccca30)