Skip to content

Use IArrayPool in more places for JSON#8236

Merged
andrewlock merged 6 commits into
masterfrom
andrew/moar-json-array-pool
Mar 9, 2026
Merged

Use IArrayPool in more places for JSON#8236
andrewlock merged 6 commits into
masterfrom
andrew/moar-json-array-pool

Conversation

@andrewlock

Copy link
Copy Markdown
Member

Summary of changes

Adds JSON helper APIs to ensure we use the array pool where possible

Reason for change

There are various "helper" APIs, which are wrappers around Newtonsoft.JSON's JsonSerializer and JsonReader/JsonWriter APIs. In #8228 we updated the explicit usages to use an array pool implementation for JsonReader/JsonWriter calls, but they're used internally without a pool in the helper cases.

This PR creates alternative implementations which do use the pool, updates existing code to use them, and adds the existing APIs to the "banned API" list.

There's another possible approach, in which we update the vendored code to always use the array pool. I was torn as to which is the better option, and went for this approach in the end, but I'm not wedded to it, so happy to take the alternative approach if people think it's preferable?

Implementation details

  • Use 🤖 to find all the potential places that we need to convert.
  • Create "array pool" versions of the APIs
  • Update the call sites to use the new APIs
  • Add the original APIs to the list of "banned" APIs to avoid using them accidentally in the future

Test coverage

Covered by all our existing tests, added some unit tests to confirm the new tests behave as expected

Other details

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

Discovered this while exploring remote config optimizations, but should help lots of areas.

@andrewlock andrewlock added the type:performance Performance, speed, latency, resource usage (CPU, memory) label Feb 23, 2026
@andrewlock
andrewlock requested a review from a team as a code owner February 23, 2026 16:32
@andrewlock andrewlock added the AI Generated Largely based on code generated by an AI or LLM. This label is the same across all dd-trace-* repos label Feb 23, 2026
@andrewlock
andrewlock requested review from a team as code owners February 23, 2026 16:32
@andrewlock
andrewlock requested review from apiarian-datadog and link04 and removed request for a team February 23, 2026 16:32
@andrewlock
andrewlock force-pushed the andrew/moar-json-array-pool branch from 7585dbf to 01f5961 Compare February 23, 2026 16:32
@dd-trace-dotnet-ci-bot

dd-trace-dotnet-ci-bot Bot commented Feb 23, 2026

Copy link
Copy Markdown

Execution-Time Benchmarks Report ⏱️

