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

Improve performance of async deserialization #130

Merged
merged 24 commits into from
Dec 10, 2024
Merged

Improve performance of async deserialization #130

merged 24 commits into from
Dec 10, 2024

Conversation

AArnott
Copy link
Owner

@AArnott AArnott commented Nov 29, 2024

The problem with async deserialization perf is that async methods cannot pass ref struct around. Passing around ReadOnlySequence<byte> to every single read method kills perf.

In this change, I radically restructure MessagePackReader to create MessagePackStreamingReader which is a ref struct with all synchronous methods except for one that asynchronously fetches more bytes from an arbitrary source. Doing so destroys the ref struct so the caller has to recreate it. I've done what I can to minimize the syntax overhead on the caller, but it's likely to never be as convenient as the MessagePackReader. But it's more convenient and complete than the old async model.

If we use the MessagePackStreamingReader instead of MessagePackReader in synchronous converters, the perf is almost as good, but not quite (at least not yet).
My dream would be that a converter can be written using the new async pattern and have perf that is competitive with the old sync pattern such that implementing a sync converter and an async converter is no longer necessary in any case.

Other benefits of the new model:

  • Support for beginning deserialization before all bytes have downloaded even in synchronous mode.
  • More APIs that avoid exceptions.
  • No mandatory allocation for async deserialization.

Before


BenchmarkDotNet v0.14.0, Windows 11 (10.0.22631.4460/23H2/2023Update/SunValley3)
AMD Ryzen 9 3950X, 1 CPU, 32 logical and 16 physical cores
.NET SDK 9.0.101
  [Host]   : .NET 8.0.11 (8.0.1124.51707), X64 RyuJIT AVX2
  ShortRun : .NET 8.0.11 (8.0.1124.51707), X64 RyuJIT AVX2

Job=ShortRun  IterationCount=3  LaunchCount=1  
WarmupCount=3  

Method Categories Mean Error StdDev Ratio RatioSD Gen0 Allocated Alloc Ratio
DeserializeAsArrayInit array-init,Deserialize 131.6 ns 15.44 ns 0.85 ns 1.00 0.01 0.0095 80 B 1.00
DeserializeAsyncAsArrayInit array-init,Deserialize 664.5 ns 114.58 ns 6.28 ns 5.05 0.05 0.0257 216 B 2.70
SerializeAsArrayInit array-init,Serialize 216.1 ns 28.18 ns 1.54 ns 1.00 0.01 - - NA
SerializeAsyncAsArrayInit array-init,Serialize 294.1 ns 38.89 ns 2.13 ns 1.36 0.01 0.0067 56 B NA
DeserializeAsArray array,Deserialize 132.8 ns 6.20 ns 0.34 ns 1.00 0.00 0.0095 80 B 1.00
DeserializeAsyncAsArray array,Deserialize 711.1 ns 415.59 ns 22.78 ns 5.36 0.15 0.0257 216 B 2.70
SerializeAsArray array,Serialize 211.0 ns 15.74 ns 0.86 ns 1.00 0.01 - - NA
SerializeAsyncAsArray array,Serialize 289.7 ns 9.41 ns 0.52 ns 1.37 0.01 0.0067 56 B NA
DeserializeMapInit map-init,Deserialize 210.6 ns 92.10 ns 5.05 ns 1.00 0.03 0.0095 80 B 1.00
DeserializeAsyncMapInit map-init,Deserialize 644.5 ns 120.17 ns 6.59 ns 3.06 0.07 0.0257 216 B 2.70
SerializeMapInit map-init,Serialize 280.8 ns 13.08 ns 0.72 ns 1.00 0.00 - - NA
SerializeAsyncMapInit map-init,Serialize 288.8 ns 11.44 ns 0.63 ns 1.03 0.00 0.0067 56 B NA
DeserializeMap map,Deserialize 247.9 ns 20.79 ns 1.14 ns 1.00 0.01 0.0095 80 B 1.00
DeserializeAsyncMap map,Deserialize 751.7 ns 388.72 ns 21.31 ns 3.03 0.08 0.0257 216 B 2.70
SerializeMap map,Serialize 303.7 ns 84.11 ns 4.61 ns 1.00 0.02 - - NA
SerializeAsyncMap map,Serialize 329.2 ns 90.39 ns 4.95 ns 1.08 0.02 0.0067 56 B NA

After


BenchmarkDotNet v0.14.0, Windows 11 (10.0.22631.4460/23H2/2023Update/SunValley3)
AMD Ryzen 9 3950X, 1 CPU, 32 logical and 16 physical cores
.NET SDK 9.0.101
  [Host]   : .NET 8.0.11 (8.0.1124.51707), X64 RyuJIT AVX2
  ShortRun : .NET 8.0.11 (8.0.1124.51707), X64 RyuJIT AVX2

Job=ShortRun  IterationCount=3  LaunchCount=1  
WarmupCount=3  

Method Categories Mean Error StdDev Ratio RatioSD Gen0 Allocated Alloc Ratio
DeserializeAsArrayInit array-init,Deserialize 138.7 ns 20.46 ns 1.12 ns 1.00 0.01 0.0095 80 B 1.00
DeserializeAsyncAsArrayInit array-init,Deserialize 527.6 ns 22.42 ns 1.23 ns 3.80 0.03 0.0286 240 B 3.00
SerializeAsArrayInit array-init,Serialize 212.7 ns 15.80 ns 0.87 ns 1.00 0.00 - - NA
SerializeAsyncAsArrayInit array-init,Serialize 289.1 ns 48.60 ns 2.66 ns 1.36 0.01 0.0067 56 B NA
DeserializeAsArray array,Deserialize 133.7 ns 11.03 ns 0.60 ns 1.00 0.01 0.0095 80 B 1.00
DeserializeAsyncAsArray array,Deserialize 556.2 ns 66.32 ns 3.64 ns 4.16 0.03 0.0286 240 B 3.00
SerializeAsArray array,Serialize 217.2 ns 35.21 ns 1.93 ns 1.00 0.01 - - NA
SerializeAsyncAsArray array,Serialize 302.2 ns 15.02 ns 0.82 ns 1.39 0.01 0.0067 56 B NA
DeserializeMapInit map-init,Deserialize 229.9 ns 19.49 ns 1.07 ns 1.00 0.01 0.0095 80 B 1.00
DeserializeAsyncMapInit map-init,Deserialize 612.3 ns 19.17 ns 1.05 ns 2.66 0.01 0.0286 240 B 3.00
SerializeMapInit map-init,Serialize 278.2 ns 117.33 ns 6.43 ns 1.00 0.03 - - NA
SerializeAsyncMapInit map-init,Serialize 301.7 ns 16.10 ns 0.88 ns 1.09 0.02 0.0067 56 B NA
DeserializeMap map,Deserialize 245.3 ns 7.66 ns 0.42 ns 1.00 0.00 0.0095 80 B 1.00
DeserializeAsyncMap map,Deserialize 641.3 ns 15.93 ns 0.87 ns 2.61 0.00 0.0286 240 B 3.00
SerializeMap map,Serialize 268.9 ns 4.70 ns 0.26 ns 1.00 0.00 - - NA
SerializeAsyncMap map,Serialize 281.9 ns 67.71 ns 3.71 ns 1.05 0.01 0.0067 56 B NA

@AArnott AArnott added the enhancement New feature or request label Nov 29, 2024
@AArnott AArnott marked this pull request as ready for review December 10, 2024 00:03
@AArnott AArnott enabled auto-merge (squash) December 10, 2024 00:19
@AArnott AArnott disabled auto-merge December 10, 2024 00:20
@AArnott AArnott merged commit aaa2227 into main Dec 10, 2024
2 checks passed
@AArnott AArnott deleted the asyncreader branch December 10, 2024 00:40
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants