Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[WIP] Improve performance of Utf8Parser.TryParseInt32D #9

Closed
wants to merge 7 commits into from

Conversation

GrabYourPitchforks
Copy link
Owner

Copied from dotnet#32843.


WIP because it's not plumbed through to other Utf8Parser APIs and because these same improvements are likely viable for other methods like int.Parse. I also need to clean up / comment the code a little bit.

There are a handful of patterns being used here that result in improved codegen. Some of these are things that really should be in the JIT's wheelhouse rather than worked around in this code base. Given that Utf8Parser.TryParse (and related APIs int.Parse) are such hot code paths, though, it may be worthwhile to take these code changes anyway while waiting for the JIT's changes to come through.

Overall perf results

Method Toolchain Mean Error StdDev Ratio
TryParseInt32 master 1,217.5 ns 12.02 ns 10.03 ns 1.00
TryParseInt32 thisbranch 954.9 ns 8.77 ns 6.85 ns 0.78

Fast-track 'default' formats

Since we expect standardFormat = default to be the common case, it's fast-tracked at the start of the method rather than entering the switch statement. Additionally, entries in the switch statement are normalized to lowercase to minimize the number of if conditions present.

; BEFORE
movzx   ebx,r9w
cmp     ebx,4Eh
ja      System_Private_CoreLib!System.Buffers.Text.Utf8Parser.TryParse(System.ReadOnlySpan`1<Byte>, Int32 ByRef, Int32 ByRef, Char)+0xffffffff`a3c352f5 (00007ff9`b841a555)
cmp     ebx,44h
ja      System_Private_CoreLib!System.Buffers.Text.Utf8Parser.TryParse(System.ReadOnlySpan`1<Byte>, Int32 ByRef, Int32 ByRef, Char)+0xffffffff`a3c352cd (00007ff9`b841a52d)
test    ebx,ebx
je      System_Private_CoreLib!System.Buffers.Text.Utf8Parser.TryParse(System.ReadOnlySpan`1<Byte>, Int32 ByRef, Int32 ByRef, Char)+0xffffffff`a3c352af (00007ff9`b841a50f)
cmp     ebx,44h
jne     System_Private_CoreLib!System.Buffers.Text.Utf8Parser.TryParse(System.ReadOnlySpan`1<Byte>, Int32 ByRef, Int32 ByRef, Char)+0xffffffff`a3c35337 (00007ff9`b841a597)
lea     rcx,[rsp+20h]
mov     qword ptr [rcx],rsi
mov     dword ptr [rcx+8],edi
lea     rcx,[rsp+20h]
call    CLRStub[MethodDescPrestub]@7ff9b84188c0 (00007ff9`b84188c0)

; AFTER
movzx   eax,r9w
test    eax,eax
je      System_Private_CoreLib!System.Buffers.Text.Utf8Parser.TryParse(System.ReadOnlySpan`1<Byte>, Int32 ByRef, Int32 ByRef, Char)+0xffffffff`a3c78a18 (00007ff9`b843abb8)
or      eax,20h
cmp     eax,67h
je      System_Private_CoreLib!System.Buffers.Text.Utf8Parser.TryParse(System.ReadOnlySpan`1<Byte>, Int32 ByRef, Int32 ByRef, Char)+0xffffffff`a3c78a18 (00007ff9`b843abb8)
cmp     eax,64h
jne     System_Private_CoreLib!System.Buffers.Text.Utf8Parser.TryParse(System.ReadOnlySpan`1<Byte>, Int32 ByRef, Int32 ByRef, Char)+0xffffffff`a3c78a35 (00007ff9`b843abd5)
lea     rcx,[rsp+28h]
mov     qword ptr [rcx],rsi
mov     dword ptr [rcx+8],edi
lea     rcx,[rsp+28h]
call    CLRStub[MethodDescPrestub]@7ff9b8438388 (00007ff9`b8438388)

Repurpose some locals for more efficient register allocation

The answer local is repurposed to hold the running value from the very start of the method, no longer bouncing through num. The check for '+' and '-' is moved out of the common path because those characters should be uncommon.

Avoid expensive instructions

The span is indexed by native int rather than int. This avoids an expensive movsxd instruction in the hot path. Additionally, at the end of the method the imul instruction is removed in favor of cheaper xor and sub instructions.

Finally, the intermediate value resulting from the num - '0' operation is reused as the addend when building up the expression answer = answer * 10 + .... This avoids an expensive three-component lea instruction in the hot path.

; HOT PATH - BEFORE
cmp     r10d,ecx
jae     System_Private_CoreLib!System.Buffers.Text.Utf8Parser.TryParseInt32D(System.ReadOnlySpan`1<Byte>, Int32 ByRef, Int32 ByRef)+0xffffffff`beef5172 (00007ff9`e303a882)
movsxd  r11,r10d
movzx   r11d,byte ptr [rax+r11]
lea     edi,[r11-30h]
cmp     edi,9
ja      System_Private_CoreLib!System.Buffers.Text.Utf8Parser.TryParseInt32D(System.ReadOnlySpan`1<Byte>, Int32 ByRef, Int32 ByRef)+0xffffffff`beef5172 (00007ff9`e303a882)
inc     r10d
lea     esi,[rsi+rsi*4]
lea     esi,[r11+rsi*2-30h]

; HOT PATH - AFTER
cmp     r11,rdi
jae     System_Private_CoreLib!System.Buffers.Text.Utf8Parser.TryParseInt32D(System.ReadOnlySpan`1<Byte>, Int32 ByRef, Int32 ByRef)+0xffffffff`befc8605 (00007ff9`e30eac25)
movzx   eax,byte ptr [r9+r11]
add     eax,0FFFFFFD0h
cmp     eax,9
ja      System_Private_CoreLib!System.Buffers.Text.Utf8Parser.TryParseInt32D(System.ReadOnlySpan`1<Byte>, Int32 ByRef, Int32 ByRef)+0xffffffff`befc8605 (00007ff9`e30eac25)
inc     r11
lea     ecx,[rsi+rsi*4]
lea     esi,[rax+rcx*2]

Benchmark application

public class Utf8ParserRunner
{
    private byte[][] _intStrings;

    [GlobalSetup]
    public void Setup()
    {
        // Randomly create 200 strings "19472903" and similar.
        // Vary the lengths to throw off the branch predictor.
        Random rnd = new Random(0x12345);
        byte[][] intStrings = new byte[200][];
        for (int i = 0; i < intStrings.Length; i++)
        {
            byte[] thisIntString = new byte[rnd.Next(1, 10)];
            for (int j = 0; j < thisIntString.Length; j++)
            {
                thisIntString[j] = (byte)rnd.Next(0, 10);
            }
            intStrings[i] = thisIntString;
        }
        _intStrings = intStrings;
    }

    [Benchmark]
    public bool TryParseInt32()
    {
        bool retVal = false;

        byte[][] strs = _intStrings;
        for (int i = 0; i < strs.Length; i++)
        {
            byte[] str = strs[i];
            _ = str.Length; // deref now to avoid implicit span null check

            retVal = Utf8Parser.TryParse(str, out int _, out int _);
        }

        return retVal;
    }
}

Related issues: dotnet#28070, dotnet#12218, dotnet#12402

GrabYourPitchforks pushed a commit that referenced this pull request May 18, 2021
…tnet#52769)

Transition to GC Unsafe mode on every MONO_RT_EXTERNAL_ONLY function in
reflection.c

In particular, fix mono_reflection_type_from_name which is used in
https://github.com/xamarin/xamarin-android/blob/681887ebdbd192ce7ce1cd02221d4939599ba762/src/monodroid/jni/embedded-assemblies.cc#L350

Fixes stack traces like

```
05-14 08:06:12.848 31274 31274 F DEBUG   :       #00 pc 00000b99  [vdso] (__kernel_vsyscall+9)
05-14 08:06:12.848 31274 31274 F DEBUG   :       #1 pc 0005ad68  /apex/com.android.runtime/lib/bionic/libc.so (syscall+40) (BuildId: 6e3a0180fa6637b68c0d181c343e6806)
05-14 08:06:12.848 31274 31274 F DEBUG   :       #2 pc 00076511  /apex/com.android.runtime/lib/bionic/libc.so (abort+209) (BuildId: 6e3a0180fa6637b68c0d181c343e6806)
05-14 08:06:12.848 31274 31274 F DEBUG   :       #3 pc 0002afcd  /data/app/~~rMrkpKmVPaBpM5jKb8fPAg==/com.microsoft.maui-JfRo8RWSDJaNtJuBa0y7_Q==/lib/x86/libmonodroid.so (xamarin::android::internal::MonodroidRuntime::mono_log_handler(char const*, char const*, char const*, int, void*)+141) (BuildId: 9726f32ad5f8fa5e7c5762baf2f6e3294da41cc1)
05-14 08:06:12.848 31274 31274 F DEBUG   :       #4 pc 00112c5d  /data/app/~~rMrkpKmVPaBpM5jKb8fPAg==/com.microsoft.maui-JfRo8RWSDJaNtJuBa0y7_Q==/lib/x86/libmonosgen-2.0.so (eglib_log_adapter+141) (BuildId: b67e93dd750dafdd6f65f408b021b6a3a74868ac)
05-14 08:06:12.849 31274 31274 F DEBUG   :       #5 pc 00020fdf  /data/app/~~rMrkpKmVPaBpM5jKb8fPAg==/com.microsoft.maui-JfRo8RWSDJaNtJuBa0y7_Q==/lib/x86/libmonosgen-2.0.so (monoeg_g_logv+175) (BuildId: b67e93dd750dafdd6f65f408b021b6a3a74868ac)
05-14 08:06:12.849 31274 31274 F DEBUG   :       #6 pc 0002113a  /data/app/~~rMrkpKmVPaBpM5jKb8fPAg==/com.microsoft.maui-JfRo8RWSDJaNtJuBa0y7_Q==/lib/x86/libmonosgen-2.0.so (monoeg_g_log+42) (BuildId: b67e93dd750dafdd6f65f408b021b6a3a74868ac)
05-14 08:06:12.849 31274 31274 F DEBUG   :       #7 pc 00128892  /data/app/~~rMrkpKmVPaBpM5jKb8fPAg==/com.microsoft.maui-JfRo8RWSDJaNtJuBa0y7_Q==/lib/x86/libmonosgen-2.0.so (mono_threads_transition_do_blocking+258) (BuildId: b67e93dd750dafdd6f65f408b021b6a3a74868ac)
05-14 08:06:12.849 31274 31274 F DEBUG   :       #8 pc 0012a406  /data/app/~~rMrkpKmVPaBpM5jKb8fPAg==/com.microsoft.maui-JfRo8RWSDJaNtJuBa0y7_Q==/lib/x86/libmonosgen-2.0.so (mono_threads_enter_gc_safe_region_unbalanced_with_info+134) (BuildId: b67e93dd750dafdd6f65f408b021b6a3a74868ac)
05-14 08:06:12.849 31274 31274 F DEBUG   :       #9 pc 0012a27e  /data/app/~~rMrkpKmVPaBpM5jKb8fPAg==/com.microsoft.maui-JfRo8RWSDJaNtJuBa0y7_Q==/lib/x86/libmonosgen-2.0.so (mono_threads_enter_gc_safe_region_internal+46) (BuildId: b67e93dd750dafdd6f65f408b021b6a3a74868ac)
05-14 08:06:12.849 31274 31274 F DEBUG   :       #10 pc 000799a7  /data/app/~~rMrkpKmVPaBpM5jKb8fPAg==/com.microsoft.maui-JfRo8RWSDJaNtJuBa0y7_Q==/lib/x86/libmonosgen-2.0.so (mono_loader_lock+71) (BuildId: b67e93dd750dafdd6f65f408b021b6a3a74868ac)
05-14 08:06:12.849 31274 31274 F DEBUG   :       #11 pc 000447a1  /data/app/~~rMrkpKmVPaBpM5jKb8fPAg==/com.microsoft.maui-JfRo8RWSDJaNtJuBa0y7_Q==/lib/x86/libmonosgen-2.0.so (mono_class_create_from_typedef+129) (BuildId: b67e93dd750dafdd6f65f408b021b6a3a74868ac)
05-14 08:06:12.849 31274 31274 F DEBUG   :       #12 pc 0003c073  /data/app/~~rMrkpKmVPaBpM5jKb8fPAg==/com.microsoft.maui-JfRo8RWSDJaNtJuBa0y7_Q==/lib/x86/libmonosgen-2.0.so (mono_class_get_checked+99) (BuildId: b67e93dd750dafdd6f65f408b021b6a3a74868ac)
05-14 08:06:12.849 31274 31274 F DEBUG   :       #13 pc 0003cc0f  /data/app/~~rMrkpKmVPaBpM5jKb8fPAg==/com.microsoft.maui-JfRo8RWSDJaNtJuBa0y7_Q==/lib/x86/libmonosgen-2.0.so (mono_class_from_name_checked_aux+735) (BuildId: b67e93dd750dafdd6f65f408b021b6a3a74868ac)
05-14 08:06:12.849 31274 31274 F DEBUG   :       #14 pc 00037989  /data/app/~~rMrkpKmVPaBpM5jKb8fPAg==/com.microsoft.maui-JfRo8RWSDJaNtJuBa0y7_Q==/lib/x86/libmonosgen-2.0.so (mono_class_from_name_checked+73) (BuildId: b67e93dd750dafdd6f65f408b021b6a3a74868ac)
05-14 08:06:12.849 31274 31274 F DEBUG   :       #15 pc 000cc5f4  /data/app/~~rMrkpKmVPaBpM5jKb8fPAg==/com.microsoft.maui-JfRo8RWSDJaNtJuBa0y7_Q==/lib/x86/libmonosgen-2.0.so (mono_reflection_get_type_internal+132) (BuildId: b67e93dd750dafdd6f65f408b021b6a3a74868ac)
05-14 08:06:12.849 31274 31274 F DEBUG   :       #16 pc 000c9bce  /data/app/~~rMrkpKmVPaBpM5jKb8fPAg==/com.microsoft.maui-JfRo8RWSDJaNtJuBa0y7_Q==/lib/x86/libmonosgen-2.0.so (mono_reflection_get_type_with_rootimage+126) (BuildId: b67e93dd750dafdd6f65f408b021b6a3a74868ac)
05-14 08:06:12.849 31274 31274 F DEBUG   :       #17 pc 000ca204  /data/app/~~rMrkpKmVPaBpM5jKb8fPAg==/com.microsoft.maui-JfRo8RWSDJaNtJuBa0y7_Q==/lib/x86/libmonosgen-2.0.so (_mono_reflection_get_type_from_info+292) (BuildId: b67e93dd750dafdd6f65f408b021b6a3a74868ac)
05-14 08:06:12.849 31274 31274 F DEBUG   :       #18 pc 000ca06e  /data/app/~~rMrkpKmVPaBpM5jKb8fPAg==/com.microsoft.maui-JfRo8RWSDJaNtJuBa0y7_Q==/lib/x86/libmonosgen-2.0.so (mono_reflection_type_from_name_checked+334) (BuildId: b67e93dd750dafdd6f65f408b021b6a3a74868ac)
05-14 08:06:12.849 31274 31274 F DEBUG   :       #19 pc 000c9f01  /data/app/~~rMrkpKmVPaBpM5jKb8fPAg==/com.microsoft.maui-JfRo8RWSDJaNtJuBa0y7_Q==/lib/x86/libmonosgen-2.0.so (mono_reflection_type_from_name+49) (BuildId: b67e93dd750dafdd6f65f408b021b6a3a74868ac)
05-14 08:06:12.849 31274 31274 F DEBUG   :       #20 pc 0001b40b  /data/app/~~rMrkpKmVPaBpM5jKb8fPAg==/com.microsoft.maui-JfRo8RWSDJaNtJuBa0y7_Q==/lib/x86/libmonodroid.so (xamarin::android::internal::EmbeddedAssemblies::typemap_java_to_managed(char const*)+427) (BuildId: 9726f32ad5f8fa5e7c5762baf2f6e3294da41cc1)
05-14 08:06:12.849 31274 31274 F DEBUG   :       dotnet#21 pc 0001b551  /data/app/~~rMrkpKmVPaBpM5jKb8fPAg==/com.microsoft.maui-JfRo8RWSDJaNtJuBa0y7_Q==/lib/x86/libmonodroid.so (xamarin::android::internal::EmbeddedAssemblies::typemap_java_to_managed(_MonoString*)+113) (BuildId: 9726f32ad5f8fa5e7c5762baf2f6e3294da41cc1)
05-14 08:06:12.849 31274 31274 F DEBUG   :       dotnet#22 pc 000211a7  /data/app/~~rMrkpKmVPaBpM5jKb8fPAg==/com.microsoft.maui-JfRo8RWSDJaNtJuBa0y7_Q==/lib/x86/libmonodroid.so (xamarin::android::internal::MonodroidRuntime::typemap_java_to_managed(_MonoString*)+39) (BuildId: 9726f32ad5f8fa5e7c5762baf2f6e3294da41cc1)
```
GrabYourPitchforks pushed a commit that referenced this pull request Jun 30, 2022
* Initial implementation for contract customization

fix build errors

Move converter rooting to DefaultJsonTypeInfoResolver so that it can be used standalone

Fix ConfigurationList.IsReadOnly

Minor refactorings (#1)

* Makes the following changes:

* Move singleton initialization for DefaultTypeInfoResolver behind a static property.
* Consolidate JsonSerializerContext & IJsonTypeInfoResolver values to a single field.
* Move reflection fallback logic away from JsonSerializerContext and into JsonSerializerOptions

* Update src/libraries/System.Text.Json/src/System/Text/Json/Serialization/JsonSerializerOptions.cs

* remove testing of removed field

Simplify the JsonTypeInfo.CreateObject implemenetation (#2)

* Simplify the JsonTypeInfo.CreateObject implemenetation

* Update src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Metadata/JsonTypeInfoOfT.cs

* Update src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Metadata/JsonTypeInfoOfT.cs

Co-authored-by: Krzysztof Wicher <[email protected]>

Co-authored-by: Krzysztof Wicher <[email protected]>

Tests and fixes for JsonTypeInfoKind.None

TypeInfo type mismatch tests

Allow setting NumberHandling on JsonTypeInfoKind.None

test resolver returning wrong type of options

JsonTypeInfo/JsonPropertyInfo mutability tests

rename test file

Move default converter rooting responsibility behind DefaultJsonTypeInfoResolver (#3)

* Move default converter rooting responsibility behind DefaultJsonTypeInfoResolver

* address feedback

Add simple test for using JsonTypeInfo<T> with APIs directly taking it

fix and tests for untyped/typed CreateObject

uncomment test cases, remove todo

More tests and tiny fixes

Add a JsonTypeInfoResolver.Combine test for JsonSerializerContext (#4)

* Fix JsonTypeInfoResolver.Combine for JsonSerializerContext

* Break up failing test

Fix simple scenarios for combining contexts (#6)

* Fix simple scenarios for combining contexts

* feedback

JsonSerializerContext combine test with different camel casing

Remove unneeded virtual calls & branching when accessing Get & Set delegates (#7)

JsonPropertyInfo tests everything minus ShouldSerialize & NumberHandling

Update src/libraries/System.Text.Json/src/System/Text/Json/Serialization/JsonConverterOfT.cs

Update src/libraries/System.Text.Json/src/System/Text/Json/Serialization/JsonConverterOfT.cs

throw InvalidOperationException rather than ArgumentNullException for source gen when PropertyInfo.Name is assigned through JsonPropertyInfoValues

tests for duplicated property names and JsonPropertyInfo.NumberHandling

Add tests for NumberHandling and failing tests for ShouldSerialize

disable the failing test and add extra checks

disable remainder of the failing ShouldSerialize tests, fix working one

Fix ShouldSerialize and IgnoreCondition interop

Add failing tests for CreateObject + parametrized constructors

Fix CreateObject support for JsonConstructor types (#10)

* Fix CreateObject support for JsonConstructor types

* address feedback

Make contexts more combinator friendly (#9)

* Make contexts more combinator friendly

* remove converter cache

* redesign test to account for JsonConstructorAttribute

* Combine unit tests

* address feedback

* Add acceptance tests for DataContract attributes & Specified pattern (#11)

* Add private field serialization acceptance test (#13)

* tests, PR feedback (#14)

* PR feedback and extra tests

* Shorten class name, remove incorrect check (not true for polimorphic cases)

* Make parameter matching for custom properties map property Name with parameter (#16)

* Test static initialization with JsonTypeInfo (#17)

* Fix test failures and proper fix this time (#18)

* Fix test failures and proper fix this time

* reinstate ActiveIssueAttribute

* PR feedback and adjust couple of tests which don't set TypeInfoResolver

* fix IAsyncEnumerable tests

* Lock JsonSerializerOptions in JsonTypeInfo.EnsureConfigured()

Co-authored-by: Eirik Tsarpalis <[email protected]>

Co-authored-by: Eirik Tsarpalis <[email protected]>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

1 participant