Skip to content

Conversation

@habbes
Copy link
Contributor

@habbes habbes commented Jan 30, 2024

Fix #97628

Benchmark results

I used the dotnet-performance benchmarks with *WriteJson<BinaryData>* as the filter.

Results before


BenchmarkDotNet v0.13.11-nightly.20231126.107, Windows 11 (10.0.22621.3007/22H2/2022Update/SunValley2)
Intel Xeon W-2123 CPU 3.60GHz, 1 CPU, 8 logical and 4 physical cores
.NET SDK 9.0.100-preview.2.24078.1
  [Host]     : .NET 9.0.0 (9.0.24.7604), X64 RyuJIT AVX-512F+CD+BW+DQ+VL
  Job-SULXBD : .NET 9.0.0 (42.42.42.42424), X64 RyuJIT AVX-512F+CD+BW+DQ+VL

PowerPlanMode=00000000-0000-0000-0000-000000000000  Toolchain=CoreRun  IterationTime=250.0000 ms
MaxIterationCount=20  MinIterationCount=15  WarmupCount=1

Method Mode Mean Error StdDev Median Min Max Gen0 Allocated
SerializeToString Reflection 555.3 ns 15.82 ns 18.22 ns 557.8 ns 520.9 ns 589.0 ns 0.6458 2792 B
SerializeToUtf8Bytes Reflection 407.3 ns 12.77 ns 14.19 ns 405.9 ns 385.1 ns 431.4 ns 0.3258 1408 B
SerializeToWriter Reflection 220.6 ns 8.85 ns 9.84 ns 218.4 ns 206.0 ns 240.0 ns - -
SerializeObjectProperty Reflection 768.6 ns 90.76 ns 97.11 ns 733.1 ns 667.4 ns 966.3 ns 0.7232 3120 B
SerializeToString SourceGen 508.6 ns 20.22 ns 22.48 ns 504.3 ns 474.5 ns 553.1 ns 0.6465 2792 B
SerializeToUtf8Bytes SourceGen 350.0 ns 9.83 ns 10.92 ns 348.9 ns 335.8 ns 374.5 ns 0.3263 1408 B
SerializeToWriter SourceGen 201.0 ns 9.64 ns 10.72 ns 197.1 ns 186.6 ns 218.5 ns - -
SerializeObjectProperty SourceGen 711.5 ns 46.65 ns 51.86 ns 706.9 ns 639.6 ns 823.8 ns 0.7232 3120 B
SerializeToStream Reflection 397.4 ns 15.65 ns 17.40 ns 394.9 ns 378.7 ns 438.7 ns 0.0349 152 B
SerializeToStream SourceGen 329.3 ns 15.47 ns 17.19 ns 325.2 ns 307.9 ns 366.7 ns 0.0065 32 B

Results After


BenchmarkDotNet v0.13.11-nightly.20231126.107, Windows 11 (10.0.22621.3007/22H2/2022Update/SunValley2)
Intel Xeon W-2123 CPU 3.60GHz, 1 CPU, 8 logical and 4 physical cores
.NET SDK 9.0.100-preview.2.24078.1
  [Host]     : .NET 9.0.0 (9.0.24.7604), X64 RyuJIT AVX-512F+CD+BW+DQ+VL
  Job-ZXNFMG : .NET 9.0.0 (42.42.42.42424), X64 RyuJIT AVX-512F+CD+BW+DQ+VL

PowerPlanMode=00000000-0000-0000-0000-000000000000  Toolchain=CoreRun  IterationTime=250.0000 ms
MaxIterationCount=20  MinIterationCount=15  WarmupCount=1

Method Mode Mean Error StdDev Median Min Max Gen0 Allocated
SerializeToString Reflection 485.4 ns 3.89 ns 3.04 ns 485.3 ns 480.0 ns 489.8 ns 0.6469 2792 B
SerializeToUtf8Bytes Reflection 346.7 ns 4.64 ns 4.11 ns 346.3 ns 341.8 ns 355.1 ns 0.3253 1408 B
SerializeToWriter Reflection 173.0 ns 5.06 ns 5.41 ns 173.5 ns 163.4 ns 182.3 ns - -
SerializeObjectProperty Reflection 643.4 ns 13.66 ns 15.73 ns 639.5 ns 626.3 ns 674.2 ns 0.7212 3120 B
SerializeToString SourceGen 456.6 ns 8.78 ns 8.62 ns 454.0 ns 447.3 ns 473.0 ns 0.6468 2792 B
SerializeToUtf8Bytes SourceGen 297.6 ns 5.76 ns 5.11 ns 295.4 ns 293.2 ns 310.0 ns 0.3259 1408 B
SerializeToWriter SourceGen 134.1 ns 0.69 ns 0.54 ns 134.1 ns 133.2 ns 134.8 ns - -
SerializeObjectProperty SourceGen 634.5 ns 25.21 ns 29.03 ns 644.0 ns 591.5 ns 684.3 ns 0.7225 3120 B
SerializeToStream Reflection 344.3 ns 9.61 ns 10.68 ns 338.7 ns 335.3 ns 369.1 ns 0.0343 152 B
SerializeToStream SourceGen 277.8 ns 1.28 ns 1.07 ns 277.6 ns 276.4 ns 279.5 ns 0.0067 32 B

Profiler results

While the benchmark so some speedup, they don't show any improvements in allocations. Either because the buffers are too small that they're allocated on the stack, or, since the buffer are rented, subsequent writes will likely reuse the same buffer and it probably won't show up in the out-of-process benchmark results. To demonstrate the allocations improvements, I created a sample repo that performs concurrent: https://github.com/habbes/experiments/tree/master/WriteBase64JsonMemoryRepro

CPU usage before

Span<byte>.CopyTo() and Buffer._Memmove() contribute to a relative high % of CPU use

image

CPU usage after

No traces of the buffer copies

image

Allocations before

Several large byte[] allocations from SharedArrayPool<byte>.Rent under Base64EncodeAndWrite.

image

Allocations after

No traces of Base64EncodeAndWrite in allocations

image

@ghost ghost added area-System.Text.Json community-contribution Indicates that the PR has been added by a community member labels Jan 30, 2024
@ghost
Copy link

ghost commented Jan 30, 2024

Tagging subscribers to this area: @dotnet/area-system-text-json, @gregsdennis
See info in area-owners.md if you want to be subscribed.

Issue Details

Fix #97628

Author: habbes
Assignees: -
Labels:

area-System.Text.Json

Milestone: -

@habbes habbes marked this pull request as ready for review January 30, 2024 07:59
Copy link
Member

@stephentoub stephentoub left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM. Thanks.

@eiriktsarpalis eiriktsarpalis added the tenet-performance Performance related issue label Jan 30, 2024
Copy link
Member

@eiriktsarpalis eiriktsarpalis left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks!

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

area-System.Text.Json community-contribution Indicates that the PR has been added by a community member tenet-performance Performance related issue

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Unnecessary buffer allocations when writing base64 values with Utf8JsonWriter.

3 participants