Execution-time results for samples comparing This PR (8236) 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
duration77.20 ± (77.03 - 77.37) ms76.96 ± (76.97 - 77.31) ms-0.3%
.NET Framework 4.8 - Bailout
duration82.19 ± (82.06 - 82.47) ms82.68 ± (82.44 - 82.84) ms+0.6%✅⬆️
.NET Framework 4.8 - CallTarget+Inlining+NGEN
duration1102.92 ± (1102.31 - 1108.66) ms1103.68 ± (1103.64 - 1110.28) ms+0.1%✅⬆️
.NET Core 3.1 - Baseline
process.internal_duration_ms23.47 ± (23.42 - 23.53) ms23.40 ± (23.35 - 23.45) ms-0.3%
process.time_to_main_ms89.89 ± (89.68 - 90.09) ms89.39 ± (89.19 - 89.60) ms-0.5%
runtime.dotnet.exceptions.count0 ± (0 - 0)0 ± (0 - 0)+0.0%
runtime.dotnet.mem.committed10.92 ± (10.92 - 10.93) MB10.92 ± (10.92 - 10.93) MB-0.0%
runtime.dotnet.threads.count12 ± (12 - 12)12 ± (12 - 12)+0.0%
.NET Core 3.1 - Bailout
process.internal_duration_ms23.54 ± (23.49 - 23.59) ms23.29 ± (23.24 - 23.34) ms-1.1%
process.time_to_main_ms91.91 ± (91.71 - 92.11) ms90.69 ± (90.49 - 90.89) ms-1.3%
runtime.dotnet.exceptions.count0 ± (0 - 0)0 ± (0 - 0)+0.0%
runtime.dotnet.mem.committed10.96 ± (10.95 - 10.96) MB10.95 ± (10.95 - 10.96) MB-0.0%
runtime.dotnet.threads.count13 ± (13 - 13)13 ± (13 - 13)+0.0%
.NET Core 3.1 - CallTarget+Inlining+NGEN
process.internal_duration_ms246.27 ± (242.16 - 250.39) ms231.48 ± (228.19 - 234.78) ms-6.0%
process.time_to_main_ms508.35 ± (507.55 - 509.15) ms509.76 ± (508.88 - 510.64) ms+0.3%✅⬆️
runtime.dotnet.exceptions.count0 ± (0 - 0)0 ± (0 - 0)+0.0%
runtime.dotnet.mem.committed47.55 ± (47.53 - 47.58) MB47.61 ± (47.59 - 47.63) MB+0.1%✅⬆️
runtime.dotnet.threads.count28 ± (28 - 28)28 ± (28 - 28)+0.0%
.NET 6 - Baseline
process.internal_duration_ms22.09 ± (22.05 - 22.13) ms22.27 ± (22.22 - 22.33) ms+0.8%✅⬆️
process.time_to_main_ms78.19 ± (78.00 - 78.37) ms78.53 ± (78.35 - 78.72) ms+0.4%✅⬆️
runtime.dotnet.exceptions.count0 ± (0 - 0)0 ± (0 - 0)+0.0%
runtime.dotnet.mem.committed10.62 ± (10.61 - 10.62) MB10.64 ± (10.64 - 10.65) MB+0.3%✅⬆️
runtime.dotnet.threads.count10 ± (10 - 10)10 ± (10 - 10)+0.0%
.NET 6 - Bailout
process.internal_duration_ms22.02 ± (21.97 - 22.07) ms22.24 ± (22.18 - 22.30) ms+1.0%✅⬆️
process.time_to_main_ms79.82 ± (79.63 - 80.01) ms80.03 ± (79.83 - 80.22) ms+0.3%✅⬆️
runtime.dotnet.exceptions.count0 ± (0 - 0)0 ± (0 - 0)+0.0%
runtime.dotnet.mem.committed10.71 ± (10.71 - 10.72) MB10.75 ± (10.75 - 10.76) MB+0.4%✅⬆️
runtime.dotnet.threads.count11 ± (11 - 11)11 ± (11 - 11)+0.0%
.NET 6 - CallTarget+Inlining+NGEN
process.internal_duration_ms253.06 ± (249.16 - 256.96) ms242.67 ± (238.36 - 246.98) ms-4.1%
process.time_to_main_ms487.72 ± (487.08 - 488.37) ms492.20 ± (491.40 - 493.00) ms+0.9%✅⬆️
runtime.dotnet.exceptions.count0 ± (0 - 0)0 ± (0 - 0)+0.0%
runtime.dotnet.mem.committed48.39 ± (48.38 - 48.41) MB48.33 ± (48.31 - 48.35) MB-0.1%
runtime.dotnet.threads.count28 ± (28 - 28)28 ± (28 - 28)+0.2%✅⬆️
.NET 8 - Baseline
process.internal_duration_ms20.26 ± (20.22 - 20.30) ms20.32 ± (20.26 - 20.37) ms+0.3%✅⬆️
process.time_to_main_ms77.03 ± (76.86 - 77.20) ms77.49 ± (77.31 - 77.67) ms+0.6%✅⬆️
runtime.dotnet.exceptions.count0 ± (0 - 0)0 ± (0 - 0)+0.0%
runtime.dotnet.mem.committed7.64 ± (7.64 - 7.65) MB7.71 ± (7.70 - 7.72) MB+0.9%✅⬆️
runtime.dotnet.threads.count10 ± (10 - 10)10 ± (10 - 10)+0.0%
.NET 8 - Bailout
process.internal_duration_ms20.33 ± (20.28 - 20.39) ms20.46 ± (20.39 - 20.52) ms+0.6%✅⬆️
process.time_to_main_ms78.75 ± (78.56 - 78.94) ms79.21 ± (79.04 - 79.37) ms+0.6%✅⬆️
runtime.dotnet.exceptions.count0 ± (0 - 0)0 ± (0 - 0)+0.0%
runtime.dotnet.mem.committed7.72 ± (7.71 - 7.73) MB7.74 ± (7.73 - 7.75) MB+0.3%✅⬆️
runtime.dotnet.threads.count11 ± (11 - 11)11 ± (11 - 11)+0.0%
.NET 8 - CallTarget+Inlining+NGEN
process.internal_duration_ms194.72 ± (194.04 - 195.40) ms196.28 ± (195.40 - 197.16) ms+0.8%✅⬆️
process.time_to_main_ms466.33 ± (465.62 - 467.04) ms472.07 ± (471.19 - 472.96) ms+1.2%✅⬆️
runtime.dotnet.exceptions.count0 ± (0 - 0)0 ± (0 - 0)+0.0%
runtime.dotnet.mem.committed36.13 ± (36.09 - 36.17) MB36.40 ± (36.35 - 36.45) MB+0.8%✅⬆️
runtime.dotnet.threads.count27 ± (27 - 27)27 ± (27 - 27)+0.2%✅⬆️

