Fix Environment.Version causing huge allocation#8068
Conversation
…nt.Version too much (it allocates)
| private static readonly IDatadogLogger Log = DatadogLogging.GetLoggerFor(typeof(ActivityEnumerationHelper)); | ||
|
|
||
| private static readonly bool IsDotNet10 | ||
| #if NET10_0_OR_GREATER |
There was a problem hiding this comment.
We don't build for this now, but maybe we will in the future, so why not
|
|
||
| private static readonly Assembly RootAssembly = typeof(object).Assembly; | ||
|
|
||
| private static readonly Tuple<int, string>[] DotNetFrameworkVersionMapping = |
There was a problem hiding this comment.
Instead of allocating this array, then enumerating it using linq, just converted into a big switch statement below
| private static readonly Lazy<FrameworkDescription> _instance = new(Create); | ||
|
|
||
| public static FrameworkDescription Instance | ||
| { | ||
| get { return _instance ?? (_instance = Create()); } | ||
| } | ||
| public static FrameworkDescription Instance => _instance.Value; |
There was a problem hiding this comment.
I couldn't really see a reason that we were doing this in a "fake lazy" way 🤔 I think we can get rid of the Lazy<T> too, but it seemed safer to not, just in case
| private static string GetNetCoreOrNetFrameworkVersion() | ||
| private static void GetNetCoreOrNetFrameworkVersion(out Version version, out string productVersion) | ||
| { | ||
| string productVersion = null; |
There was a problem hiding this comment.
These changes basically just grab Environment.Version now or if it can't be trusted, try to parse it from the calculated productVersion
|
|
||
| public static bool IsNet5() | ||
| { | ||
| return Environment.Version.Major >= 5; |
There was a problem hiding this comment.
Is this Environment.Version an issue as well?
There was a problem hiding this comment.
Yup, fixed in #8069, just didn't want to delay getting this fix in 😄
BenchmarksBenchmark execution time: 2026-01-15 16:11:41 Comparing candidate commit c83799b in PR branch Found 9 performance improvements and 7 performance regressions! Performance is the same for 164 metrics, 12 unstable metrics. scenario:Benchmarks.Trace.ActivityBenchmark.StartStopWithChild net6.0
scenario:Benchmarks.Trace.ActivityBenchmark.StartStopWithChild netcoreapp3.1
scenario:Benchmarks.Trace.Asm.AppSecBodyBenchmark.AllCycleSimpleBody netcoreapp3.1
scenario:Benchmarks.Trace.Asm.AppSecBodyBenchmark.ObjectExtractorSimpleBody netcoreapp3.1
scenario:Benchmarks.Trace.AspNetCoreBenchmark.SendRequest net6.0
scenario:Benchmarks.Trace.CIVisibilityProtocolWriterBenchmark.WriteAndFlushEnrichedTraces netcoreapp3.1
scenario:Benchmarks.Trace.CharSliceBenchmark.OptimizedCharSliceWithPool net6.0
scenario:Benchmarks.Trace.ElasticsearchBenchmark.CallElasticsearch net6.0
scenario:Benchmarks.Trace.GraphQLBenchmark.ExecuteAsync netcoreapp3.1
scenario:Benchmarks.Trace.Iast.StringAspectsBenchmark.StringConcatBenchmark netcoreapp3.1
scenario:Benchmarks.Trace.RedisBenchmark.SendReceive net472
scenario:Benchmarks.Trace.SpanBenchmark.StartFinishScope net6.0
scenario:Benchmarks.Trace.TraceAnnotationsBenchmark.RunOnMethodBegin netcoreapp3.1
|
Execution-Time Benchmarks Report ⏱️Execution-time results for samples comparing This PR (8068) 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 (8068) - mean (68ms) : 67, 70
master - mean (68ms) : 67, 70
section Bailout
This PR (8068) - mean (72ms) : 71, 73
master - mean (72ms) : 71, 73
section CallTarget+Inlining+NGEN
This PR (8068) - mean (1,020ms) : 935, 1104
master - mean (1,008ms) : 946, 1070
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 (8068) - mean (106ms) : 103, 109
master - mean (106ms) : 104, 109
section Bailout
This PR (8068) - mean (107ms) : 105, 108
master - mean (107ms) : 105, 108
section CallTarget+Inlining+NGEN
This PR (8068) - mean (737ms) : 674, 799
master - mean (754ms) : 729, 778
FakeDbCommand (.NET 6)gantt
title Execution time (ms) FakeDbCommand (.NET 6)
dateFormat x
axisFormat %Q
todayMarker off
section Baseline
This PR (8068) - mean (93ms) : 91, 96
master - mean (94ms) : 91, 97
section Bailout
This PR (8068) - mean (94ms) : 93, 95
master - mean (94ms) : 93, 95
section CallTarget+Inlining+NGEN
This PR (8068) - mean (721ms) : 688, 753
master - mean (719ms) : 694, 743
FakeDbCommand (.NET 8)gantt
title Execution time (ms) FakeDbCommand (.NET 8)
dateFormat x
axisFormat %Q
todayMarker off
section Baseline
This PR (8068) - mean (92ms) : 90, 95
master - mean (92ms) : 90, 94
section Bailout
This PR (8068) - mean (93ms) : 92, 94
master - mean (93ms) : 92, 94
section CallTarget+Inlining+NGEN
This PR (8068) - mean (635ms) : 623, 647
master - mean (634ms) : 618, 650
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 (8068) - mean (196ms) : 188, 203
master - mean (193ms) : 187, 199
section Bailout
This PR (8068) - mean (200ms) : 194, 206
master - mean (196ms) : 193, 200
section CallTarget+Inlining+NGEN
This PR (8068) - mean (1,140ms) : 1080, 1199
master - mean (1,133ms) : 1063, 1203
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 (8068) - mean (279ms) : 273, 285
master - mean (278ms) : 271, 284
section Bailout
This PR (8068) - mean (283ms) : 272, 293
master - mean (277ms) : 273, 282
section CallTarget+Inlining+NGEN
This PR (8068) - mean (944ms) : 903, 984
master - mean (938ms) : 904, 973
HttpMessageHandler (.NET 6)gantt
title Execution time (ms) HttpMessageHandler (.NET 6)
dateFormat x
axisFormat %Q
todayMarker off
section Baseline
This PR (8068) - mean (274ms) : 266, 282
master - mean (270ms) : 266, 274
section Bailout
This PR (8068) - mean (279ms) : 269, 288
master - mean (270ms) : 266, 274
section CallTarget+Inlining+NGEN
This PR (8068) - mean (930ms) : 862, 997
master - mean (929ms) : 880, 977
HttpMessageHandler (.NET 8)gantt
title Execution time (ms) HttpMessageHandler (.NET 8)
dateFormat x
axisFormat %Q
todayMarker off
section Baseline
This PR (8068) - mean (275ms) : 265, 286
master - mean (269ms) : 265, 274
section Bailout
This PR (8068) - mean (273ms) : 268, 278
master - mean (270ms) : 265, 274
section CallTarget+Inlining+NGEN
This PR (8068) - mean (839ms) : 819, 858
master - mean (831ms) : 810, 851
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
| return productVersion; | ||
| } | ||
|
|
||
| private static bool GetDotNetFrameworkProductMapping( |
There was a problem hiding this comment.
This could probably also move to FrameworkDescription.NetFramework.cs to exclude the code entirely for other TFMs.
There was a problem hiding this comment.
So FrameworkDescription.NetFramework is only in .NET Framework, but given the above is needed for .NET Standard 2.0 as well, it's probably correct?
## Summary of changes Replaced remaining usages of `Environment.Version` other than `FrameworkDescription` ## Reason for change `Environment.Version` allocates, but it's used in a bunch of places unnecessarily. Switching to use `FrameworkDescription.Runtimeversion` removes those allocations ## Implementation details - Find everywhere using `Environment.Version` and fix it to not - Inlined a couple of places where it didn't seem necessary - Remove an allocating path in `IsCoreClr()` ## Test coverage Should be covered by existing ## Other details Stacked on - #8068
Summary of changes
Environment.Versionexcept inFrameworkDescriptionActivityEnumerationHelperto useFrameworkDescriptioninstead ofEnvironment.VersionReason for change
Environment.Versionallocates every time you access it. So my "improve performance by checking if we're in .NET 10" PR caused a massive allocation regression 💀 😂So I think it makes sense to cache
Environment.Versionin ourFrameworkDescriptiontype, and access it from there.Implementation details
Environment.Versionfor the app inFrameworkDescriptionFrameworkDescription.RuntimeVersion#nullable enableseeing as I need to think about all that properly anywayTest coverage
This fixes the benchmarks again now 🙄 Everything else should be covered by existing, but I'll likely follow up with some more tests in a follow up PR (just don't want to delay this getting in!)
Other details
The benchmarks did show the regression in the PR #8058... but I wasn't looking, because those numbers are so flaky 😬