Skip to content

Comments

Add an IArrayPool<char> implementation for vendored Newtonsoft.JSON#8228

Open
andrewlock wants to merge 3 commits intoandrew/add-streaming-base64-decoderfrom
andrew/use-json-array-pool
Open

Add an IArrayPool<char> implementation for vendored Newtonsoft.JSON#8228
andrewlock wants to merge 3 commits intoandrew/add-streaming-base64-decoderfrom
andrew/use-json-array-pool

Conversation

@andrewlock
Copy link
Member

Summary of changes

Adds a simple IArrayPool<char> for use by Newtonsoft.JSON, and uses it everywhere we can

Reason for change

Newtonsoft.JSON fundamentally works with .NET's char type (UTF-16), (as opposed to System.Text.Json which works with UTF-8 where it can). To do so, it needs to create a bunch of char[] instances to use as buffers.

The JsonTextReader and JsonTextWriter abstractions allow plugging in an IArrayPool<char> implementation, and luckily this matches (pretty much exactly) the API exposed by the ArrayPool in corelib (+ vendored), so it's easy to implement.

This should help alleviate some GC pressure, as we currently do a fair amount of serializing and deserializing.

Implementation details

Pretty simple:

  • Implement IArrayPool<char> using ArrayPool<char>.Shared
  • Get 🤖 to find everywhere that we could use it (JsonTextReader and JsonTextWriter) and initialize
  • Fix some cases where these weren't being disposed.

Warning

It's important that we do dispose these, so that the arrays are correctly returned to the pool, so that we don't leak memory

There are actually other places we can update too, as this PR doesn't cover the common JsonConvert.Serialize() etc, but I'll follow up with those in a separate PR.

Test coverage

All the existing tests should pass. I worked on this as part of general perf work on remote config, so the results are a bit fuzzy (as I can't remember if it includes the savings from #8226 as well), but the results are pretty conclusive, especially for big payloads 😅

Method size Mean Error Allocated
DeserializeResponse_Original Small 22.71 ns 2,076.771 ns 23.83 KB
DeserializeResponse_Updated Small 13.70 us 0.186 us 17.23 KB
DeserializeResponse_Original Big 1,953.04 us 58,665.219 ns 2,343.26 KB
DeserializeResponse_Updated Big 614.46 us 11.988 us 252.37 KB

Other details

https://datadoghq.atlassian.net/browse/LANGPLAT-940

Discovered this while exploring remote config optimizations

@andrewlock andrewlock requested a review from a team as a code owner February 20, 2026 17:52
@andrewlock andrewlock added the type:performance Performance, speed, latency, resource usage (CPU, memory) label Feb 20, 2026
@andrewlock andrewlock requested review from a team as code owners February 20, 2026 17:52
Copy link

@chatgpt-codex-connector chatgpt-codex-connector bot left a comment

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: ad20bb98cd

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

@andrewlock andrewlock force-pushed the andrew/use-json-array-pool branch from ad20bb9 to 7c8bf45 Compare February 20, 2026 18:00
Comment on lines +135 to +136
// Default values for StreamReader, but with leaveOpen:true
using var streamReader = new StreamReader(stream, Encoding.UTF8, detectEncodingFromByteOrderMarks: true, bufferSize: 1024, leaveOpen: true);
Copy link
Member Author

Choose a reason for hiding this comment

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

Previously this wasn't disposing the StreamReader, presumably because that closes the MemoryStream by default. I think technically that was ok, but seemed better to be explicit here, especially as we now have to dispose the JsonTextReader, and don't want there to be any ambiguity around it

scopeMembers.Add(result);

var reader = new JsonTextReader(new StringReader(expressionJson));
using var reader = new JsonTextReader(new StringReader(expressionJson)) { ArrayPool = JsonArrayPool.Shared };
Copy link
Member Author

Choose a reason for hiding this comment

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

I'm pretty sure this was just an accidental miss of the using

@@ -276,11 +277,11 @@ internal static void WriteValue(JsonWriter writer, object? value)
}

