Skip to content

Fix (and extend) AgentWriter benchmark#7882

Merged
andrewlock merged 4 commits intomasterfrom
andrew/fix-agent-writer-benchmark
Dec 1, 2025
Merged

Fix (and extend) AgentWriter benchmark#7882
andrewlock merged 4 commits intomasterfrom
andrew/fix-agent-writer-benchmark

Conversation

@andrewlock
Copy link
Member

Summary of changes

Reason for change

The AgentWriter benchmark has been broken since #7724 was merged, as it's passing a null ref to a non-null field. This fixes that issue.

Also, this adds a benchmark (which doesn't run in CI currently) which tests the serialization of spans without flushing, so we can distinguish the source of allocation more easily.

Finally, I made some changes to the spans used in the existing AgentWriter benchmark. This is due to discrepancies I noticed in the results from the benchmark vs other testing.

Implementation details

  • Pass a StatsdManager into the AgentWriter instead of null
  • Create a "noop" Api which doesn't do anything, and use that in a different AgentWriter instance, this effectively isolates the overhead in the benchmark to being the serialization only, instead of including "flush" related overhead (which is included in the existing benchmark). To avoid bloating our benchmarking runs, don't bother to run this one in CI (it's just useful for inestigation).
  • Pass a SqlTags object into the spans, with a couple of fields set.
  • Set a tag other than env on the spans. env is a "special" trace-level tag, so it doesn't really do what you think it does here and IMO is less revealing.

Test coverage

Tested locally - running the benchmarks without the sql tags change:

Method Runtime Mean Error StdDev Median Ratio RatioSD Allocated Alloc Ratio
WriteEnrichedTraces .NET 6.0 391.0 us 7.54 us 9.54 us 389.2 us 0.79 0.02 105 B 0.50
WriteEnrichedTraces .NET Framework 4.7.2 499.2 us 3.95 us 3.69 us 499.0 us 1.00 0.00 208 B 1.00
WriteAndFlushEnrichedTraces .NET 6.0 391.9 us 8.65 us 24.82 us 384.9 us 0.81 0.05 2697 B 0.81
WriteAndFlushEnrichedTraces .NET Framework 4.7.2 511.3 us 5.30 us 4.43 us 511.0 us 1.00 0.00 3312 B 1.00

I then made the SqlTags change, and ran again:

Method Runtime Mean Error StdDev Ratio RatioSD Gen0 Allocated Alloc Ratio
WriteEnrichedTraces .NET 6.0 488.9 us 7.55 us 11.29 us 0.70 0.02 - 110 B 0.001
WriteEnrichedTraces .NET Framework 4.7.2 703.3 us 5.56 us 4.93 us 1.00 0.00 17.5781 112537 B 1.000
WriteAndFlushEnrichedTraces .NET 6.0 481.2 us 9.39 us 10.43 us 0.64 0.02 - 2701 B 0.02
WriteAndFlushEnrichedTraces .NET Framework 4.7.2 725.2 us 14.49 us 27.21 us 1.00 0.00 17.5781 115641 B 1.00

Yikes, we have some rogue allocation there on .NET FX!

Other details

@andrewlock andrewlock requested a review from a team as a code owner December 1, 2025 12:21
@andrewlock andrewlock added the area:benchmarks Benchmarks, throughput tests, Crank, Bombardier, etc label Dec 1, 2025
@github-actions github-actions bot added the area:tests unit tests, integration tests label Dec 1, 2025
@dd-trace-dotnet-ci-bot
Copy link

dd-trace-dotnet-ci-bot bot commented Dec 1, 2025

Execution-Time Benchmarks Report ⏱️

Execution-time results for samples comparing This PR (7882) 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
duration76.67 ± (76.43 - 77.46) ms74.81 ± (74.97 - 75.83) ms-2.4%
.NET Framework 4.8 - Bailout
duration79.31 ± (79.23 - 79.86) ms79.99 ± (80.05 - 81.08) ms+0.9%✅⬆️
.NET Framework 4.8 - CallTarget+Inlining+NGEN
duration1063.58 ± (1063.74 - 1071.29) ms1067.81 ± (1069.03 - 1078.48) ms+0.4%✅⬆️
.NET Core 3.1 - Baseline
process.internal_duration_ms23.06 ± (22.99 - 23.12) ms23.02 ± (22.94 - 23.10) ms-0.2%
process.time_to_main_ms88.91 ± (88.56 - 89.26) ms88.25 ± (87.84 - 88.65) ms-0.7%
runtime.dotnet.exceptions.count0 ± (0 - 0)0 ± (0 - 0)+0.0%
runtime.dotnet.mem.committed10.92 ± (10.91 - 10.92) MB10.91 ± (10.91 - 10.91) MB-0.1%
runtime.dotnet.threads.count12 ± (12 - 12)12 ± (12 - 12)+0.0%
.NET Core 3.1 - Bailout
process.internal_duration_ms22.94 ± (22.87 - 23.01) ms23.01 ± (22.95 - 23.08) ms+0.3%✅⬆️
process.time_to_main_ms90.14 ± (89.72 - 90.56) ms90.41 ± (89.95 - 90.87) ms+0.3%✅⬆️
runtime.dotnet.exceptions.count0 ± (0 - 0)0 ± (0 - 0)+0.0%
runtime.dotnet.mem.committed10.96 ± (10.96 - 10.96) MB10.96 ± (10.96 - 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_ms225.23 ± (223.66 - 226.80) ms224.24 ± (222.96 - 225.52) ms-0.4%
process.time_to_main_ms506.63 ± (505.42 - 507.84) ms508.34 ± (506.96 - 509.71) ms+0.3%✅⬆️
runtime.dotnet.exceptions.count0 ± (0 - 0)0 ± (0 - 0)+0.0%
runtime.dotnet.mem.committed47.85 ± (47.83 - 47.87) MB47.89 ± (47.87 - 47.91) MB+0.1%✅⬆️
runtime.dotnet.threads.count28 ± (28 - 28)28 ± (28 - 28)+0.1%✅⬆️
.NET 6 - Baseline
process.internal_duration_ms21.72 ± (21.66 - 21.78) ms21.95 ± (21.87 - 22.04) ms+1.1%✅⬆️
process.time_to_main_ms76.43 ± (76.14 - 76.72) ms77.09 ± (76.64 - 77.54) ms+0.9%✅⬆️
runtime.dotnet.exceptions.count0 ± (0 - 0)0 ± (0 - 0)+0.0%
runtime.dotnet.mem.committed10.59 ± (10.59 - 10.59) MB10.64 ± (10.64 - 10.64) MB+0.5%✅⬆️
runtime.dotnet.threads.count10 ± (10 - 10)10 ± (10 - 10)+0.0%
.NET 6 - Bailout
process.internal_duration_ms21.65 ± (21.59 - 21.71) ms21.93 ± (21.86 - 22.00) ms+1.3%✅⬆️
process.time_to_main_ms76.88 ± (76.55 - 77.21) ms78.60 ± (78.21 - 78.98) ms+2.2%✅⬆️
runtime.dotnet.exceptions.count0 ± (0 - 0)0 ± (0 - 0)+0.0%
runtime.dotnet.mem.committed10.64 ± (10.64 - 10.64) MB10.73 ± (10.73 - 10.74) MB+0.9%✅⬆️
runtime.dotnet.threads.count11 ± (11 - 11)11 ± (11 - 11)+0.0%
.NET 6 - CallTarget+Inlining+NGEN
process.internal_duration_ms213.09 ± (212.04 - 214.14) ms212.58 ± (211.47 - 213.69) ms-0.2%
process.time_to_main_ms470.36 ± (469.32 - 471.39) ms472.88 ± (471.56 - 474.20) ms+0.5%✅⬆️
runtime.dotnet.exceptions.count0 ± (0 - 0)0 ± (0 - 0)+0.0%
runtime.dotnet.mem.committed48.08 ± (48.06 - 48.11) MB48.06 ± (48.03 - 48.08) MB-0.1%
runtime.dotnet.threads.count28 ± (28 - 28)28 ± (28 - 28)-0.0%
.NET 8 - Baseline
process.internal_duration_ms19.93 ± (19.87 - 20.00) ms20.05 ± (19.99 - 20.12) ms+0.6%✅⬆️
process.time_to_main_ms76.27 ± (75.91 - 76.63) ms75.54 ± (75.20 - 75.87) ms-1.0%
runtime.dotnet.exceptions.count0 ± (0 - 0)0 ± (0 - 0)+0.0%
runtime.dotnet.mem.committed7.64 ± (7.63 - 7.65) MB7.63 ± (7.63 - 7.64) MB-0.1%
runtime.dotnet.threads.count10 ± (10 - 10)10 ± (10 - 10)+0.0%
.NET 8 - Bailout
process.internal_duration_ms19.90 ± (19.85 - 19.96) ms20.10 ± (20.02 - 20.19) ms+1.0%✅⬆️
process.time_to_main_ms76.91 ± (76.61 - 77.20) ms77.51 ± (77.08 - 77.94) ms+0.8%✅⬆️
runtime.dotnet.exceptions.count0 ± (0 - 0)0 ± (0 - 0)+0.0%
runtime.dotnet.mem.committed7.69 ± (7.68 - 7.69) MB7.70 ± (7.69 - 7.71) MB+0.2%✅⬆️
runtime.dotnet.threads.count11 ± (11 - 11)11 ± (11 - 11)+0.0%
.NET 8 - CallTarget+Inlining+NGEN
process.internal_duration_ms194.14 ± (192.94 - 195.34) ms193.73 ± (192.79 - 194.68) ms-0.2%
process.time_to_main_ms457.75 ± (456.72 - 458.78) ms461.55 ± (460.27 - 462.83) ms+0.8%✅⬆️
runtime.dotnet.exceptions.count0 ± (0 - 0)0 ± (0 - 0)+0.0%
runtime.dotnet.mem.committed36.41 ± (36.37 - 36.45) MB36.49 ± (36.45 - 36.53) MB+0.2%✅⬆️
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
duration192.40 ± (192.45 - 193.31) ms193.34 ± (193.09 - 193.94) ms+0.5%✅⬆️
.NET Framework 4.8 - Bailout
duration195.04 ± (194.82 - 195.27) ms197.77 ± (197.54 - 198.33) ms+1.4%✅⬆️
.NET Framework 4.8 - CallTarget+Inlining+NGEN
duration1104.03 ± (1110.09 - 1119.81) ms1110.96 ± (1115.09 - 1123.32) ms+0.6%✅⬆️
.NET Core 3.1 - Baseline
process.internal_duration_ms186.65 ± (186.33 - 186.96) ms188.53 ± (188.14 - 188.93) ms+1.0%✅⬆️
process.time_to_main_ms80.03 ± (79.85 - 80.21) ms80.87 ± (80.65 - 81.08) ms+1.0%✅⬆️
runtime.dotnet.exceptions.count3 ± (3 - 3)3 ± (3 - 3)+0.0%
runtime.dotnet.mem.committed16.19 ± (16.17 - 16.21) MB16.07 ± (16.04 - 16.10) MB-0.7%
runtime.dotnet.threads.count20 ± (19 - 20)20 ± (20 - 20)+0.9%✅⬆️
.NET Core 3.1 - Bailout
process.internal_duration_ms186.67 ± (186.35 - 187.00) ms187.89 ± (187.50 - 188.27) ms+0.7%✅⬆️
process.time_to_main_ms81.53 ± (81.35 - 81.70) ms82.35 ± (82.17 - 82.53) ms+1.0%✅⬆️
runtime.dotnet.exceptions.count3 ± (3 - 3)3 ± (3 - 3)+0.0%
runtime.dotnet.mem.committed16.18 ± (16.11 - 16.25) MB16.14 ± (16.11 - 16.17) MB-0.2%
runtime.dotnet.threads.count21 ± (20 - 21)21 ± (21 - 21)+0.7%✅⬆️
.NET Core 3.1 - CallTarget+Inlining+NGEN
process.internal_duration_ms396.58 ± (393.84 - 399.32) ms402.32 ± (399.56 - 405.08) ms+1.4%✅⬆️
process.time_to_main_ms470.63 ± (470.00 - 471.26) ms476.62 ± (475.79 - 477.46) ms+1.3%✅⬆️
runtime.dotnet.exceptions.count3 ± (3 - 3)3 ± (3 - 3)+0.0%
runtime.dotnet.mem.committed58.58 ± (58.43 - 58.72) MB58.75 ± (58.62 - 58.88) MB+0.3%✅⬆️
runtime.dotnet.threads.count29 ± (29 - 29)29 ± (29 - 30)+0.1%✅⬆️
.NET 6 - Baseline
process.internal_duration_ms190.63 ± (190.38 - 190.88) ms195.40 ± (194.94 - 195.87) ms+2.5%✅⬆️
process.time_to_main_ms69.37 ± (69.26 - 69.49) ms71.23 ± (71.04 - 71.43) ms+2.7%✅⬆️
runtime.dotnet.exceptions.count4 ± (4 - 4)4 ± (4 - 4)+0.0%
runtime.dotnet.mem.committed16.26 ± (16.14 - 16.37) MB16.36 ± (16.33 - 16.38) MB+0.6%✅⬆️
runtime.dotnet.threads.count18 ± (18 - 18)19 ± (19 - 19)+3.8%✅⬆️
.NET 6 - Bailout
process.internal_duration_ms190.63 ± (190.25 - 191.01) ms192.74 ± (192.35 - 193.12) ms+1.1%✅⬆️
process.time_to_main_ms70.51 ± (70.39 - 70.64) ms71.45 ± (71.30 - 71.60) ms+1.3%✅⬆️
runtime.dotnet.exceptions.count4 ± (4 - 4)4 ± (4 - 4)+0.0%
runtime.dotnet.mem.committed16.04 ± (15.88 - 16.20) MB16.38 ± (16.31 - 16.46) MB+2.1%✅⬆️
runtime.dotnet.threads.count19 ± (19 - 19)20 ± (20 - 20)+3.9%✅⬆️
.NET 6 - CallTarget+Inlining+NGEN
process.internal_duration_ms405.02 ± (402.64 - 407.40) ms411.76 ± (409.49 - 414.03) ms+1.7%✅⬆️
process.time_to_main_ms438.30 ± (437.78 - 438.81) ms446.10 ± (445.32 - 446.88) ms+1.8%✅⬆️
runtime.dotnet.exceptions.count4 ± (4 - 4)4 ± (4 - 4)+0.0%
runtime.dotnet.mem.committed58.38 ± (58.22 - 58.54) MB59.04 ± (58.92 - 59.17) MB+1.1%✅⬆️
runtime.dotnet.threads.count29 ± (29 - 30)30 ± (29 - 30)+0.2%✅⬆️
.NET 8 - Baseline
process.internal_duration_ms190.33 ± (189.96 - 190.69) ms191.36 ± (190.95 - 191.78) ms+0.5%✅⬆️
process.time_to_main_ms69.35 ± (69.12 - 69.58) ms70.13 ± (69.98 - 70.28) ms+1.1%✅⬆️
runtime.dotnet.exceptions.count4 ± (4 - 4)4 ± (4 - 4)+0.0%
runtime.dotnet.mem.committed11.75 ± (11.72 - 11.77) MB11.74 ± (11.72 - 11.76) MB-0.1%
runtime.dotnet.threads.count18 ± (18 - 18)18 ± (18 - 18)-0.1%
.NET 8 - Bailout
process.internal_duration_ms188.05 ± (187.85 - 188.25) ms190.54 ± (190.22 - 190.86) ms+1.3%✅⬆️
process.time_to_main_ms70.00 ± (69.91 - 70.09) ms70.79 ± (70.65 - 70.92) ms+1.1%✅⬆️
runtime.dotnet.exceptions.count4 ± (4 - 4)4 ± (4 - 4)+0.0%
runtime.dotnet.mem.committed11.83 ± (11.80 - 11.86) MB11.77 ± (11.74 - 11.79) MB-0.5%
runtime.dotnet.threads.count19 ± (19 - 19)19 ± (19 - 19)+0.4%✅⬆️
.NET 8 - CallTarget+Inlining+NGEN
process.internal_duration_ms364.31 ± (362.97 - 365.64) ms370.55 ± (369.17 - 371.92) ms+1.7%✅⬆️
process.time_to_main_ms426.43 ± (425.83 - 427.03) ms435.06 ± (434.17 - 435.95) ms+2.0%✅⬆️
runtime.dotnet.exceptions.count4 ± (4 - 4)4 ± (4 - 4)+0.0%
runtime.dotnet.mem.committed47.83 ± (47.79 - 47.86) MB47.95 ± (47.93 - 47.98) MB+0.3%✅⬆️
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 (7882) - mean (75ms)  : 69, 82
    master - mean (77ms)  : 70, 84

    section Bailout
    This PR (7882) - mean (81ms)  : 73, 89
    master - mean (80ms)  : 75, 84

    section CallTarget+Inlining+NGEN
    This PR (7882) - mean (1,074ms)  : 1005, 1142
    master - mean (1,068ms)  : 1013, 1122

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 (7882) - mean (119ms)  : 110, 127
    master - mean (120ms)  : 112, 127

    section Bailout
    This PR (7882) - mean (121ms)  : 114, 128
    master - mean (121ms)  : 114, 127

    section CallTarget+Inlining+NGEN
    This PR (7882) - mean (769ms)  : 723, 815
    master - mean (773ms)  : 733, 813

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

    section Bailout
    This PR (7882) - mean (108ms)  : 101, 115
    master - mean (106ms)  : 100, 111

    section CallTarget+Inlining+NGEN
    This PR (7882) - mean (713ms)  : 677, 749
    master - mean (711ms)  : 682, 740

Loading
FakeDbCommand (.NET 8)
gantt
    title Execution time (ms) FakeDbCommand (.NET 8)
    dateFormat  x
    axisFormat %Q
    todayMarker off
    section Baseline
    This PR (7882) - mean (104ms)  : 98, 110
    master - mean (105ms)  : 97, 113

    section Bailout
    This PR (7882) - mean (106ms)  : 97, 115
    master - mean (105ms)  : 100, 111

    section CallTarget+Inlining+NGEN
    This PR (7882) - mean (693ms)  : 659, 726
    master - mean (687ms)  : 650, 725

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 (7882) - mean (194ms)  : 189, 198
    master - mean (193ms)  : 188, 197

    section Bailout
    This PR (7882) - mean (198ms)  : 194, 202
    master - mean (195ms)  : 193, 197

    section CallTarget+Inlining+NGEN
    This PR (7882) - mean (1,119ms)  : 1060, 1179
    master - mean (1,115ms)  : 1043, 1187

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 (7882) - mean (278ms)  : 272, 284
    master - mean (275ms)  : 270, 280

    section Bailout
    This PR (7882) - mean (279ms)  : 274, 284
    master - mean (276ms)  : 272, 280

    section CallTarget+Inlining+NGEN
    This PR (7882) - mean (918ms)  : 858, 979
    master - mean (903ms)  : 854, 951

Loading
HttpMessageHandler (.NET 6)
gantt
    title Execution time (ms) HttpMessageHandler (.NET 6)
    dateFormat  x
    axisFormat %Q
    todayMarker off
    section Baseline
    This PR (7882) - mean (275ms)  : 264, 287
    master - mean (268ms)  : 265, 272

    section Bailout
    This PR (7882) - mean (273ms)  : 267, 278
    master - mean (269ms)  : 264, 274

    section CallTarget+Inlining+NGEN
    This PR (7882) - mean (893ms)  : 849, 938
    master - mean (875ms)  : 834, 917

Loading
HttpMessageHandler (.NET 8)
gantt
    title Execution time (ms) HttpMessageHandler (.NET 8)
    dateFormat  x
    axisFormat %Q
    todayMarker off
    section Baseline
    This PR (7882) - mean (271ms)  : 265, 277
    master - mean (269ms)  : 264, 274

    section Bailout
    This PR (7882) - mean (271ms)  : 267, 275
    master - mean (267ms)  : 265, 270

    section CallTarget+Inlining+NGEN
    This PR (7882) - mean (837ms)  : 807, 866
    master - mean (822ms)  : 802, 841

Loading

Copy link
Collaborator

@NachoEchevarria NachoEchevarria left a comment

Choose a reason for hiding this comment

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

Thanks!

@pr-commenter
Copy link

pr-commenter bot commented Dec 1, 2025

Benchmarks

Benchmarks Report for benchmark platform 🐌

Benchmarks for #7882 compared to master:

  • All benchmarks have the same speed
  • 5 benchmarks have fewer allocations
  • 6 benchmarks have more allocations

The following thresholds were used for comparing the benchmark speeds:

  • Mann–Whitney U test with statistical test for significance of 5%
  • Only results indicating a difference greater than 10% and 0.3 ns are considered.

Allocation changes below 0.5% are ignored.

Benchmark details

Benchmarks.Trace.ActivityBenchmark - Same speed ✔️ Fewer allocations 🎉

Fewer allocations 🎉 in #7882

Benchmark Base Allocated Diff Allocated Change Change %
Benchmarks.Trace.ActivityBenchmark.StartStopWithChild‑net472 6.14 KB 6.09 KB -51 B -0.83%

Raw results

Branch Method Toolchain Mean StdError StdDev Gen 0 Gen 1 Gen 2 Allocated
master StartStopWithChild net6.0 10.6μs 59.2ns 360ns 0 0 0 5.51 KB
master StartStopWithChild netcoreapp3.1 14.3μs 73ns 350ns 0 0 0 5.73 KB
master StartStopWithChild net472 22.9μs 118ns 728ns 0.896 0.336 0 6.14 KB
#7882 StartStopWithChild net6.0 11.1μs 58ns 278ns 0 0 0 5.51 KB
#7882 StartStopWithChild netcoreapp3.1 13.8μs 71.5ns 358ns 0 0 0 5.7 KB
#7882 StartStopWithChild net472 22.1μs 111ns 566ns 0.904 0.339 0 6.09 KB
Benchmarks.Trace.AgentWriterBenchmark - Unknown 🤷 More allocations ⚠️

More allocations ⚠️ in #7882

Benchmark Base Allocated Diff Allocated Change Change %
Benchmarks.Trace.AgentWriterBenchmark.WriteAndFlushEnrichedTraces‑net6.0 0 b 2.71 KB 2.71 KB
Benchmarks.Trace.AgentWriterBenchmark.WriteAndFlushEnrichedTraces‑netcoreapp3.1 0 b 2.7 KB 2.7 KB
Benchmarks.Trace.AgentWriterBenchmark.WriteAndFlushEnrichedTraces‑net472 0 b 115.65 KB 115.65 KB

Raw results

Branch Method Toolchain Mean StdError StdDev Gen 0 Gen 1 Gen 2 Allocated
master WriteAndFlushEnrichedTraces net6.0 N/A N/A N/A NaN NaN NaN 0 b
master WriteAndFlushEnrichedTraces netcoreapp3.1 N/A N/A N/A NaN NaN NaN 0 b
master WriteAndFlushEnrichedTraces net472 N/A N/A N/A NaN NaN NaN 0 b
#7882 WriteAndFlushEnrichedTraces net6.0 1.25ms 84.7ns 328ns 0 0 0 2.71 KB
#7882 WriteAndFlushEnrichedTraces netcoreapp3.1 1.38ms 1.28μs 4.96μs 0 0 0 2.7 KB
#7882 WriteAndFlushEnrichedTraces net472 1.77ms 770ns 2.98μs 15.6 0 0 115.65 KB
Benchmarks.Trace.Asm.AppSecBodyBenchmark - Same speed ✔️ Same allocations ✔️

Raw results

Branch Method Toolchain Mean StdError StdDev Gen 0 Gen 1 Gen 2 Allocated
master AllCycleSimpleBody net6.0 1.08μs 5.79ns 33.3ns 0 0 0 1.22 KB
master AllCycleSimpleBody netcoreapp3.1 1.39μs 7.9ns 55.9ns 0 0 0 1.2 KB
master AllCycleSimpleBody net472 1.11μs 1.11ns 4.3ns 0.194 0 0 1.23 KB
master AllCycleMoreComplexBody net6.0 7.22μs 4.09ns 15.8ns 0 0 0 4.72 KB
master AllCycleMoreComplexBody netcoreapp3.1 9.35μs 43.6ns 175ns 0 0 0 4.62 KB
master AllCycleMoreComplexBody net472 7.58μs 3.67ns 14.2ns 0.718 0 0 4.74 KB
master ObjectExtractorSimpleBody net6.0 321ns 1.78ns 11ns 0 0 0 280 B
master ObjectExtractorSimpleBody netcoreapp3.1 405ns 1.99ns 8.42ns 0 0 0 272 B
master ObjectExtractorSimpleBody net472 299ns 0.178ns 0.667ns 0.0442 0 0 281 B
master ObjectExtractorMoreComplexBody net6.0 6.27μs 31.4ns 137ns 0 0 0 3.78 KB
master ObjectExtractorMoreComplexBody netcoreapp3.1 7.87μs 36.9ns 148ns 0 0 0 3.69 KB
master ObjectExtractorMoreComplexBody net472 6.6μs 1.21ns 4.53ns 0.598 0 0 3.8 KB
#7882 AllCycleSimpleBody net6.0 1.05μs 6.07ns 49.3ns 0 0 0 1.22 KB
#7882 AllCycleSimpleBody netcoreapp3.1 1.39μs 7.67ns 49.1ns 0 0 0 1.2 KB
#7882 AllCycleSimpleBody net472 1.06μs 3.9ns 15.1ns 0.193 0 0 1.23 KB
#7882 AllCycleMoreComplexBody net6.0 7.14μs 3ns 11.6ns 0 0 0 4.72 KB
#7882 AllCycleMoreComplexBody netcoreapp3.1 8.88μs 46ns 221ns 0 0 0 4.62 KB
#7882 AllCycleMoreComplexBody net472 7.59μs 9.19ns 35.6ns 0.718 0 0 4.74 KB
#7882 ObjectExtractorSimpleBody net6.0 326ns 1.74ns 8.87ns 0 0 0 280 B
#7882 ObjectExtractorSimpleBody netcoreapp3.1 397ns 2.19ns 12.4ns 0 0 0 272 B
#7882 ObjectExtractorSimpleBody net472 301ns 0.415ns 1.61ns 0.0444 0 0 281 B
#7882 ObjectExtractorMoreComplexBody net6.0 6.28μs 31.9ns 153ns 0 0 0 3.78 KB
#7882 ObjectExtractorMoreComplexBody netcoreapp3.1 7.67μs 39.5ns 189ns 0 0 0 3.69 KB
#7882 ObjectExtractorMoreComplexBody net472 6.7μs 4.41ns 17.1ns 0.6 0 0 3.8 KB
Benchmarks.Trace.Asm.AppSecEncoderBenchmark - Same speed ✔️ Same allocations ✔️

Raw results

Branch Method Toolchain Mean StdError StdDev Gen 0 Gen 1 Gen 2 Allocated
master EncodeArgs net6.0 76.3μs 214ns 828ns 0 0 0 32.4 KB
master EncodeArgs netcoreapp3.1 97.7μs 24.1ns 90.3ns 0 0 0 32.4 KB
master EncodeArgs net472 110μs 27ns 105ns 4.94 0 0 32.51 KB
master EncodeLegacyArgs net6.0 144μs 26ns 101ns 0 0 0 2.15 KB
master EncodeLegacyArgs netcoreapp3.1 201μs 755ns 2.93μs 0 0 0 2.15 KB
master EncodeLegacyArgs net472 265μs 142ns 551ns 0 0 0 2.16 KB
#7882 EncodeArgs net6.0 77.5μs 223ns 864ns 0 0 0 32.4 KB
#7882 EncodeArgs netcoreapp3.1 98.1μs 275ns 1.07μs 0 0 0 32.4 KB
#7882 EncodeArgs net472 109μs 10.9ns 42.1ns 4.9 0 0 32.51 KB
#7882 EncodeLegacyArgs net6.0 148μs 55.5ns 208ns 0 0 0 2.15 KB
#7882 EncodeLegacyArgs netcoreapp3.1 198μs 70.3ns 263ns 0 0 0 2.15 KB
#7882 EncodeLegacyArgs net472 266μs 233ns 901ns 0 0 0 2.16 KB
Benchmarks.Trace.Asm.AppSecWafBenchmark - Same speed ✔️ More allocations ⚠️

More allocations ⚠️ in #7882

Benchmark Base Allocated Diff Allocated Change Change %
Benchmarks.Trace.Asm.AppSecWafBenchmark.RunWafRealisticBenchmark‑net6.0 5.48 KB 5.82 KB 336 B 6.13%

Fewer allocations 🎉 in #7882

Benchmark Base Allocated Diff Allocated Change Change %
Benchmarks.Trace.Asm.AppSecWafBenchmark.RunWafRealisticBenchmarkWithAttack‑net6.0 2.83 KB 2.54 KB -288 B -10.17%

Raw results

Branch Method Toolchain Mean StdError StdDev Gen 0 Gen 1 Gen 2 Allocated
master RunWafRealisticBenchmark net6.0 446μs 2.59μs 23.2μs 0 0 0 5.48 KB
master RunWafRealisticBenchmark netcoreapp3.1 473μs 2.77μs 25.8μs 0 0 0 4.58 KB
master RunWafRealisticBenchmark net472 492μs 356ns 1.28μs 0 0 0 0 b
master RunWafRealisticBenchmarkWithAttack net6.0 321μs 920ns 3.19μs 0 0 0 2.83 KB
master RunWafRealisticBenchmarkWithAttack netcoreapp3.1 362μs 3.19μs 31.1μs 0 0 0 2.32 KB
master RunWafRealisticBenchmarkWithAttack net472 368μs 449ns 1.68μs 0 0 0 0 b
#7882 RunWafRealisticBenchmark net6.0 440μs 2.32μs 11.6μs 0 0 0 5.82 KB
#7882 RunWafRealisticBenchmark netcoreapp3.1 470μs 2.6μs 17.2μs 0 0 0 4.58 KB
#7882 RunWafRealisticBenchmark net472 505μs 251ns 870ns 0 0 0 0 b
#7882 RunWafRealisticBenchmarkWithAttack net6.0 318μs 1.24μs 4.62μs 0 0 0 2.54 KB
#7882 RunWafRealisticBenchmarkWithAttack netcoreapp3.1 350μs 3.3μs 32μs 0 0 0 2.32 KB
#7882 RunWafRealisticBenchmarkWithAttack net472 369μs 405ns 1.52μs 0 0 0 0 b
Benchmarks.Trace.AspNetCoreBenchmark - Same speed ✔️ Same allocations ✔️

Raw results

Branch Method Toolchain Mean StdError StdDev Gen 0 Gen 1 Gen 2 Allocated
master SendRequest net6.0 60.6μs 36.8ns 133ns 0 0 0 14.52 KB
master SendRequest netcoreapp3.1 72.2μs 334ns 1.34μs 0 0 0 17.42 KB
master SendRequest net472 0.161ns 0.00536ns 0.0208ns 0 0 0 0 b
#7882 SendRequest net6.0 61.5μs 34.9ns 126ns 0 0 0 14.52 KB
#7882 SendRequest netcoreapp3.1 72.7μs 66ns 247ns 0 0 0 17.42 KB
#7882 SendRequest net472 0.0132ns 0.00364ns 0.0141ns 0 0 0 0 b
Benchmarks.Trace.CharSliceBenchmark - Same speed ✔️ Fewer allocations 🎉

Fewer allocations 🎉 in #7882

Benchmark Base Allocated Diff Allocated Change Change %
Benchmarks.Trace.CharSliceBenchmark.OptimizedCharSliceWithPool‑net6.0 640 B 304 B -336 B -52.50%
Benchmarks.Trace.CharSliceBenchmark.OptimizedCharSlice‑net6.0 976 B 304 B -672 B -68.85%

Raw results

Branch Method Toolchain Mean StdError StdDev Gen 0 Gen 1 Gen 2 Allocated
master OriginalCharSlice net6.0 1.89ms 1.31μs 4.9μs 0 0 0 640.3 KB
master OriginalCharSlice netcoreapp3.1 3.99ms 1.21μs 4.19μs 0 0 0 640.1 KB
master OriginalCharSlice net472 2.66ms 1.44μs 5.59μs 0 0 0 647.17 KB
master OptimizedCharSlice net6.0 1.42ms 502ns 1.74μs 0 0 0 976 B
master OptimizedCharSlice netcoreapp3.1 2.77ms 1.17μs 4.53μs 0 0 0 104 B
master OptimizedCharSlice net472 1.92ms 1.12μs 4.18μs 0 0 0 0 b
master OptimizedCharSliceWithPool net6.0 1.01ms 744ns 2.88μs 0 0 0 640 B
master OptimizedCharSliceWithPool netcoreapp3.1 1.92ms 1.72μs 6.64μs 0 0 0 104 B
master OptimizedCharSliceWithPool net472 1.14ms 273ns 946ns 0 0 0 0 b
#7882 OriginalCharSlice net6.0 2.06ms 2.02μs 7.57μs 0 0 0 640.98 KB
#7882 OriginalCharSlice netcoreapp3.1 4.03ms 1.82μs 6.57μs 0 0 0 640.1 KB
#7882 OriginalCharSlice net472 2.67ms 594ns 2.3μs 0 0 0 647.17 KB
#7882 OptimizedCharSlice net6.0 1.48ms 717ns 2.78μs 0 0 0 304 B
#7882 OptimizedCharSlice netcoreapp3.1 3.01ms 2.21μs 8.58μs 0 0 0 104 B
#7882 OptimizedCharSlice net472 1.92ms 1.48μs 5.72μs 0 0 0 0 b
#7882 OptimizedCharSliceWithPool net6.0 1.07ms 736ns 2.85μs 0 0 0 304 B
#7882 OptimizedCharSliceWithPool netcoreapp3.1 1.88ms 3.14μs 12.1μs 0 0 0 104 B
#7882 OptimizedCharSliceWithPool net472 1.25ms 615ns 2.13μs 0 0 0 0 b
Benchmarks.Trace.CIVisibilityProtocolWriterBenchmark - Same speed ✔️ Same allocations ✔️

Raw results

Branch Method Toolchain Mean StdError StdDev Gen 0 Gen 1 Gen 2 Allocated
master WriteAndFlushEnrichedTraces net6.0 714μs 3.63μs 16.6μs 0 0 0 42.23 KB
master WriteAndFlushEnrichedTraces netcoreapp3.1 746μs 4.1μs 24.9μs 0 0 0 41.86 KB
master WriteAndFlushEnrichedTraces net472 939μs 4.3μs 23.2μs 4.46 0 0 55.79 KB
#7882 WriteAndFlushEnrichedTraces net6.0 746μs 3.15μs 12.2μs 0 0 0 42.05 KB
#7882 WriteAndFlushEnrichedTraces netcoreapp3.1 818μs 3.88μs 15μs 0 0 0 41.75 KB
#7882 WriteAndFlushEnrichedTraces net472 1.01ms 5.04μs 26.2μs 4.81 0 0 56.02 KB
Benchmarks.Trace.DbCommandBenchmark - Same speed ✔️ Same allocations ✔️

Raw results

Branch Method Toolchain Mean StdError StdDev Gen 0 Gen 1 Gen 2 Allocated
master ExecuteNonQuery net6.0 1.91μs 8.67ns 33.6ns 0 0 0 1.02 KB
master ExecuteNonQuery netcoreapp3.1 2.53μs 5.29ns 20.5ns 0 0 0 1.02 KB
master ExecuteNonQuery net472 2.74μs 3.2ns 12.4ns 0.151 0 0 987 B
#7882 ExecuteNonQuery net6.0 1.84μs 8.85ns 37.5ns 0 0 0 1.02 KB
#7882 ExecuteNonQuery netcoreapp3.1 2.59μs 12.1ns 46.8ns 0 0 0 1.02 KB
#7882 ExecuteNonQuery net472 2.89μs 4.22ns 16.3ns 0.148 0 0 987 B
Benchmarks.Trace.ElasticsearchBenchmark - Same speed ✔️ Same allocations ✔️

Raw results

Branch Method Toolchain Mean StdError StdDev Gen 0 Gen 1 Gen 2 Allocated
master CallElasticsearch net6.0 1.75μs 9.2ns 44.1ns 0 0 0 1.03 KB
master CallElasticsearch netcoreapp3.1 2.26μs 10.9ns 45.1ns 0 0 0 1.03 KB
master CallElasticsearch net472 3.48μs 1.22ns 4.56ns 0.157 0 0 1.04 KB
master CallElasticsearchAsync net6.0 1.87μs 7.73ns 30ns 0 0 0 1.01 KB
master CallElasticsearchAsync netcoreapp3.1 2.41μs 7.48ns 25.9ns 0 0 0 1.08 KB
master CallElasticsearchAsync net472 3.69μs 4.57ns 17.7ns 0.167 0 0 1.1 KB
#7882 CallElasticsearch net6.0 1.72μs 9.33ns 49.4ns 0 0 0 1.03 KB
#7882 CallElasticsearch netcoreapp3.1 2.34μs 8.12ns 31.4ns 0 0 0 1.03 KB
#7882 CallElasticsearch net472 3.41μs 2.93ns 11.4ns 0.154 0 0 1.04 KB
#7882 CallElasticsearchAsync net6.0 1.8μs 0.949ns 3.55ns 0 0 0 1.01 KB
#7882 CallElasticsearchAsync netcoreapp3.1 2.56μs 7.73ns 29.9ns 0 0 0 1.08 KB
#7882 CallElasticsearchAsync net472 3.78μs 3.72ns 14.4ns 0.172 0 0 1.1 KB
Benchmarks.Trace.GraphQLBenchmark - Same speed ✔️ Same allocations ✔️

Raw results

Branch Method Toolchain Mean StdError StdDev Gen 0 Gen 1 Gen 2 Allocated
master ExecuteAsync net6.0 1.92μs 7.18ns 27.8ns 0 0 0 952 B
master ExecuteAsync netcoreapp3.1 2.45μs 8.55ns 33.1ns 0 0 0 952 B
master ExecuteAsync net472 2.58μs 2.98ns 11.5ns 0.141 0 0 915 B
#7882 ExecuteAsync net6.0 1.85μs 0.486ns 1.75ns 0 0 0 952 B
#7882 ExecuteAsync netcoreapp3.1 2.42μs 11.7ns 45.2ns 0 0 0 952 B
#7882 ExecuteAsync net472 2.54μs 1.44ns 5.38ns 0.14 0 0 915 B
Benchmarks.Trace.HttpClientBenchmark - Same speed ✔️ Same allocations ✔️

Raw results

Branch Method Toolchain Mean StdError StdDev Gen 0 Gen 1 Gen 2 Allocated
master SendAsync net6.0 7μs 11.5ns 44.4ns 0 0 0 2.36 KB
master SendAsync netcoreapp3.1 8.7μs 19.8ns 76.9ns 0 0 0 2.9 KB
master SendAsync net472 12.3μs 7.96ns 29.8ns 0.489 0 0 3.18 KB
#7882 SendAsync net6.0 7.19μs 3.48ns 12.1ns 0 0 0 2.36 KB
#7882 SendAsync netcoreapp3.1 8.82μs 6.96ns 27ns 0 0 0 2.9 KB
#7882 SendAsync net472 12.4μs 13.5ns 52.4ns 0.492 0 0 3.18 KB
Benchmarks.Trace.Iast.StringAspectsBenchmark - Same speed ✔️ More allocations ⚠️

More allocations ⚠️ in #7882

Benchmark Base Allocated Diff Allocated Change Change %
Benchmarks.Trace.Iast.StringAspectsBenchmark.StringConcatAspectBenchmark‑net472 278.53 KB 286.72 KB 8.19 KB 2.94%
Benchmarks.Trace.Iast.StringAspectsBenchmark.StringConcatAspectBenchmark‑netcoreapp3.1 273.14 KB 276.28 KB 3.14 KB 1.15%

Fewer allocations 🎉 in #7882

Benchmark Base Allocated Diff Allocated Change Change %
Benchmarks.Trace.Iast.StringAspectsBenchmark.StringConcatAspectBenchmark‑net6.0 273.29 KB 257.51 KB -15.78 KB -5.77%

Raw results

Branch Method Toolchain Mean StdError StdDev Gen 0 Gen 1 Gen 2 Allocated
master StringConcatBenchmark net6.0 45μs 304ns 2.83μs 0 0 0 43.78 KB
master StringConcatBenchmark netcoreapp3.1 52.5μs 503ns 4.91μs 0 0 0 43.09 KB
master StringConcatBenchmark net472 57μs 278ns 1.15μs 0 0 0 57.34 KB
master StringConcatAspectBenchmark net6.0 460μs 1.51μs 8.01μs 0 0 0 273.29 KB
master StringConcatAspectBenchmark netcoreapp3.1 510μs 2.39μs 10.7μs 0 0 0 273.14 KB
master StringConcatAspectBenchmark net472 411μs 1.98μs 14.3μs 0 0 0 278.53 KB
#7882 StringConcatBenchmark net6.0 44.6μs 236ns 1.23μs 0 0 0 43.64 KB
#7882 StringConcatBenchmark netcoreapp3.1 49μs 279ns 1.93μs 0 0 0 43.22 KB
#7882 StringConcatBenchmark net472 56.2μs 269ns 1.04μs 0 0 0 57.34 KB
#7882 StringConcatAspectBenchmark net6.0 465μs 2.21μs 9.38μs 0 0 0 257.51 KB
#7882 StringConcatAspectBenchmark netcoreapp3.1 524μs 1.3μs 4.69μs 0 0 0 276.28 KB
#7882 StringConcatAspectBenchmark net472 400μs 2.02μs 9.25μs 0 0 0 286.72 KB
Benchmarks.Trace.ILoggerBenchmark - Same speed ✔️ Same allocations ✔️

Raw results

Branch Method Toolchain Mean StdError StdDev Gen 0 Gen 1 Gen 2 Allocated
master EnrichedLog net6.0 2.71μs 13.2ns 54.3ns 0 0 0 1.7 KB
master EnrichedLog netcoreapp3.1 3.62μs 12.5ns 48.5ns 0 0 0 1.7 KB
master EnrichedLog net472 3.95μs 6.07ns 23.5ns 0.254 0 0 1.64 KB
#7882 EnrichedLog net6.0 2.77μs 7.87ns 30.5ns 0 0 0 1.7 KB
#7882 EnrichedLog netcoreapp3.1 3.61μs 12.1ns 45.2ns 0 0 0 1.7 KB
#7882 EnrichedLog net472 3.9μs 4.28ns 16.6ns 0.252 0 0 1.64 KB
Benchmarks.Trace.Log4netBenchmark - Same speed ✔️ Same allocations ✔️

Raw results

Branch Method Toolchain Mean StdError StdDev Gen 0 Gen 1 Gen 2 Allocated
master EnrichedLog net6.0 123μs 78.6ns 305ns 0 0 0 4.31 KB
master EnrichedLog netcoreapp3.1 126μs 43ns 161ns 0 0 0 4.31 KB
master EnrichedLog net472 165μs 56.7ns 204ns 0 0 0 4.51 KB
#7882 EnrichedLog net6.0 124μs 73.6ns 265ns 0 0 0 4.31 KB
#7882 EnrichedLog netcoreapp3.1 127μs 185ns 717ns 0 0 0 4.31 KB
#7882 EnrichedLog net472 167μs 106ns 410ns 0 0 0 4.51 KB
Benchmarks.Trace.NLogBenchmark - Same speed ✔️ Same allocations ✔️

Raw results

Branch Method Toolchain Mean StdError StdDev Gen 0 Gen 1 Gen 2 Allocated
master EnrichedLog net6.0 5.17μs 21.3ns 82.6ns 0 0 0 2.26 KB
master EnrichedLog netcoreapp3.1 7.06μs 15.3ns 57.1ns 0 0 0 2.26 KB
master EnrichedLog net472 7.73μs 5.53ns 21.4ns 0.307 0 0 2.08 KB
#7882 EnrichedLog net6.0 4.91μs 4.08ns 15.8ns 0 0 0 2.26 KB
#7882 EnrichedLog netcoreapp3.1 7.08μs 19.8ns 76.8ns 0 0 0 2.26 KB
#7882 EnrichedLog net472 7.64μs 9.05ns 35.1ns 0.306 0 0 2.08 KB
Benchmarks.Trace.RedisBenchmark - Same speed ✔️ Same allocations ✔️

Raw results

Branch Method Toolchain Mean StdError StdDev Gen 0 Gen 1 Gen 2 Allocated
master SendReceive net6.0 2.15μs 1.92ns 7.2ns 0 0 0 1.2 KB
master SendReceive netcoreapp3.1 2.65μs 12.6ns 52ns 0 0 0 1.2 KB
master SendReceive net472 3.11μs 5.92ns 22.9ns 0.185 0 0 1.2 KB
#7882 SendReceive net6.0 1.96μs 10ns 45.8ns 0 0 0 1.2 KB
#7882 SendReceive netcoreapp3.1 2.54μs 12.6ns 50.4ns 0 0 0 1.2 KB
#7882 SendReceive net472 3.25μs 2.86ns 11.1ns 0.179 0 0 1.2 KB
Benchmarks.Trace.SerilogBenchmark - Same speed ✔️ Same allocations ✔️

Raw results

Branch Method Toolchain Mean StdError StdDev Gen 0 Gen 1 Gen 2 Allocated
master EnrichedLog net6.0 4.36μs 11.4ns 44.1ns 0 0 0 1.58 KB
master EnrichedLog netcoreapp3.1 5.82μs 16.5ns 64ns 0 0 0 1.63 KB
master EnrichedLog net472 6.56μs 9.44ns 36.5ns 0.299 0 0 2.03 KB
#7882 EnrichedLog net6.0 4.39μs 16.2ns 62.9ns 0 0 0 1.58 KB
#7882 EnrichedLog netcoreapp3.1 5.79μs 7.22ns 27ns 0 0 0 1.63 KB
#7882 EnrichedLog net472 6.62μs 6.57ns 25.4ns 0.297 0 0 2.03 KB
Benchmarks.Trace.SpanBenchmark - Same speed ✔️ Same allocations ✔️

Raw results

Branch Method Toolchain Mean StdError StdDev Gen 0 Gen 1 Gen 2 Allocated
master StartFinishSpan net6.0 782ns 3.87ns 16.4ns 0 0 0 576 B
master StartFinishSpan netcoreapp3.1 984ns 4.62ns 17.9ns 0 0 0 576 B
master StartFinishSpan net472 917ns 0.137ns 0.514ns 0.0879 0 0 578 B
master StartFinishScope net6.0 932ns 4.67ns 20.9ns 0 0 0 696 B
master StartFinishScope netcoreapp3.1 1.17μs 5.15ns 19.3ns 0 0 0 696 B
master StartFinishScope net472 1.13μs 0.23ns 0.86ns 0.102 0 0 658 B
master StartFinishTwoScopes net6.0 1.73μs 9.05ns 43.4ns 0 0 0 1.19 KB
master StartFinishTwoScopes netcoreapp3.1 2.24μs 11.4ns 54.5ns 0 0 0 1.19 KB
master StartFinishTwoScopes net472 2.2μs 1.84ns 6.89ns 0.166 0 0 1.08 KB
#7882 StartFinishSpan net6.0 791ns 3.02ns 11.3ns 0 0 0 576 B
#7882 StartFinishSpan netcoreapp3.1 978ns 4.83ns 21.6ns 0 0 0 576 B
#7882 StartFinishSpan net472 908ns 0.104ns 0.389ns 0.0908 0 0 578 B
#7882 StartFinishScope net6.0 947ns 0.651ns 2.52ns 0 0 0 696 B
#7882 StartFinishScope netcoreapp3.1 1.17μs 5.95ns 27.9ns 0 0 0 696 B
#7882 StartFinishScope net472 1.11μs 0.735ns 2.85ns 0.0996 0 0 658 B
#7882 StartFinishTwoScopes net6.0 1.72μs 9.39ns 53.1ns 0 0 0 1.19 KB
#7882 StartFinishTwoScopes netcoreapp3.1 2.24μs 11.2ns 51.5ns 0 0 0 1.19 KB
#7882 StartFinishTwoScopes net472 2.17μs 2.34ns 9.05ns 0.162 0 0 1.08 KB
Benchmarks.Trace.TraceAnnotationsBenchmark - Same speed ✔️ Same allocations ✔️

Raw results

Branch Method Toolchain Mean StdError StdDev Gen 0 Gen 1 Gen 2 Allocated
master RunOnMethodBegin net6.0 1.1μs 1.17ns 4.52ns 0 0 0 696 B
master RunOnMethodBegin netcoreapp3.1 1.42μs 7.26ns 33.3ns 0 0 0 696 B
master RunOnMethodBegin net472 1.43μs 1.4ns 5.43ns 0.101 0 0 658 B
#7882 RunOnMethodBegin net6.0 1.1μs 5.36ns 22.8ns 0 0 0 696 B
#7882 RunOnMethodBegin netcoreapp3.1 1.43μs 6.73ns 27.8ns 0 0 0 696 B
#7882 RunOnMethodBegin net472 1.44μs 0.43ns 1.67ns 0.101 0 0 658 B

@andrewlock andrewlock enabled auto-merge (squash) December 1, 2025 16:41
@andrewlock andrewlock merged commit 181939c into master Dec 1, 2025
148 of 150 checks passed
@andrewlock andrewlock deleted the andrew/fix-agent-writer-benchmark branch December 1, 2025 17:51
@github-actions github-actions bot added this to the vNext-v3 milestone Dec 1, 2025
andrewlock added a commit that referenced this pull request Dec 1, 2025
…7884)

## Summary of changes

- Fixes "incorrect" generated code from `TagsList`
  - Removes significant additional overhead during serialization
- "Fix" vendored System.Buffers code to avoid the same issue

## Reason for change

The current generated code for `TagsList` produces something like this:

```csharp
private static ReadOnlySpan<byte> DbTypeBytes => new byte[] { 167, 100, 98, 46, 116, 121, 112, 101 };
```

This _looks_ like it's allocating a new `byte[]` with every invocation,
but the compiler actually optimizes this away to be completely
zero-allocation, by embedding the array as part of the dll, and then
simply returning a `ReadOnlySpan` wrapper pointing to this fixed data.
You can see this if you look at the generated IL:

```
  .method private hidebysig static specialname valuetype [System.Runtime]System.ReadOnlySpan`1<unsigned int8>
    get_DbTypeBytes() cil managed
  {
    .maxstack 8

    // [20 58 - 20 109]
    IL_0000: ldsflda      int64 '<PrivateImplementationDetails>'::A06A154BE3B860D0B56FA96C93523B732045BA0BCE2FFD4769109575CF1953BF
    IL_0005: ldc.i4.8
    IL_0006: newobj       instance void valuetype [System.Runtime]System.ReadOnlySpan`1<unsigned int8>::.ctor(void*, int32)
    IL_000b: ret

  } // end of method SqlTags::get_DbTypeBytes
```

However, in .NET Framework, even though we have vendored
`ReadOnlySpan<T>` so we can get some of the benefits (mostly cleaner
code), we _don't_ get these benefits. Which means that the above code
_does_ generate a new array with every invocation:

```
  .method private hidebysig static specialname valuetype Datadog.Trace.VendoredMicrosoftCode.System.ReadOnlySpan`1<unsigned int8>
    get_DbTypeBytes() cil managed
  {
    .maxstack 8

    // [20 58 - 20 109]
    IL_0000: ldc.i4.8
    IL_0001: newarr       [netstandard]System.Byte
    IL_0006: dup
    IL_0007: ldtoken      field int64 '<PrivateImplementationDetails>'::A06A154BE3B860D0B56FA96C93523B732045BA0BCE2FFD4769109575CF1953BF
    IL_000c: call         void [netstandard]System.Runtime.CompilerServices.RuntimeHelpers::InitializeArray(class [netstandard]System.Array, valuetype [netstandard]System.RuntimeFieldHandle)
    IL_0011: call         valuetype Datadog.Trace.VendoredMicrosoftCode.System.ReadOnlySpan`1<!0/*unsigned int8*/> valuetype Datadog.Trace.VendoredMicrosoftCode.System.ReadOnlySpan`1<unsigned int8>::op_Implicit(!0/*unsigned int8*/[])
    IL_0016: ret

  } // end of method SqlTags::get_DbTypeBytes
```

This is... Bad 😅 And it explains the _significant_ serialization
overhead identified in #7882 for .NET Framework. I also confirmed this
applies to all <.NET Core 3.1 too (because we compile for .NET Standard)


| Method | Runtime | Mean | Allocated | Alloc Ratio |
| -------------------------- | -------------------- | -------: |
--------: | ----------: |
| WriteEnrichedTraces_Before | .NET 6.0 | 488.9 us | 110 B | 0.001 |
| WriteEnrichedTraces_Before | .NET Framework 4.7.2 | 703.3 us | 112537
B | 1.000 |
| | | | | |
| WriteEnrichedTraces_After | .NET 6.0 | 469.1 us | 105 B | 0.50 |
| WriteEnrichedTraces_After | .NET Framework 4.7.2 | 703.4 us | 208 B |
1.00 |

## Implementation details

The fix is to just do what we were doing before #5298 introduced this
regression 😄 i.e. generate code like this:

```csharp
#if NETCOREAPP
    private static ReadOnlySpan<byte> DbTypeBytes => new byte[] { 167, 100, 98, 46, 116, 121, 112, 101 };
#else
    private static readonly byte[] DbTypeBytes = new byte[] { 167, 100, 98, 46, 116, 121, 112, 101 
#endif
```

## Test coverage

This is all covered by existing tests, and the new benchmark shows the
improvment

## Other details

I found a couple of other places in the vendored code that has the same
issue, and fixed them directly in the code. However, this is not ideal,
as if we re-vendor, we'll clobber these updates, so we'll need to update
the vendoring code too
andrewlock added a commit that referenced this pull request Dec 9, 2025
…7884)

## Summary of changes

- Fixes "incorrect" generated code from `TagsList`
  - Removes significant additional overhead during serialization
- "Fix" vendored System.Buffers code to avoid the same issue

## Reason for change

The current generated code for `TagsList` produces something like this:

```csharp
private static ReadOnlySpan<byte> DbTypeBytes => new byte[] { 167, 100, 98, 46, 116, 121, 112, 101 };
```

This _looks_ like it's allocating a new `byte[]` with every invocation,
but the compiler actually optimizes this away to be completely
zero-allocation, by embedding the array as part of the dll, and then
simply returning a `ReadOnlySpan` wrapper pointing to this fixed data.
You can see this if you look at the generated IL:

```
  .method private hidebysig static specialname valuetype [System.Runtime]System.ReadOnlySpan`1<unsigned int8>
    get_DbTypeBytes() cil managed
  {
    .maxstack 8

    // [20 58 - 20 109]
    IL_0000: ldsflda      int64 '<PrivateImplementationDetails>'::A06A154BE3B860D0B56FA96C93523B732045BA0BCE2FFD4769109575CF1953BF
    IL_0005: ldc.i4.8
    IL_0006: newobj       instance void valuetype [System.Runtime]System.ReadOnlySpan`1<unsigned int8>::.ctor(void*, int32)
    IL_000b: ret

  } // end of method SqlTags::get_DbTypeBytes
```

However, in .NET Framework, even though we have vendored
`ReadOnlySpan<T>` so we can get some of the benefits (mostly cleaner
code), we _don't_ get these benefits. Which means that the above code
_does_ generate a new array with every invocation:

```
  .method private hidebysig static specialname valuetype Datadog.Trace.VendoredMicrosoftCode.System.ReadOnlySpan`1<unsigned int8>
    get_DbTypeBytes() cil managed
  {
    .maxstack 8

    // [20 58 - 20 109]
    IL_0000: ldc.i4.8
    IL_0001: newarr       [netstandard]System.Byte
    IL_0006: dup
    IL_0007: ldtoken      field int64 '<PrivateImplementationDetails>'::A06A154BE3B860D0B56FA96C93523B732045BA0BCE2FFD4769109575CF1953BF
    IL_000c: call         void [netstandard]System.Runtime.CompilerServices.RuntimeHelpers::InitializeArray(class [netstandard]System.Array, valuetype [netstandard]System.RuntimeFieldHandle)
    IL_0011: call         valuetype Datadog.Trace.VendoredMicrosoftCode.System.ReadOnlySpan`1<!0/*unsigned int8*/> valuetype Datadog.Trace.VendoredMicrosoftCode.System.ReadOnlySpan`1<unsigned int8>::op_Implicit(!0/*unsigned int8*/[])
    IL_0016: ret

  } // end of method SqlTags::get_DbTypeBytes
```

This is... Bad 😅 And it explains the _significant_ serialization
overhead identified in #7882 for .NET Framework. I also confirmed this
applies to all <.NET Core 3.1 too (because we compile for .NET Standard)


| Method | Runtime | Mean | Allocated | Alloc Ratio |
| -------------------------- | -------------------- | -------: |
--------: | ----------: |
| WriteEnrichedTraces_Before | .NET 6.0 | 488.9 us | 110 B | 0.001 |
| WriteEnrichedTraces_Before | .NET Framework 4.7.2 | 703.3 us | 112537
B | 1.000 |
| | | | | |
| WriteEnrichedTraces_After | .NET 6.0 | 469.1 us | 105 B | 0.50 |
| WriteEnrichedTraces_After | .NET Framework 4.7.2 | 703.4 us | 208 B |
1.00 |

## Implementation details

The fix is to just do what we were doing before #5298 introduced this
regression 😄 i.e. generate code like this:

```csharp
#if NETCOREAPP
    private static ReadOnlySpan<byte> DbTypeBytes => new byte[] { 167, 100, 98, 46, 116, 121, 112, 101 };
#else
    private static readonly byte[] DbTypeBytes = new byte[] { 167, 100, 98, 46, 116, 121, 112, 101 
#endif
```

## Test coverage

This is all covered by existing tests, and the new benchmark shows the
improvment

## Other details

I found a couple of other places in the vendored code that has the same
issue, and fixed them directly in the code. However, this is not ideal,
as if we re-vendor, we'll clobber these updates, so we'll need to update
the vendoring code too
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area:benchmarks Benchmarks, throughput tests, Crank, Bombardier, etc area:tests unit tests, integration tests

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants