Add an IArrayPool<char> implementation for vendored Newtonsoft.JSON#8228
Add an IArrayPool<char> implementation for vendored Newtonsoft.JSON#8228andrewlock wants to merge 3 commits intoandrew/add-streaming-base64-decoderfrom
IArrayPool<char> implementation for vendored Newtonsoft.JSON#8228Conversation
There was a problem hiding this comment.
💡 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".
tracer/src/Datadog.Trace/ClrProfiler/AutoInstrumentation/AWS/Kinesis/ContextPropagation.cs
Outdated
Show resolved
Hide resolved
ad20bb9 to
7c8bf45
Compare
| // Default values for StreamReader, but with leaveOpen:true | ||
| using var streamReader = new StreamReader(stream, Encoding.UTF8, detectEncodingFromByteOrderMarks: true, bufferSize: 1024, leaveOpen: true); |
There was a problem hiding this comment.
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 }; |
There was a problem hiding this comment.
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) | |||
There was a problem hiding this comment.
The callers "own" and handle disposing the returned object
7c8bf45 to
947d1d6
Compare
BenchmarksBenchmark execution time: 2026-02-23 13:22:14 Comparing candidate commit dd07383 in PR branch 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
scenario:Benchmarks.Trace.AgentWriterBenchmark.WriteAndFlushEnrichedTraces net6.0
scenario:Benchmarks.Trace.Asm.AppSecBodyBenchmark.AllCycleMoreComplexBody net6.0
scenario:Benchmarks.Trace.Asm.AppSecBodyBenchmark.ObjectExtractorSimpleBody net6.0
scenario:Benchmarks.Trace.Asm.AppSecBodyBenchmark.ObjectExtractorSimpleBody netcoreapp3.1
scenario:Benchmarks.Trace.Asm.AppSecEncoderBenchmark.EncodeLegacyArgs netcoreapp3.1
scenario:Benchmarks.Trace.CIVisibilityProtocolWriterBenchmark.WriteAndFlushEnrichedTraces net6.0
scenario:Benchmarks.Trace.CharSliceBenchmark.OptimizedCharSlice netcoreapp3.1
scenario:Benchmarks.Trace.CharSliceBenchmark.OptimizedCharSliceWithPool net6.0
scenario:Benchmarks.Trace.Iast.StringAspectsBenchmark.StringConcatBenchmark netcoreapp3.1
scenario:Benchmarks.Trace.Log4netBenchmark.EnrichedLog netcoreapp3.1
scenario:Benchmarks.Trace.SpanBenchmark.StartFinishTwoScopes netcoreapp3.1
|
Execution-Time Benchmarks Report ⏱️Execution-time results for samples comparing This PR (8228) and master. ✅ No regressions detected - check the details below Full Metrics ComparisonFakeDbCommand
HttpMessageHandler
Comparison explanationExecution-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:
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 chartsFakeDbCommand (.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
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
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
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
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
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
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
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
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
947d1d6 to
dd07383
Compare
33c73ce to
3d24011
Compare
Summary of changes
Adds a simple
IArrayPool<char>for use by Newtonsoft.JSON, and uses it everywhere we canReason for change
Newtonsoft.JSON fundamentally works with .NET's
chartype (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 ofchar[]instances to use as buffers.The
JsonTextReaderandJsonTextWriterabstractions allow plugging in anIArrayPool<char>implementation, and luckily this matches (pretty much exactly) the API exposed by theArrayPoolin 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:
IArrayPool<char>usingArrayPool<char>.SharedJsonTextReaderandJsonTextWriter) and initializeWarning
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 😅
Other details
https://datadoghq.atlassian.net/browse/LANGPLAT-940
Discovered this while exploring remote config optimizations