HttpMessageHandler

Metric Master (Mean ± 95% CI) Current (Mean ± 95% CI) Change Status
.NET Framework 4.8 - Baseline
duration197.86 ± (197.82 - 198.87) ms196.06 ± (195.91 - 196.77) ms-0.9%
.NET Framework 4.8 - Bailout
duration202.86 ± (202.20 - 203.20) ms200.11 ± (199.86 - 200.49) ms-1.4%
.NET Framework 4.8 - CallTarget+Inlining+NGEN
duration1167.61 ± (1167.67 - 1175.75) ms1161.72 ± (1162.63 - 1169.85) ms-0.5%
.NET Core 3.1 - Baseline
process.internal_duration_ms190.62 ± (190.23 - 191.01) ms191.14 ± (190.70 - 191.58) ms+0.3%✅⬆️
process.time_to_main_ms82.83 ± (82.57 - 83.08) ms83.04 ± (82.81 - 83.28) ms+0.3%✅⬆️
runtime.dotnet.exceptions.count3 ± (3 - 3)3 ± (3 - 3)+0.0%
runtime.dotnet.mem.committed16.20 ± (16.18 - 16.23) MB16.02 ± (16.00 - 16.04) MB-1.1%
runtime.dotnet.threads.count20 ± (20 - 20)20 ± (20 - 20)+0.1%✅⬆️
.NET Core 3.1 - Bailout
process.internal_duration_ms191.48 ± (191.05 - 191.92) ms190.81 ± (190.37 - 191.25) ms-0.4%
process.time_to_main_ms84.61 ± (84.36 - 84.86) ms84.30 ± (84.10 - 84.50) ms-0.4%
runtime.dotnet.exceptions.count3 ± (3 - 3)3 ± (3 - 3)+0.0%
runtime.dotnet.mem.committed16.22 ± (16.20 - 16.24) MB16.14 ± (16.11 - 16.17) MB-0.5%
runtime.dotnet.threads.count21 ± (21 - 21)21 ± (20 - 21)-0.1%
.NET Core 3.1 - CallTarget+Inlining+NGEN
process.internal_duration_ms440.87 ± (438.65 - 443.09) ms444.44 ± (442.02 - 446.85) ms+0.8%✅⬆️
process.time_to_main_ms482.80 ± (482.01 - 483.58) ms482.85 ± (482.13 - 483.57) ms+0.0%✅⬆️
runtime.dotnet.exceptions.count3 ± (3 - 3)3 ± (3 - 3)+0.0%
runtime.dotnet.mem.committed57.99 ± (57.87 - 58.10) MB57.95 ± (57.83 - 58.06) MB-0.1%
runtime.dotnet.threads.count29 ± (29 - 30)29 ± (29 - 29)-0.0%
.NET 6 - Baseline
process.internal_duration_ms196.49 ± (196.03 - 196.95) ms195.12 ± (194.68 - 195.56) ms-0.7%
process.time_to_main_ms71.98 ± (71.76 - 72.20) ms71.48 ± (71.24 - 71.73) ms-0.7%
runtime.dotnet.exceptions.count4 ± (4 - 4)4 ± (4 - 4)+0.0%
runtime.dotnet.mem.committed16.36 ± (16.34 - 16.38) MB16.36 ± (16.33 - 16.39) MB-0.0%
runtime.dotnet.threads.count19 ± (19 - 19)19 ± (19 - 19)-1.0%
.NET 6 - Bailout
process.internal_duration_ms197.01 ± (196.56 - 197.45) ms193.88 ± (193.43 - 194.34) ms-1.6%
process.time_to_main_ms73.57 ± (73.36 - 73.79) ms72.75 ± (72.54 - 72.96) ms-1.1%
runtime.dotnet.exceptions.count4 ± (4 - 4)4 ± (4 - 4)+0.0%
runtime.dotnet.mem.committed16.36 ± (16.34 - 16.39) MB16.11 ± (15.97 - 16.26) MB-1.5%
runtime.dotnet.threads.count20 ± (20 - 20)19 ± (19 - 20)-3.6%
.NET 6 - CallTarget+Inlining+NGEN
process.internal_duration_ms457.28 ± (455.53 - 459.02) ms456.46 ± (454.70 - 458.22) ms-0.2%
process.time_to_main_ms457.58 ± (456.85 - 458.32) ms456.47 ± (455.98 - 456.96) ms-0.2%
runtime.dotnet.exceptions.count4 ± (4 - 4)4 ± (4 - 4)+0.0%
runtime.dotnet.mem.committed57.68 ± (57.57 - 57.78) MB57.88 ± (57.77 - 57.99) MB+0.4%✅⬆️
runtime.dotnet.threads.count29 ± (29 - 29)29 ± (29 - 29)-0.1%
.NET 8 - Baseline
process.internal_duration_ms192.62 ± (192.32 - 192.93) ms192.75 ± (192.34 - 193.16) ms+0.1%✅⬆️
process.time_to_main_ms71.04 ± (70.85 - 71.24) ms70.98 ± (70.78 - 71.18) ms-0.1%
runtime.dotnet.exceptions.count4 ± (4 - 4)4 ± (4 - 4)+0.0%
runtime.dotnet.mem.committed11.70 ± (11.67 - 11.72) MB11.75 ± (11.73 - 11.77) MB+0.4%✅⬆️
runtime.dotnet.threads.count18 ± (18 - 18)18 ± (18 - 18)+0.2%✅⬆️
.NET 8 - Bailout
process.internal_duration_ms195.17 ± (194.66 - 195.68) ms192.48 ± (192.11 - 192.85) ms-1.4%
process.time_to_main_ms73.09 ± (72.91 - 73.27) ms72.03 ± (71.89 - 72.17) ms-1.5%
runtime.dotnet.exceptions.count4 ± (4 - 4)4 ± (4 - 4)+0.0%
runtime.dotnet.mem.committed11.77 ± (11.74 - 11.79) MB11.77 ± (11.74 - 11.79) MB+0.0%✅⬆️
runtime.dotnet.threads.count19 ± (19 - 19)19 ± (19 - 19)-0.5%
.NET 8 - CallTarget+Inlining+NGEN
process.internal_duration_ms373.75 ± (372.38 - 375.12) ms370.61 ± (369.20 - 372.02) ms-0.8%
process.time_to_main_ms443.05 ± (442.27 - 443.84) ms441.41 ± (440.70 - 442.13) ms-0.4%
runtime.dotnet.exceptions.count4 ± (4 - 4)4 ± (4 - 4)+0.0%
runtime.dotnet.mem.committed47.74 ± (47.71 - 47.77) MB47.82 ± (47.78 - 47.85) MB+0.2%✅⬆️
runtime.dotnet.threads.count29 ± (29 - 29)29 ± (29 - 29)-0.6%
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 (8236) - mean (77ms)  : 75, 79
    master - mean (77ms)  : 75, 80

    section Bailout
    This PR (8236) - mean (83ms)  : 81, 85
    master - mean (82ms)  : 80, 85

    section CallTarget+Inlining+NGEN
    This PR (8236) - mean (1,107ms)  : 1059, 1155
    master - mean (1,105ms)  : 1060, 1151

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 (8236) - mean (120ms)  : 116, 124
    master - mean (121ms)  : 117, 124

    section Bailout
    This PR (8236) - mean (121ms)  : 118, 124
    master - mean (123ms)  : 120, 125

    section CallTarget+Inlining+NGEN
    This PR (8236) - mean (786ms)  : 721, 851
    master - mean (796ms)  : 732, 859

Loading
FakeDbCommand (.NET 6)
gantt
    title Execution time (ms) FakeDbCommand (.NET 6)
    dateFormat  x
    axisFormat %Q
    todayMarker off
    section Baseline
    This PR (8236) - mean (108ms)  : 104, 111
    master - mean (107ms)  : 103, 111

    section Bailout
    This PR (8236) - mean (109ms)  : 106, 112
    master - mean (109ms)  : 105, 112

    section CallTarget+Inlining+NGEN
    This PR (8236) - mean (768ms)  : 687, 849
    master - mean (773ms)  : 703, 843

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

    section Bailout
    This PR (8236) - mean (108ms)  : 106, 110
    master - mean (107ms)  : 104, 110

    section CallTarget+Inlining+NGEN
    This PR (8236) - mean (703ms)  : 674, 732
    master - mean (699ms)  : 674, 724

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 (8236) - mean (196ms)  : 192, 201
    master - mean (198ms)  : 192, 205

    section Bailout
    This PR (8236) - mean (200ms)  : 197, 203
    master - mean (203ms)  : 198, 208

    section CallTarget+Inlining+NGEN
    This PR (8236) - mean (1,166ms)  : 1114, 1218
    master - mean (1,172ms)  : 1113, 1230

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 (8236) - mean (283ms)  : 276, 291
    master - mean (282ms)  : 275, 289

    section Bailout
    This PR (8236) - mean (284ms)  : 277, 291
    master - mean (285ms)  : 278, 292

    section CallTarget+Inlining+NGEN
    This PR (8236) - mean (959ms)  : 921, 997
    master - mean (953ms)  : 912, 994

Loading
HttpMessageHandler (.NET 6)
gantt
    title Execution time (ms) HttpMessageHandler (.NET 6)
    dateFormat  x
    axisFormat %Q
    todayMarker off
    section Baseline
    This PR (8236) - mean (275ms)  : 268, 282
    master - mean (277ms)  : 270, 284

    section Bailout
    This PR (8236) - mean (275ms)  : 268, 282
    master - mean (279ms)  : 272, 286

    section CallTarget+Inlining+NGEN
    This PR (8236) - mean (945ms)  : 915, 975
    master - mean (945ms)  : 909, 981

Loading
HttpMessageHandler (.NET 8)
gantt
    title Execution time (ms) HttpMessageHandler (.NET 8)
    dateFormat  x
    axisFormat %Q
    todayMarker off
    section Baseline
    This PR (8236) - mean (274ms)  : 266, 281
    master - mean (273ms)  : 269, 278

    section Bailout
    This PR (8236) - mean (274ms)  : 269, 279
    master - mean (278ms)  : 271, 286

    section CallTarget+Inlining+NGEN
    This PR (8236) - mean (844ms)  : 818, 871
    master - mean (848ms)  : 826, 871

Loading

@pr-commenter

pr-commenter Bot commented Feb 23, 2026

Copy link
Copy Markdown

Benchmarks

Benchmark execution time: 2026-03-09 18:29:28

Comparing candidate commit c42342d in PR branch andrew/moar-json-array-pool with baseline commit d17ace6 in branch master.

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

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

  • 🟩 throughput [+8278.171op/s; +10398.974op/s] or [+6.786%; +8.524%]

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

  • 🟩 execution_time [-17.799ms; -11.612ms] or [-8.430%; -5.500%]

scenario:Benchmarks.Trace.Asm.AppSecBodyBenchmark.ObjectExtractorSimpleBody net472

  • 🟩 throughput [+209363.193op/s; +213205.262op/s] or [+6.679%; +6.801%]

scenario:Benchmarks.Trace.CIVisibilityProtocolWriterBenchmark.WriteAndFlushEnrichedTraces net472

  • 🟥 throughput [-87.487op/s; -58.513op/s] or [-8.103%; -5.420%]

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

  • 🟩 throughput [+216.945op/s; +284.698op/s] or [+15.922%; +20.895%]

scenario:Benchmarks.Trace.CIVisibilityProtocolWriterBenchmark.WriteAndFlushEnrichedTraces netcoreapp3.1

  • 🟩 execution_time [-25.702ms; -21.786ms] or [-16.600%; -14.071%]
  • 🟩 throughput [+235.457op/s; +275.962op/s] or [+16.204%; +18.991%]

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

  • 🟥 execution_time [+139.567µs; +256.473µs] or [+5.088%; +9.350%]

scenario:Benchmarks.Trace.GraphQLBenchmark.ExecuteAsync netcoreapp3.1

  • 🟩 throughput [+22701.995op/s; +33535.833op/s] or [+5.434%; +8.027%]

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

  • 🟥 execution_time [+16.714ms; +20.476ms] or [+9.837%; +12.051%]

scenario:Benchmarks.Trace.RedisBenchmark.SendReceive net6.0

  • 🟥 throughput [-42523.606op/s; -29984.570op/s] or [-7.563%; -5.333%]

scenario:Benchmarks.Trace.SingleSpanAspNetCoreBenchmark.SingleSpanAspNetCore netcoreapp3.1

  • 🟥 throughput [-18208905.005op/s; -16837282.066op/s] or [-7.553%; -6.984%]

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

  • 🟩 execution_time [-16.144ms; -10.649ms] or [-7.668%; -5.058%]

scenario:Benchmarks.Trace.SpanBenchmark.StartFinishSpan net6.0

  • 🟥 execution_time [+12.153ms; +18.129ms] or [+6.200%; +9.249%]

@link04 link04 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

LGTM

@dromanol dromanol left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Nice

@andrewlock
andrewlock force-pushed the andrew/use-json-array-pool branch from dd07383 to a33aae2 Compare March 6, 2026 14:13
Base automatically changed from andrew/use-json-array-pool to master March 9, 2026 17:06
@andrewlock
andrewlock force-pushed the andrew/moar-json-array-pool branch from 01f5961 to c42342d Compare March 9, 2026 17:27
@andrewlock
andrewlock enabled auto-merge (squash) March 9, 2026 17:27
@andrewlock
andrewlock merged commit 59db4b3 into master Mar 9, 2026
141 checks passed
@andrewlock
andrewlock deleted the andrew/moar-json-array-pool branch March 9, 2026 19:06
@github-actions github-actions Bot added this to the vNext-v3 milestone Mar 9, 2026
andrewlock added a commit that referenced this pull request Mar 11, 2026
## Summary of changes

Adds JSON helper APIs to ensure we use the array pool where possible

## Reason for change

There are various "helper" APIs, which are wrappers around
Newtonsoft.JSON's `JsonSerializer` and `JsonReader`/`JsonWriter` APIs.
In #8228 we updated the explicit usages to use an array pool
implementation for `JsonReader`/`JsonWriter` calls, but they're used
internally without a pool in the helper cases.

This PR creates alternative implementations which _do_ use the pool,
updates existing code to use them, and adds the existing APIs to the
"banned API" list.

> There's another possible approach, in which we update the vendored
code to _always_ use the array pool. I was torn as to which is the
better option, and went for this approach in the end, but I'm not wedded
to it, so happy to take the alternative approach if people think it's
preferable?

## Implementation details

- Use 🤖 to find all the potential places that we need to convert.
- Create "array pool" versions of the APIs
- Update the call sites to use the new APIs
- Add the original APIs to the list of "banned" APIs to avoid using them
accidentally in the future

## Test coverage

Covered by all our existing tests, added some unit tests to confirm the
new tests behave as expected

## Other details

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

Discovered this while exploring remote config optimizations, but should
help lots of areas.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

AI Generated Largely based on code generated by an AI or LLM. This label is the same across all dd-trace-* repos type:performance Performance, speed, latency, resource usage (CPU, memory)

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants