Remove allocation and delay in startup due to JSON parsing in Symbol uploader#8589
Conversation
Execution-Time Benchmarks Report ⏱️Execution-time results for samples comparing This PR (8589) 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 (8589) - mean (74ms) : 70, 78
master - mean (73ms) : 70, 77
section Bailout
This PR (8589) - mean (77ms) : 76, 79
master - mean (77ms) : 75, 79
section CallTarget+Inlining+NGEN
This PR (8589) - mean (1,099ms) : 1046, 1152
master - mean (1,099ms) : 1042, 1156
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 (8589) - mean (115ms) : 110, 119
master - mean (115ms) : 111, 119
section Bailout
This PR (8589) - mean (116ms) : 112, 119
master - mean (116ms) : 111, 121
section CallTarget+Inlining+NGEN
This PR (8589) - mean (794ms) : 768, 821
master - mean (788ms) : 765, 812
FakeDbCommand (.NET 6)gantt
title Execution time (ms) FakeDbCommand (.NET 6)
dateFormat x
axisFormat %Q
todayMarker off
section Baseline
This PR (8589) - mean (101ms) : 97, 104
master - mean (102ms) : 97, 107
section Bailout
This PR (8589) - mean (103ms) : 98, 107
master - mean (102ms) : 99, 105
section CallTarget+Inlining+NGEN
This PR (8589) - mean (945ms) : 912, 977
master - mean (944ms) : 900, 987
FakeDbCommand (.NET 8)gantt
title Execution time (ms) FakeDbCommand (.NET 8)
dateFormat x
axisFormat %Q
todayMarker off
section Baseline
This PR (8589) - mean (103ms) : 96, 109
master - mean (103ms) : 98, 108
section Bailout
This PR (8589) - mean (101ms) : 98, 104
master - mean (103ms) : 98, 109
section CallTarget+Inlining+NGEN
This PR (8589) - mean (827ms) : 780, 875
master - mean (826ms) : 783, 869
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 (8589) - mean (196ms) : 191, 202
master - mean (197ms) : 194, 200
section Bailout
This PR (8589) - mean (204ms) : 199, 210
master - mean (200ms) : 197, 202
section CallTarget+Inlining+NGEN
This PR (8589) - mean (1,182ms) : 1145, 1219
master - mean (1,179ms) : 1130, 1228
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 (8589) - mean (285ms) : 271, 298
master - mean (288ms) : 281, 296
section Bailout
This PR (8589) - mean (284ms) : 279, 290
master - mean (283ms) : 277, 288
section CallTarget+Inlining+NGEN
This PR (8589) - mean (953ms) : 929, 976
master - mean (942ms) : 918, 965
HttpMessageHandler (.NET 6)gantt
title Execution time (ms) HttpMessageHandler (.NET 6)
dateFormat x
axisFormat %Q
todayMarker off
section Baseline
This PR (8589) - mean (275ms) : 269, 280
master - mean (272ms) : 267, 277
section Bailout
This PR (8589) - mean (275ms) : 271, 278
master - mean (272ms) : 269, 276
section CallTarget+Inlining+NGEN
This PR (8589) - mean (1,149ms) : 1108, 1189
master - mean (1,138ms) : 1085, 1191
HttpMessageHandler (.NET 8)gantt
title Execution time (ms) HttpMessageHandler (.NET 8)
dateFormat x
axisFormat %Q
todayMarker off
section Baseline
This PR (8589) - mean (269ms) : 264, 274
master - mean (271ms) : 265, 277
section Bailout
This PR (8589) - mean (269ms) : 266, 273
master - mean (272ms) : 269, 276
section CallTarget+Inlining+NGEN
This PR (8589) - mean (1,034ms) : 990, 1077
master - mean (1,034ms) : 989, 1080
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
BenchmarksBenchmark execution time: 2026-05-11 17:50:47 Comparing candidate commit 9a6750a in PR branch Some scenarios are present only in baseline or only in candidate runs. If you didn't create or remove some scenarios in your branch, this maybe a sign of crashed benchmarks 💥💥💥 Scenarios present only in baseline:
Found 2 performance improvements and 4 performance regressions! Performance is the same for 50 metrics, 16 unstable metrics, 86 known flaky benchmarks, 40 flaky benchmarks without significant changes.
|
… for just embedding as a HashSet - Use HashSet<string> instead of ImmutableHashSet<string> as it's faster, and as it's an internal implementation, there's no need. - Remove the "IsValid" path (not necessary) - Move the type to separate folder (as not just used by exception replay) - Add simple unit tests
d56c0d4 to
9a6750a
Compare
Summary of changes
Removes allocation and delay during startup due to JSON parsing in Symbol uploader
Reason for change
After we merged #8565 which enables symbol DB uploading by default, we saw a significant delay during startup (10% impact), even when we ultimately bail out of uploading (due to insufficient agent version, for example).
On analysis, a big part of this is likely due to the third-party-module-names.json file. We were embedding this JSON file in the assembly, and then parsing it using Newtonsoft.JSON, adding each item to a
HashSet<string>, and then building anImmutableHashSet<string>from this.The harness around this process used a
Lazy<bool>to avoid running this parsing immediately, however there were code paths that checkThirdPartyModules.IsValidduring startup, which immediately trigger the loading synchronously on the startup hotpath.Implementation details
Ultimately, the JSON layer is an unnecessary abstraction layer. We can build the
HashSet<string>directly from an embedded collection ofstring, and that avoids all of the parsing overhead, as well asHashSet<string>resizing, and the final copy.Additionally, made some other minor changes:
HashSet<string>instead ofImmutableHashSet<string>as it's faster for lookups, and as it's a private field there's no need for the Immutable guarantees.ThirdPartyModulestype to its own folder (as not just used by exception replay, it's used by the symbol uploads too)Test coverage
Added some simple unit tests for the
ThirdyPartyModulestype, mostly to verify that the hashset can be created correctly (and to codify some assumptions like case sensitivity).