internal static JsonTextWriter GetJsonWriter(StringBuilder builder)
Copy link
Member Author

Choose a reason for hiding this comment

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

The callers "own" and handle disposing the returned object

@andrewlock andrewlock force-pushed the andrew/use-json-array-pool branch from 7c8bf45 to 947d1d6 Compare February 20, 2026 18:04
@pr-commenter
Copy link

pr-commenter bot commented Feb 20, 2026

Benchmarks

Benchmark execution time: 2026-02-23 13:22:14

Comparing candidate commit dd07383 in PR branch andrew/use-json-array-pool with baseline commit caa8d05 in branch master.

Found 6 performance improvements and 8 performance regressions! Performance is the same for 158 metrics, 20 unstable metrics.

scenario:Benchmarks.Trace.ActivityBenchmark.StartStopWithChild net6.0

  • 🟥 throughput [-9235.421op/s; -7366.936op/s] or [-7.265%; -5.795%]

scenario:Benchmarks.Trace.AgentWriterBenchmark.WriteAndFlushEnrichedTraces net6.0

  • 🟥 execution_time [+96.449ms; +96.588ms] or [+91.416%; +91.548%]

scenario:Benchmarks.Trace.Asm.AppSecBodyBenchmark.AllCycleMoreComplexBody net6.0

  • 🟥 execution_time [+10.682ms; +16.155ms] or [+5.345%; +8.084%]

scenario:Benchmarks.Trace.Asm.AppSecBodyBenchmark.ObjectExtractorSimpleBody net6.0

  • 🟩 execution_time [-19.812ms; -15.226ms] or [-8.991%; -6.910%]

scenario:Benchmarks.Trace.Asm.AppSecBodyBenchmark.ObjectExtractorSimpleBody netcoreapp3.1

  • 🟥 execution_time [+18.807ms; +23.076ms] or [+9.525%; +11.687%]

scenario:Benchmarks.Trace.Asm.AppSecEncoderBenchmark.EncodeLegacyArgs netcoreapp3.1

  • 🟩 execution_time [-22.252ms; -21.693ms] or [-10.984%; -10.708%]

scenario:Benchmarks.Trace.CIVisibilityProtocolWriterBenchmark.WriteAndFlushEnrichedTraces net6.0

  • 🟥 execution_time [+41.294ms; +45.334ms] or [+26.454%; +29.041%]
  • 🟥 throughput [-209.701op/s; -180.335op/s] or [-12.787%; -10.996%]

scenario:Benchmarks.Trace.CharSliceBenchmark.OptimizedCharSlice netcoreapp3.1

  • 🟥 throughput [-149.666op/s; -108.125op/s] or [-30.272%; -21.870%]

scenario:Benchmarks.Trace.CharSliceBenchmark.OptimizedCharSliceWithPool net6.0

  • 🟩 execution_time [-84.077µs; -80.003µs] or [-7.438%; -7.078%]
  • 🟩 throughput [+67.569op/s; +70.890op/s] or [+7.638%; +8.013%]

scenario:Benchmarks.Trace.Iast.StringAspectsBenchmark.StringConcatBenchmark netcoreapp3.1

  • 🟩 throughput [+2042.111op/s; +3428.901op/s] or [+11.653%; +19.567%]

scenario:Benchmarks.Trace.Log4netBenchmark.EnrichedLog netcoreapp3.1

  • 🟩 execution_time [-30.064ms; -25.353ms] or [-14.798%; -12.479%]

scenario:Benchmarks.Trace.SpanBenchmark.StartFinishTwoScopes netcoreapp3.1

  • 🟥 execution_time [+14.794ms; +20.655ms] or [+7.567%; +10.565%]

@dd-trace-dotnet-ci-bot
Copy link

dd-trace-dotnet-ci-bot bot commented Feb 20, 2026

Execution-Time Benchmarks Report ⏱️

Execution-time results for samples comparing This PR (8228) and master.

✅ No regressions detected - check the details below

Full Metrics Comparison

FakeDbCommand

Metric Master (Mean ± 95% CI) Current (Mean ± 95% CI) Change Status
.NET Framework 4.8 - Baseline
duration74.24 ± (74.53 - 75.20) ms74.15 ± (74.12 - 74.40) ms-0.1%
.NET Framework 4.8 - Bailout
duration79.19 ± (79.09 - 79.50) ms77.94 ± (77.82 - 78.20) ms-1.6%
.NET Framework 4.8 - CallTarget+Inlining+NGEN
duration1073.55 ± (1075.37 - 1080.96) ms1069.89 ± (1070.12 - 1075.83) ms-0.3%
.NET Core 3.1 - Baseline
process.internal_duration_ms22.79 ± (22.73 - 22.84) ms22.62 ± (22.57 - 22.67) ms-0.7%
process.time_to_main_ms85.77 ± (85.57 - 85.96) ms85.15 ± (84.96 - 85.33) ms-0.7%
runtime.dotnet.exceptions.count0 ± (0 - 0)0 ± (0 - 0)+0.0%
runtime.dotnet.mem.committed10.91 ± (10.90 - 10.91) MB10.86 ± (10.85 - 10.86) MB-0.4%
runtime.dotnet.threads.count12 ± (12 - 12)12 ± (12 - 12)+0.0%
.NET Core 3.1 - Bailout
process.internal_duration_ms22.69 ± (22.64 - 22.73) ms22.64 ± (22.61 - 22.68) ms-0.2%
process.time_to_main_ms87.05 ± (86.82 - 87.29) ms87.03 ± (86.84 - 87.23) ms-0.0%
runtime.dotnet.exceptions.count0 ± (0 - 0)0 ± (0 - 0)+0.0%
runtime.dotnet.mem.committed10.95 ± (10.95 - 10.96) MB10.96 ± (10.96 - 10.97) MB+0.1%✅⬆️
runtime.dotnet.threads.count13 ± (13 - 13)13 ± (13 - 13)+0.0%
.NET Core 3.1 - CallTarget+Inlining+NGEN
process.internal_duration_ms246.05 ± (242.13 - 249.96) ms243.96 ± (238.33 - 249.59) ms-0.8%
process.time_to_main_ms488.72 ± (488.10 - 489.34) ms488.74 ± (487.84 - 489.64) ms+0.0%✅⬆️
runtime.dotnet.exceptions.count0 ± (0 - 0)0 ± (0 - 0)+0.0%
runtime.dotnet.mem.committed47.65 ± (47.63 - 47.67) MB47.67 ± (47.64 - 47.70) MB+0.0%✅⬆️
runtime.dotnet.threads.count28 ± (28 - 28)28 ± (28 - 28)-0.2%
.NET 6 - Baseline
process.internal_duration_ms21.54 ± (21.49 - 21.58) ms21.37 ± (21.33 - 21.41) ms-0.8%
process.time_to_main_ms74.73 ± (74.57 - 74.89) ms74.75 ± (74.60 - 74.91) ms+0.0%✅⬆️
runtime.dotnet.exceptions.count0 ± (0 - 0)0 ± (0 - 0)+0.0%
runtime.dotnet.mem.committed10.62 ± (10.62 - 10.63) MB10.64 ± (10.64 - 10.64) MB+0.2%✅⬆️
runtime.dotnet.threads.count10 ± (10 - 10)10 ± (10 - 10)+0.0%
.NET 6 - Bailout
process.internal_duration_ms21.56 ± (21.51 - 21.61) ms21.33 ± (21.30 - 21.37) ms-1.1%
process.time_to_main_ms76.37 ± (76.18 - 76.55) ms75.51 ± (75.38 - 75.65) ms-1.1%
runtime.dotnet.exceptions.count0 ± (0 - 0)0 ± (0 - 0)+0.0%
runtime.dotnet.mem.committed10.68 ± (10.68 - 10.69) MB10.73 ± (10.72 - 10.73) MB+0.4%✅⬆️
runtime.dotnet.threads.count11 ± (11 - 11)11 ± (11 - 11)+0.0%
.NET 6 - CallTarget+Inlining+NGEN
process.internal_duration_ms254.87 ± (251.70 - 258.05) ms250.54 ± (246.86 - 254.23) ms-1.7%
process.time_to_main_ms470.00 ± (469.40 - 470.60) ms474.68 ± (473.76 - 475.60) ms+1.0%✅⬆️
runtime.dotnet.exceptions.count0 ± (0 - 0)0 ± (0 - 0)+0.0%
runtime.dotnet.mem.committed48.38 ± (48.36 - 48.41) MB48.32 ± (48.29 - 48.34) MB-0.1%
runtime.dotnet.threads.count28 ± (28 - 28)28 ± (28 - 28)-0.1%
.NET 8 - Baseline
process.internal_duration_ms19.68 ± (19.63 - 19.72) ms19.73 ± (19.69 - 19.77) ms+0.3%✅⬆️
process.time_to_main_ms74.22 ± (74.05 - 74.39) ms74.14 ± (73.99 - 74.29) ms-0.1%
runtime.dotnet.exceptions.count0 ± (0 - 0)0 ± (0 - 0)+0.0%
runtime.dotnet.mem.committed7.67 ± (7.66 - 7.67) MB7.68 ± (7.68 - 7.69) MB+0.3%✅⬆️
runtime.dotnet.threads.count10 ± (10 - 10)10 ± (10 - 10)+0.0%
.NET 8 - Bailout
process.internal_duration_ms19.57 ± (19.53 - 19.62) ms19.88 ± (19.83 - 19.92) ms+1.5%✅⬆️
process.time_to_main_ms75.23 ± (75.05 - 75.42) ms75.94 ± (75.78 - 76.11) ms+0.9%✅⬆️
runtime.dotnet.exceptions.count0 ± (0 - 0)0 ± (0 - 0)+0.0%
runtime.dotnet.mem.committed7.74 ± (7.73 - 7.75) MB7.73 ± (7.73 - 7.74) MB-0.1%
runtime.dotnet.threads.count11 ± (11 - 11)11 ± (11 - 11)+0.0%
.NET 8 - CallTarget+Inlining+NGEN
process.internal_duration_ms187.71 ± (186.75 - 188.67) ms189.85 ± (189.18 - 190.51) ms+1.1%✅⬆️
process.time_to_main_ms449.88 ± (449.16 - 450.60) ms453.60 ± (453.07 - 454.13) ms+0.8%✅⬆️
runtime.dotnet.exceptions.count0 ± (0 - 0)0 ± (0 - 0)+0.0%
runtime.dotnet.mem.committed36.06 ± (36.01 - 36.11) MB36.09 ± (36.06 - 36.12) MB+0.1%✅⬆️
runtime.dotnet.threads.count27 ± (27 - 27)27 ± (27 - 27)-0.1%

HttpMessageHandler

Metric Master (Mean ± 95% CI) Current (Mean ± 95% CI) Change Status
.NET Framework 4.8 - Baseline
duration191.52 ± (191.14 - 192.06) ms191.97 ± (191.74 - 192.33) ms+0.2%✅⬆️
.NET Framework 4.8 - Bailout
duration195.74 ± (195.28 - 195.97) ms194.86 ± (194.80 - 195.36) ms-0.4%
.NET Framework 4.8 - CallTarget+Inlining+NGEN
duration1132.12 ± (1135.03 - 1143.27) ms1132.83 ± (1132.96 - 1140.39) ms+0.1%✅⬆️
.NET Core 3.1 - Baseline
process.internal_duration_ms186.50 ± (186.12 - 186.87) ms186.35 ± (186.04 - 186.66) ms-0.1%
process.time_to_main_ms80.88 ± (80.63 - 81.12) ms80.62 ± (80.37 - 80.86) ms-0.3%
runtime.dotnet.exceptions.count3 ± (3 - 3)3 ± (3 - 3)+0.0%
runtime.dotnet.mem.committed16.17 ± (16.14 - 16.20) MB16.23 ± (16.19 - 16.27) MB+0.4%✅⬆️
runtime.dotnet.threads.count19 ± (19 - 20)20 ± (19 - 20)+0.5%✅⬆️
.NET Core 3.1 - Bailout
process.internal_duration_ms186.25 ± (185.94 - 186.56) ms185.51 ± (185.14 - 185.87) ms-0.4%
process.time_to_main_ms81.98 ± (81.83 - 82.14) ms81.55 ± (81.37 - 81.73) ms-0.5%
runtime.dotnet.exceptions.count3 ± (3 - 3)3 ± (3 - 3)+0.0%
runtime.dotnet.mem.committed16.20 ± (16.17 - 16.23) MB16.23 ± (16.20 - 16.26) MB+0.2%✅⬆️
runtime.dotnet.threads.count21 ± (21 - 21)20 ± (20 - 21)-0.6%
.NET Core 3.1 - CallTarget+Inlining+NGEN
process.internal_duration_ms435.81 ± (433.79 - 437.84) ms436.57 ± (434.33 - 438.81) ms+0.2%✅⬆️
process.time_to_main_ms468.60 ± (468.14 - 469.06) ms470.39 ± (469.83 - 470.94) ms+0.4%✅⬆️
runtime.dotnet.exceptions.count3 ± (3 - 3)3 ± (3 - 3)+0.0%
runtime.dotnet.mem.committed58.14 ± (58.02 - 58.26) MB57.83 ± (57.71 - 57.95) MB-0.5%
runtime.dotnet.threads.count29 ± (29 - 30)29 ± (29 - 29)-0.0%
.NET 6 - Baseline
process.internal_duration_ms189.40 ± (189.07 - 189.74) ms189.89 ± (189.56 - 190.23) ms+0.3%✅⬆️
process.time_to_main_ms69.57 ± (69.39 - 69.74) ms69.66 ± (69.53 - 69.80) ms+0.1%✅⬆️
runtime.dotnet.exceptions.count4 ± (4 - 4)4 ± (4 - 4)+0.0%
runtime.dotnet.mem.committed16.01 ± (15.84 - 16.17) MB15.98 ± (15.82 - 16.14) MB-0.2%
runtime.dotnet.threads.count18 ± (18 - 18)18 ± (18 - 18)-0.1%
.NET 6 - Bailout
process.internal_duration_ms188.99 ± (188.71 - 189.27) ms189.17 ± (188.87 - 189.47) ms+0.1%✅⬆️
process.time_to_main_ms70.37 ± (70.28 - 70.46) ms70.62 ± (70.50 - 70.73) ms+0.3%✅⬆️
runtime.dotnet.exceptions.count4 ± (4 - 4)4 ± (4 - 4)+0.0%
runtime.dotnet.mem.committed16.07 ± (15.92 - 16.23) MB16.03 ± (15.87 - 16.18) MB-0.3%
runtime.dotnet.threads.count19 ± (19 - 19)19 ± (19 - 19)+0.3%✅⬆️
.NET 6 - CallTarget+Inlining+NGEN
process.internal_duration_ms446.43 ± (444.73 - 448.12) ms449.32 ± (447.58 - 451.07) ms+0.6%✅⬆️
process.time_to_main_ms445.04 ± (444.50 - 445.58) ms448.03 ± (447.33 - 448.72) ms+0.7%✅⬆️
runtime.dotnet.exceptions.count4 ± (4 - 4)4 ± (4 - 4)+0.0%
runtime.dotnet.mem.committed58.24 ± (58.12 - 58.35) MB58.20 ± (58.09 - 58.31) MB-0.1%
runtime.dotnet.threads.count29 ± (29 - 29)29 ± (29 - 29)-0.0%
.NET 8 - Baseline
process.internal_duration_ms187.36 ± (186.91 - 187.80) ms187.92 ± (187.63 - 188.20) ms+0.3%✅⬆️
process.time_to_main_ms68.81 ± (68.59 - 69.02) ms69.25 ± (69.08 - 69.42) ms+0.6%✅⬆️
runtime.dotnet.exceptions.count4 ± (4 - 4)4 ± (4 - 4)+0.0%
runtime.dotnet.mem.committed11.59 ± (11.49 - 11.68) MB11.78 ± (11.71 - 11.84) MB+1.6%✅⬆️
runtime.dotnet.threads.count17 ± (17 - 18)18 ± (18 - 18)+4.1%✅⬆️
.NET 8 - Bailout
process.internal_duration_ms187.61 ± (187.32 - 187.90) ms187.65 ± (187.28 - 188.01) ms+0.0%✅⬆️
process.time_to_main_ms69.93 ± (69.80 - 70.07) ms70.48 ± (70.32 - 70.64) ms+0.8%✅⬆️
runtime.dotnet.exceptions.count4 ± (4 - 4)4 ± (4 - 4)+0.0%
runtime.dotnet.mem.committed11.82 ± (11.75 - 11.88) MB11.84 ± (11.81 - 11.87) MB+0.2%✅⬆️
runtime.dotnet.threads.count19 ± (19 - 19)19 ± (19 - 19)+0.8%✅⬆️
.NET 8 - CallTarget+Inlining+NGEN
process.internal_duration_ms360.27 ± (359.05 - 361.49) ms364.95 ± (363.67 - 366.23) ms+1.3%✅⬆️
process.time_to_main_ms427.74 ± (427.09 - 428.39) ms431.90 ± (431.12 - 432.67) ms+1.0%✅⬆️
runtime.dotnet.exceptions.count4 ± (4 - 4)4 ± (4 - 4)+0.0%
runtime.dotnet.mem.committed47.81 ± (47.78 - 47.84) MB47.71 ± (47.67 - 47.76) MB-0.2%
runtime.dotnet.threads.count29 ± (29 - 29)29 ± (29 - 29)+0.4%✅⬆️
Comparison explanation

Execution-time benchmarks measure the whole time it takes to execute a program, and are intended to measure the one-off costs. Cases where the execution time results for the PR are worse than latest master results are highlighted in **red**. The following thresholds were used for comparing the execution times:

  • Welch test with statistical test for significance of 5%
  • Only results indicating a difference greater than 5% and 5 ms are considered.

Note that these results are based on a single point-in-time result for each branch. For full results, see the dashboard.

Graphs show the p99 interval based on the mean and StdDev of the test run, as well as the mean value of the run (shown as a diamond below the graph).

Duration charts
FakeDbCommand (.NET Framework 4.8)
gantt
    title Execution time (ms) FakeDbCommand (.NET Framework 4.8)
    dateFormat  x
    axisFormat %Q
    todayMarker off
    section Baseline
    This PR (8228) - mean (74ms)  : 72, 76
    master - mean (75ms)  : 70, 80

    section Bailout
    This PR (8228) - mean (78ms)  : 76, 80
    master - mean (79ms)  : 76, 83

    section CallTarget+Inlining+NGEN
    This PR (8228) - mean (1,073ms)  : 1031, 1115
    master - mean (1,078ms)  : 1039, 1118

Loading
FakeDbCommand (.NET Core 3.1)
gantt
    title Execution time (ms) FakeDbCommand (.NET Core 3.1)
    dateFormat  x
    axisFormat %Q
    todayMarker off
    section Baseline
    This PR (8228) - mean (115ms)  : 112, 117
    master - mean (116ms)  : 113, 119

    section Bailout
    This PR (8228) - mean (116ms)  : 114, 119
    master - mean (117ms)  : 114, 120

    section CallTarget+Inlining+NGEN
    This PR (8228) - mean (765ms)  : 707, 824
    master - mean (763ms)  : 698, 827

Loading
FakeDbCommand (.NET 6)
gantt
    title Execution time (ms) FakeDbCommand (.NET 6)
    dateFormat  x
    axisFormat %Q
    todayMarker off
    section Baseline
    This PR (8228) - mean (103ms)  : 100, 106
    master - mean (103ms)  : 100, 105

    section Bailout
    This PR (8228) - mean (103ms)  : 101, 105
    master - mean (105ms)  : 102, 107

    section CallTarget+Inlining+NGEN
    This PR (8228) - mean (760ms)  : 688, 831
    master - mean (757ms)  : 701, 813

Loading
FakeDbCommand (.NET 8)
gantt
    title Execution time (ms) FakeDbCommand (.NET 8)
    dateFormat  x
    axisFormat %Q
    todayMarker off
    section Baseline
    This PR (8228) - mean (102ms)  : 99, 105
    master - mean (102ms)  : 99, 105

    section Bailout
    This PR (8228) - mean (104ms)  : 101, 107
    master - mean (103ms)  : 100, 105

    section CallTarget+Inlining+NGEN
    This PR (8228) - mean (677ms)  : 654, 699
    master - mean (667ms)  : 653, 680

Loading
HttpMessageHandler (.NET Framework 4.8)
gantt
    title Execution time (ms) HttpMessageHandler (.NET Framework 4.8)
    dateFormat  x
    axisFormat %Q
    todayMarker off
    section Baseline
    This PR (8228) - mean (192ms)  : 189, 195
    master - mean (192ms)  : 187, 196

    section Bailout
    This PR (8228) - mean (195ms)  : 192, 198
    master - mean (196ms)  : 192, 199

    section CallTarget+Inlining+NGEN
    This PR (8228) - mean (1,137ms)  : 1083, 1190
    master - mean (1,139ms)  : 1080, 1198

Loading
HttpMessageHandler (.NET Core 3.1)
gantt
    title Execution time (ms) HttpMessageHandler (.NET Core 3.1)
    dateFormat  x
    axisFormat %Q
    todayMarker off
    section Baseline
    This PR (8228) - mean (275ms)  : 270, 280
    master - mean (276ms)  : 270, 282

    section Bailout
    This PR (8228) - mean (275ms)  : 269, 280
    master - mean (276ms)  : 272, 280

    section CallTarget+Inlining+NGEN
    This PR (8228) - mean (935ms)  : 890, 979
    master - mean (932ms)  : 888, 976

Loading
HttpMessageHandler (.NET 6)
gantt
    title Execution time (ms) HttpMessageHandler (.NET 6)
    dateFormat  x
    axisFormat %Q
    todayMarker off
    section Baseline
    This PR (8228) - mean (267ms)  : 263, 272
    master - mean (267ms)  : 262, 273

    section Bailout
    This PR (8228) - mean (268ms)  : 264, 272
    master - mean (267ms)  : 263, 271

    section CallTarget+Inlining+NGEN
    This PR (8228) - mean (927ms)  : 908, 946
    master - mean (920ms)  : 896, 944

Loading
HttpMessageHandler (.NET 8)
gantt
    title Execution time (ms) HttpMessageHandler (.NET 8)
    dateFormat  x
    axisFormat %Q
    todayMarker off
    section Baseline
    This PR (8228) - mean (267ms)  : 262, 272
    master - mean (266ms)  : 260, 272

    section Bailout
    This PR (8228) - mean (267ms)  : 263, 272
    master - mean (267ms)  : 263, 271

    section CallTarget+Inlining+NGEN
    This PR (8228) - mean (828ms)  : 807, 848
    master - mean (821ms)  : 803, 838

Loading

@andrewlock andrewlock force-pushed the andrew/use-json-array-pool branch from 947d1d6 to dd07383 Compare February 23, 2026 12:39
@andrewlock andrewlock requested a review from a team as a code owner February 23, 2026 12:39
@andrewlock andrewlock force-pushed the andrew/add-streaming-base64-decoder branch from 33c73ce to 3d24011 Compare February 23, 2026 12:39
Copy link
Contributor

@link04 link04 left a comment

Choose a reason for hiding this comment

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

LGTM

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

Labels

type:performance Performance, speed, latency, resource usage (CPU, memory)

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants