Minor aspnetcore perf improvements#8210
Conversation
Execution-Time Benchmarks Report ⏱️Execution-time results for samples comparing This PR (8210) and master.
|
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Metric | Master (Mean ± 95% CI) | Current (Mean ± 95% CI) | Change | Status |
|---|---|---|---|---|
| .NET Framework 4.8 - Baseline | ||||
| duration | 203.03 ± (202.87 - 203.75) ms | 225.04 ± (224.08 - 226.62) ms | +10.8% | ❌⬆️ |
| .NET Framework 4.8 - Bailout | ||||
| duration | 206.24 ± (206.11 - 206.99) ms | 229.34 ± (228.95 - 231.09) ms | +11.2% | ❌⬆️ |
| .NET Framework 4.8 - CallTarget+Inlining+NGEN | ||||
| duration | 1182.81 ± (1181.42 - 1187.30) ms | 1255.82 ± (1253.55 - 1261.12) ms | +6.2% | ❌⬆️ |
Full Metrics Comparison
FakeDbCommand
| Metric | Master (Mean ± 95% CI) | Current (Mean ± 95% CI) | Change | Status |
|---|---|---|---|---|
| .NET Framework 4.8 - Baseline | ||||
| duration | 71.40 ± (71.35 - 71.63) ms | 76.30 ± (76.32 - 76.62) ms | +6.9% | ✅⬆️ |
| .NET Framework 4.8 - Bailout | ||||
| duration | 76.81 ± (76.76 - 77.14) ms | 80.88 ± (80.79 - 81.16) ms | +5.3% | ✅⬆️ |
| .NET Framework 4.8 - CallTarget+Inlining+NGEN | ||||
| duration | 1071.45 ± (1070.11 - 1076.29) ms | 1096.62 ± (1096.37 - 1101.78) ms | +2.3% | ✅⬆️ |
| .NET Core 3.1 - Baseline | ||||
| process.internal_duration_ms | 23.09 ± (23.04 - 23.15) ms | 23.48 ± (23.42 - 23.54) ms | +1.7% | ✅⬆️ |
| process.time_to_main_ms | 93.73 ± (93.50 - 93.96) ms | 95.95 ± (95.72 - 96.18) ms | +2.4% | ✅⬆️ |
| runtime.dotnet.exceptions.count | 0 ± (0 - 0) | 0 ± (0 - 0) | +0.0% | ✅ |
| runtime.dotnet.mem.committed | 15.45 ± (15.45 - 15.46) MB | 15.49 ± (15.48 - 15.49) MB | +0.2% | ✅⬆️ |
| runtime.dotnet.threads.count | 12 ± (12 - 12) | 12 ± (12 - 12) | +0.0% | ✅ |
| .NET Core 3.1 - Bailout | ||||
| process.internal_duration_ms | 23.17 ± (23.13 - 23.21) ms | 23.46 ± (23.38 - 23.54) ms | +1.3% | ✅⬆️ |
| process.time_to_main_ms | 96.00 ± (95.76 - 96.25) ms | 98.32 ± (97.97 - 98.68) ms | +2.4% | ✅⬆️ |
| runtime.dotnet.exceptions.count | 0 ± (0 - 0) | 0 ± (0 - 0) | +0.0% | ✅ |
| runtime.dotnet.mem.committed | 15.51 ± (15.51 - 15.52) MB | 15.53 ± (15.53 - 15.53) MB | +0.1% | ✅⬆️ |
| runtime.dotnet.threads.count | 13 ± (13 - 13) | 13 ± (13 - 13) | +0.0% | ✅ |
| .NET Core 3.1 - CallTarget+Inlining+NGEN | ||||
| process.internal_duration_ms | 268.62 ± (266.03 - 271.20) ms | 275.12 ± (272.68 - 277.57) ms | +2.4% | ✅⬆️ |
| process.time_to_main_ms | 515.97 ± (515.27 - 516.67) ms | 529.40 ± (528.64 - 530.17) ms | +2.6% | ✅⬆️ |
| runtime.dotnet.exceptions.count | 0 ± (0 - 0) | 0 ± (0 - 0) | +0.0% | ✅ |
| runtime.dotnet.mem.committed | 52.10 ± (52.08 - 52.12) MB | 52.13 ± (52.11 - 52.16) MB | +0.1% | ✅⬆️ |
| runtime.dotnet.threads.count | 28 ± (28 - 28) | 28 ± (28 - 28) | +0.0% | ✅ |
| .NET 6 - Baseline | ||||
| process.internal_duration_ms | 21.83 ± (21.79 - 21.88) ms | 22.32 ± (22.27 - 22.37) ms | +2.2% | ✅⬆️ |
| process.time_to_main_ms | 80.71 ± (80.55 - 80.88) ms | 83.79 ± (83.60 - 83.98) ms | +3.8% | ✅⬆️ |
| runtime.dotnet.exceptions.count | 0 ± (0 - 0) | 0 ± (0 - 0) | +0.0% | ✅ |
| runtime.dotnet.mem.committed | 15.16 ± (15.16 - 15.16) MB | 15.17 ± (15.17 - 15.17) MB | +0.0% | ✅⬆️ |
| runtime.dotnet.threads.count | 10 ± (10 - 10) | 10 ± (10 - 10) | +0.0% | ✅ |
| .NET 6 - Bailout | ||||
| process.internal_duration_ms | 21.82 ± (21.78 - 21.86) ms | 22.30 ± (22.25 - 22.35) ms | +2.2% | ✅⬆️ |
| process.time_to_main_ms | 82.71 ± (82.51 - 82.90) ms | 85.36 ± (85.15 - 85.58) ms | +3.2% | ✅⬆️ |
| runtime.dotnet.exceptions.count | 0 ± (0 - 0) | 0 ± (0 - 0) | +0.0% | ✅ |
| runtime.dotnet.mem.committed | 15.28 ± (15.27 - 15.28) MB | 15.28 ± (15.28 - 15.28) MB | +0.0% | ✅⬆️ |
| runtime.dotnet.threads.count | 11 ± (11 - 11) | 11 ± (11 - 11) | +0.0% | ✅ |
| .NET 6 - CallTarget+Inlining+NGEN | ||||
| process.internal_duration_ms | 267.16 ± (266.35 - 267.96) ms | 273.94 ± (273.12 - 274.76) ms | +2.5% | ✅⬆️ |
| process.time_to_main_ms | 489.54 ± (488.70 - 490.37) ms | 506.87 ± (506.05 - 507.70) ms | +3.5% | ✅⬆️ |
| runtime.dotnet.exceptions.count | 0 ± (0 - 0) | 0 ± (0 - 0) | +0.0% | ✅ |
| runtime.dotnet.mem.committed | 52.92 ± (52.90 - 52.95) MB | 52.90 ± (52.89 - 52.92) MB | -0.0% | ✅ |
| runtime.dotnet.threads.count | 28 ± (28 - 28) | 28 ± (28 - 28) | -0.3% | ✅ |
| .NET 8 - Baseline | ||||
| process.internal_duration_ms | 19.37 ± (19.33 - 19.41) ms | 20.23 ± (20.18 - 20.27) ms | +4.4% | ✅⬆️ |
| process.time_to_main_ms | 72.71 ± (72.54 - 72.87) ms | 76.39 ± (76.23 - 76.55) ms | +5.1% | ✅⬆️ |
| runtime.dotnet.exceptions.count | 0 ± (0 - 0) | 0 ± (0 - 0) | +0.0% | ✅ |
| runtime.dotnet.mem.committed | 7.65 ± (7.64 - 7.66) MB | 7.64 ± (7.64 - 7.64) MB | -0.1% | ✅ |
| runtime.dotnet.threads.count | 10 ± (10 - 10) | 10 ± (10 - 10) | +0.0% | ✅ |
| .NET 8 - Bailout | ||||
| process.internal_duration_ms | 19.38 ± (19.33 - 19.43) ms | 20.23 ± (20.18 - 20.28) ms | +4.4% | ✅⬆️ |
| process.time_to_main_ms | 73.84 ± (73.67 - 74.01) ms | 77.85 ± (77.66 - 78.03) ms | +5.4% | ✅⬆️ |
| runtime.dotnet.exceptions.count | 0 ± (0 - 0) | 0 ± (0 - 0) | +0.0% | ✅ |
| runtime.dotnet.mem.committed | 7.71 ± (7.70 - 7.72) MB | 7.71 ± (7.70 - 7.72) MB | +0.1% | ✅⬆️ |
| runtime.dotnet.threads.count | 11 ± (11 - 11) | 11 ± (11 - 11) | +0.0% | ✅ |
| .NET 8 - CallTarget+Inlining+NGEN | ||||
| process.internal_duration_ms | 187.75 ± (186.83 - 188.67) ms | 192.80 ± (192.11 - 193.49) ms | +2.7% | ✅⬆️ |
| process.time_to_main_ms | 445.02 ± (444.20 - 445.85) ms | 465.22 ± (464.52 - 465.92) ms | +4.5% | ✅⬆️ |
| runtime.dotnet.exceptions.count | 0 ± (0 - 0) | 0 ± (0 - 0) | +0.0% | ✅ |
| runtime.dotnet.mem.committed | 36.00 ± (35.96 - 36.04) MB | 36.16 ± (36.11 - 36.20) MB | +0.4% | ✅⬆️ |
| runtime.dotnet.threads.count | 27 ± (27 - 27) | 27 ± (27 - 27) | -0.0% | ✅ |
HttpMessageHandler
| Metric | Master (Mean ± 95% CI) | Current (Mean ± 95% CI) | Change | Status |
|---|---|---|---|---|
| .NET Framework 4.8 - Baseline | ||||
| duration | 203.03 ± (202.87 - 203.75) ms | 225.04 ± (224.08 - 226.62) ms | +10.8% | ❌⬆️ |
| .NET Framework 4.8 - Bailout | ||||
| duration | 206.24 ± (206.11 - 206.99) ms | 229.34 ± (228.95 - 231.09) ms | +11.2% | ❌⬆️ |
| .NET Framework 4.8 - CallTarget+Inlining+NGEN | ||||
| duration | 1182.81 ± (1181.42 - 1187.30) ms | 1255.82 ± (1253.55 - 1261.12) ms | +6.2% | ❌⬆️ |
| .NET Core 3.1 - Baseline | ||||
| process.internal_duration_ms | 201.34 ± (200.92 - 201.76) ms | 227.03 ± (225.77 - 228.29) ms | +12.8% | ✅⬆️ |
| process.time_to_main_ms | 93.16 ± (92.90 - 93.42) ms | 103.20 ± (102.77 - 103.63) ms | +10.8% | ✅⬆️ |
| runtime.dotnet.exceptions.count | 3 ± (3 - 3) | 3 ± (3 - 3) | +0.0% | ✅ |
| runtime.dotnet.mem.committed | 20.61 ± (20.60 - 20.63) MB | 20.30 ± (20.29 - 20.32) MB | -1.5% | ✅ |
| runtime.dotnet.threads.count | 20 ± (20 - 20) | 20 ± (20 - 20) | +1.7% | ✅⬆️ |
| .NET Core 3.1 - Bailout | ||||
| process.internal_duration_ms | 201.93 ± (201.50 - 202.37) ms | 229.06 ± (227.96 - 230.15) ms | +13.4% | ✅⬆️ |
| process.time_to_main_ms | 94.94 ± (94.58 - 95.30) ms | 106.30 ± (105.83 - 106.77) ms | +12.0% | ✅⬆️ |
| runtime.dotnet.exceptions.count | 3 ± (3 - 3) | 3 ± (3 - 3) | +0.0% | ✅ |
| runtime.dotnet.mem.committed | 20.55 ± (20.53 - 20.57) MB | 20.36 ± (20.35 - 20.38) MB | -0.9% | ✅ |
| runtime.dotnet.threads.count | 21 ± (21 - 21) | 21 ± (21 - 21) | +1.8% | ✅⬆️ |
| .NET Core 3.1 - CallTarget+Inlining+NGEN | ||||
| process.internal_duration_ms | 460.10 ± (458.07 - 462.13) ms | 486.93 ± (483.39 - 490.46) ms | +5.8% | ✅⬆️ |
| process.time_to_main_ms | 524.59 ± (523.77 - 525.41) ms | 563.12 ± (561.78 - 564.46) ms | +7.3% | ✅⬆️ |
| runtime.dotnet.exceptions.count | 3 ± (3 - 3) | 3 ± (3 - 3) | +0.0% | ✅ |
| runtime.dotnet.mem.committed | 61.88 ± (61.75 - 62.01) MB | 60.74 ± (60.58 - 60.89) MB | -1.8% | ✅ |
| runtime.dotnet.threads.count | 29 ± (29 - 29) | 30 ± (30 - 30) | +1.2% | ✅⬆️ |
| .NET 6 - Baseline | ||||
| process.internal_duration_ms | 204.45 ± (203.96 - 204.94) ms | 225.98 ± (224.91 - 227.05) ms | +10.5% | ✅⬆️ |
| process.time_to_main_ms | 75.24 ± (74.97 - 75.51) ms | 81.74 ± (81.43 - 82.04) ms | +8.6% | ✅⬆️ |
| runtime.dotnet.exceptions.count | 4 ± (4 - 4) | 4 ± (4 - 4) | +0.0% | ✅ |
| runtime.dotnet.mem.committed | 16.23 ± (16.21 - 16.26) MB | 16.10 ± (16.08 - 16.12) MB | -0.8% | ✅ |
| runtime.dotnet.threads.count | 19 ± (19 - 19) | 20 ± (20 - 20) | +1.4% | ✅⬆️ |
| .NET 6 - Bailout | ||||
| process.internal_duration_ms | 203.25 ± (202.78 - 203.72) ms | 226.65 ± (225.47 - 227.82) ms | +11.5% | ✅⬆️ |
| process.time_to_main_ms | 75.78 ± (75.58 - 75.98) ms | 82.94 ± (82.60 - 83.29) ms | +9.5% | ✅⬆️ |
| runtime.dotnet.exceptions.count | 4 ± (4 - 4) | 4 ± (4 - 4) | +0.0% | ✅ |
| runtime.dotnet.mem.committed | 16.27 ± (16.25 - 16.29) MB | 16.14 ± (16.12 - 16.15) MB | -0.8% | ✅ |
| runtime.dotnet.threads.count | 20 ± (20 - 20) | 21 ± (21 - 21) | +1.9% | ✅⬆️ |
| .NET 6 - CallTarget+Inlining+NGEN | ||||
| process.internal_duration_ms | 466.38 ± (463.91 - 468.85) ms | 505.63 ± (498.23 - 513.04) ms | +8.4% | ✅⬆️ |
| process.time_to_main_ms | 473.07 ± (472.39 - 473.75) ms | 504.94 ± (503.81 - 506.06) ms | +6.7% | ✅⬆️ |
| runtime.dotnet.exceptions.count | 4 ± (4 - 4) | 4 ± (4 - 4) | +0.0% | ✅ |
| runtime.dotnet.mem.committed | 57.14 ± (57.02 - 57.27) MB | 57.76 ± (57.60 - 57.93) MB | +1.1% | ✅⬆️ |
| runtime.dotnet.threads.count | 30 ± (30 - 30) | 30 ± (30 - 30) | +1.2% | ✅⬆️ |
| .NET 8 - Baseline | ||||
| process.internal_duration_ms | 206.87 ± (206.43 - 207.32) ms | 235.73 ± (234.31 - 237.15) ms | +13.9% | ✅⬆️ |
| process.time_to_main_ms | 81.25 ± (81.01 - 81.49) ms | 88.58 ± (88.22 - 88.94) ms | +9.0% | ✅⬆️ |
| runtime.dotnet.exceptions.count | 4 ± (4 - 4) | 4 ± (4 - 4) | +0.0% | ✅ |
| runtime.dotnet.mem.committed | 16.11 ± (16.10 - 16.13) MB | 15.90 ± (15.88 - 15.92) MB | -1.3% | ✅ |
| runtime.dotnet.threads.count | 19 ± (19 - 19) | 19 ± (19 - 19) | -0.0% | ✅ |
| .NET 8 - Bailout | ||||
| process.internal_duration_ms | 206.14 ± (205.64 - 206.64) ms | 232.88 ± (231.59 - 234.17) ms | +13.0% | ✅⬆️ |
| process.time_to_main_ms | 82.06 ± (81.85 - 82.28) ms | 89.96 ± (89.61 - 90.30) ms | +9.6% | ✅⬆️ |
| runtime.dotnet.exceptions.count | 4 ± (4 - 4) | 4 ± (4 - 4) | +0.0% | ✅ |
| runtime.dotnet.mem.committed | 16.17 ± (16.15 - 16.18) MB | 16.01 ± (15.99 - 16.03) MB | -1.0% | ✅ |
| runtime.dotnet.threads.count | 20 ± (20 - 20) | 20 ± (20 - 20) | +0.0% | ✅⬆️ |
| .NET 8 - CallTarget+Inlining+NGEN | ||||
| process.internal_duration_ms | 414.65 ± (407.90 - 421.39) ms | 514.59 ± (512.70 - 516.48) ms | +24.1% | ✅⬆️ |
| process.time_to_main_ms | 485.34 ± (484.59 - 486.10) ms | 516.13 ± (514.97 - 517.28) ms | +6.3% | ✅⬆️ |
| runtime.dotnet.exceptions.count | 4 ± (4 - 4) | 4 ± (4 - 4) | +0.0% | ✅ |
| runtime.dotnet.mem.committed | 53.78 ± (53.62 - 53.94) MB | 54.27 ± (54.24 - 54.30) MB | +0.9% | ✅⬆️ |
| runtime.dotnet.threads.count | 29 ± (29 - 29) | 29 ± (29 - 29) | -0.9% | ✅ |
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 (8210) - mean (76ms) : 74, 79
master - mean (71ms) : 69, 74
section Bailout
This PR (8210) - mean (81ms) : 79, 83
master - mean (77ms) : 75, 79
section CallTarget+Inlining+NGEN
This PR (8210) - mean (1,099ms) : 1061, 1138
master - mean (1,073ms) : 1028, 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 (8210) - mean (127ms) : 123, 131
master - mean (125ms) : 119, 130
section Bailout
This PR (8210) - mean (129ms) : 124, 135
master - mean (127ms) : 122, 132
section CallTarget+Inlining+NGEN
This PR (8210) - mean (842ms) : 798, 885
master - mean (814ms) : 768, 859
FakeDbCommand (.NET 6)
gantt
title Execution time (ms) FakeDbCommand (.NET 6)
dateFormat x
axisFormat %Q
todayMarker off
section Baseline
This PR (8210) - mean (113ms) : 110, 117
master - mean (109ms) : 106, 113
section Bailout
This PR (8210) - mean (115ms) : 112, 117
master - mean (112ms) : 109, 114
section CallTarget+Inlining+NGEN
This PR (8210) - mean (818ms) : 787, 850
master - mean (791ms) : 762, 820
FakeDbCommand (.NET 8)
gantt
title Execution time (ms) FakeDbCommand (.NET 8)
dateFormat x
axisFormat %Q
todayMarker off
section Baseline
This PR (8210) - mean (105ms) : 102, 108
master - mean (100ms) : 97, 103
section Bailout
This PR (8210) - mean (106ms) : 103, 109
master - mean (101ms) : 99, 103
section CallTarget+Inlining+NGEN
This PR (8210) - mean (690ms) : 664, 717
master - mean (661ms) : 642, 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 (8210) - mean (225ms) : 206, 245
master - mean (203ms) : 198, 209
section Bailout
This PR (8210) - mean (230ms) : crit, 215, 245
master - mean (207ms) : 202, 211
section CallTarget+Inlining+NGEN
This PR (8210) - mean (1,257ms) : crit, 1201, 1314
master - mean (1,184ms) : 1142, 1227
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 (8210) - mean (341ms) : 314, 368
master - mean (304ms) : 295, 313
section Bailout
This PR (8210) - mean (347ms) : crit, 324, 370
master - mean (306ms) : 299, 313
section CallTarget+Inlining+NGEN
This PR (8210) - mean (1,087ms) : crit, 1027, 1147
master - mean (1,018ms) : 981, 1055
HttpMessageHandler (.NET 6)
gantt
title Execution time (ms) HttpMessageHandler (.NET 6)
dateFormat x
axisFormat %Q
todayMarker off
section Baseline
This PR (8210) - mean (318ms) : 300, 336
master - mean (288ms) : 279, 297
section Bailout
This PR (8210) - mean (319ms) : crit, 299, 340
master - mean (288ms) : 278, 297
section CallTarget+Inlining+NGEN
This PR (8210) - mean (1,051ms) : crit, 946, 1156
master - mean (970ms) : 926, 1014
HttpMessageHandler (.NET 8)
gantt
title Execution time (ms) HttpMessageHandler (.NET 8)
dateFormat x
axisFormat %Q
todayMarker off
section Baseline
This PR (8210) - mean (337ms) : 313, 360
master - mean (299ms) : 290, 308
section Bailout
This PR (8210) - mean (335ms) : crit, 312, 357
master - mean (299ms) : 290, 308
section CallTarget+Inlining+NGEN
This PR (8210) - mean (1,070ms) : crit, 1020, 1121
master - mean (939ms) : 832, 1045
| actionName: actionName, | ||
| expandRouteParameters: tracer.Settings.ExpandRouteTemplatesEnabled); | ||
|
|
||
| resourceName = $"{rootSpanTags.HttpMethod} {request.PathBase.ToUriComponent()}{resourcePathName}"; |
There was a problem hiding this comment.
| var resourceName = request.PathBase.HasValue | |
| ? $"{rootSpanTags.HttpMethod} {request.PathBase.ToUriComponent()}{resourcePathName}" | |
| : $"{rootSpanTags.HttpMethod} {resourcePathName}"; |
Same as line 565
There was a problem hiding this comment.
Haha, I literally had that exact commit originally, and removed it 😄 As the formatted components are all strings, and request.PathBase.ToUriComponent() already has if (!request.PathBase.HasValue) { return string.Empty; }, adding the extra if doesn't actually improve things 🙂
| return _extractBehavior switch | ||
| { | ||
| ExtractBehavior.Restart when cumulativeSpanContext is not null => new PropagationContext(default, cumulativeBaggage, [new SpanLink(cumulativeSpanContext, attributes: [new("reason", "propagation_behavior_extract"), new("context_headers", initialExtractorDisplayName!)])]), | ||
| ExtractBehavior.Restart => new PropagationContext(default, cumulativeBaggage, []), |
There was a problem hiding this comment.
| ExtractBehavior.Restart => new PropagationContext(default, cumulativeBaggage, null), |
Instead an empty list, could we just pass null?
There was a problem hiding this comment.
Good catch, thanks! 👍 I was going to look at the propagators in a separate PR though, as there's a bunch of things we can do there I think, I'll make sure to include this 🙂
…t make it readonly
We never set any of the additional tags anyway, so it's just extra allocations
fe8e724 to
fcdf117
Compare
BenchmarksBenchmark execution time: 2026-02-17 16:04:54 Comparing candidate commit fcdf117 in PR branch Found 8 performance improvements and 10 performance regressions! Performance is the same for 163 metrics, 11 unstable metrics. scenario:Benchmarks.Trace.AgentWriterBenchmark.WriteAndFlushEnrichedTraces net6.0
scenario:Benchmarks.Trace.Asm.AppSecBodyBenchmark.AllCycleMoreComplexBody net6.0
scenario:Benchmarks.Trace.Asm.AppSecBodyBenchmark.AllCycleSimpleBody net6.0
scenario:Benchmarks.Trace.Asm.AppSecBodyBenchmark.AllCycleSimpleBody netcoreapp3.1
scenario:Benchmarks.Trace.Asm.AppSecBodyBenchmark.ObjectExtractorSimpleBody net6.0
scenario:Benchmarks.Trace.Asm.AppSecEncoderBenchmark.EncodeLegacyArgs net6.0
scenario:Benchmarks.Trace.AspNetCoreBenchmark.SendRequest net6.0
scenario:Benchmarks.Trace.AspNetCoreBenchmark.SendRequest netcoreapp3.1
scenario:Benchmarks.Trace.CIVisibilityProtocolWriterBenchmark.WriteAndFlushEnrichedTraces net472
scenario:Benchmarks.Trace.CIVisibilityProtocolWriterBenchmark.WriteAndFlushEnrichedTraces net6.0
scenario:Benchmarks.Trace.CIVisibilityProtocolWriterBenchmark.WriteAndFlushEnrichedTraces netcoreapp3.1
scenario:Benchmarks.Trace.CharSliceBenchmark.OptimizedCharSlice net6.0
scenario:Benchmarks.Trace.DbCommandBenchmark.ExecuteNonQuery netcoreapp3.1
scenario:Benchmarks.Trace.ElasticsearchBenchmark.CallElasticsearchAsync net6.0
scenario:Benchmarks.Trace.Iast.StringAspectsBenchmark.StringConcatAspectBenchmark netcoreapp3.1
scenario:Benchmarks.Trace.SingleSpanAspNetCoreBenchmark.SingleSpanAspNetCore net6.0
|
There was a problem hiding this comment.
Pull request overview
This PR makes small allocation/performance improvements in the ASP.NET Core tracing pipeline by simplifying tag objects, lazily allocating extracted span links, and tightening some types.
Changes:
- Reduce ASP.NET Core MVC tags footprint by switching
AspNetCoreMvcTagsto derive fromInstrumentationTags(instead ofWebTags). - Lazily allocate the extracted
SpanLinklist during context extraction to avoid per-request allocations when unused. - Make
http.routederive fromaspnet_core.route(read-only alias) and update generated tag-setters accordingly.
Reviewed changes
Copilot reviewed 8 out of 16 changed files in this pull request and generated 2 comments.
Show a summary per file
| File | Description |
|---|---|
| tracer/test/Datadog.Trace.Tests/Propagators/MultiSpanContextPropagatorTests.cs | Align expectations with Links now being nullable (null-or-empty). |
| tracer/src/Datadog.Trace/Tagging/AspNetCoreTags.cs | Make HttpRoute a computed alias of AspNetCoreRoute. |
| tracer/src/Datadog.Trace/Tagging/AspNetCoreMvcTags.cs | Switch MVC tags to InstrumentationTags + add required tags explicitly. |
| tracer/src/Datadog.Trace/SpanLink.cs | Remove unused logger field (but leaves an unused using). |
| tracer/src/Datadog.Trace/Propagators/SpanContextPropagator.cs | Lazy-create spanLinks; use static lambda for cache factory to avoid captures. |
| tracer/src/Datadog.Trace/PlatformHelpers/AspNetCoreHttpRequestHandler.cs | Simplify private pipeline-scope helper signature to non-generic WebTags. |
| tracer/src/Datadog.Trace/Generated/*/Datadog.Trace.SourceGenerators/TagListGenerator/AspNetCoreTags.g.cs | Update http.route setter behavior for read-only tag. |
| tracer/src/Datadog.Trace/Generated/*/Datadog.Trace.SourceGenerators/TagListGenerator/AspNetCoreMvcTags.g.cs | Add serialization support for new MVC tags (span.kind, component, aspnet_core.route). |
| tracer/src/Datadog.Trace/DiagnosticListeners/AspNetCoreDiagnosticObserver.cs | Remove explicit HttpRoute setting (now derived). |
| Datadog.Trace.Minimal.slnf | Expand the “minimal” solution filter to include additional projects. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Summary of changes
A few minor improvements to the standard
AspNetCoreDiagnosticObserverReason for change
Looking into obvious perf improvements for ASP.NET Core, but only a few minor things stood out (apart from related PRs like #8199 and #8203).
Implementation details
WebTags(we never set those tags anyway)Test coverage
Covered by existing tests, benchmarks show (tiny) allocation gains
Other details
Relates to https://datadoghq.atlassian.net/browse/LANGPLAT-842
Related PRs:
ValueStringBuilderavailable in.NET Core 3.1#8167AspNetCoreResourceNameHelper.SimplifyRouteTemplate#8170AspNetCoreResourceNameHelper.SimplifyRoutePattern#8180UriHelpers.CleanUri#8199HttpRequestUtils.GetUrl()#8203