Skip to content

[OTLP] Use ConditionalWeakTable for cache#7326

Merged
martincostello merged 4 commits into
open-telemetry:mainfrom
martincostello:gh-7303-follow-up
May 21, 2026
Merged

[OTLP] Use ConditionalWeakTable for cache#7326
martincostello merged 4 commits into
open-telemetry:mainfrom
martincostello:gh-7303-follow-up

Conversation

@martincostello
Copy link
Copy Markdown
Member

@martincostello martincostello commented May 20, 2026

#7303 (comment)

Changes

Follow-up to #7303 to use ConditionalWeakTable<K, V> instead of ConcurrentDictionary<K, V>.

@cijothomas does this resolve your concern in the other PR?

Benchmarks

While this PR degrades compared to the original improvements, it is still significantly faster than before the caching was added.

Run with:

dotnet run --project .\test\Benchmarks\ --framework net10.0 --configuration Release -- --memory --filter "*ProtobufOtlpResourceSerializerBenchmarks*" --runtimes net10.0 net462

Copilot Summaries

This PR. vs main

gh-7326 is a mixed bag on .NET 10.0 but a clear regression on .NET Framework 4.6.2. Allocations are unchanged at 0 B in every benchmark, so allocation deltas are 0 B throughout and allocation ratios vs. baseline are n/a because the baseline is zero.

Duration vs. baseline (gh-7326 / main)
Runtime AttributeCount Baseline gh-7326 Delta Ratio
.NET 10.0 0 0.9662 ns 0.3002 ns -0.6660 ns 0.3107x
.NET 10.0 4 11.6641 ns 7.4083 ns -4.2558 ns 0.6351x
.NET 10.0 8 6.8801 ns 8.5426 ns +1.6625 ns 1.2416x
.NET 10.0 16 13.1289 ns 13.3450 ns +0.2161 ns 1.0165x
.NET 10.0 32 13.7314 ns 18.8867 ns +5.1553 ns 1.3754x
.NET Framework 4.6.2 0 26.9956 ns 14.9621 ns -12.0335 ns 0.5542x
.NET Framework 4.6.2 4 26.7247 ns 44.2335 ns +17.5088 ns 1.6552x
.NET Framework 4.6.2 8 26.5985 ns 44.0871 ns +17.4886 ns 1.6575x
.NET Framework 4.6.2 16 26.9786 ns 43.7270 ns +16.7484 ns 1.6208x
.NET Framework 4.6.2 32 28.5463 ns 45.9108 ns +17.3645 ns 1.6083x
High-level summary
Runtime Overall summary
.NET 10.0 Mixed; geomean ratio 0.81x vs. baseline, with wins at attribute counts 0 and 4, near parity at 16, and regressions at 8 and 32
.NET Framework 4.6.2 Regression overall; geomean ratio 1.32x vs. baseline, with only attribute count 0 faster and all other cases ~1.61x-1.66x slower
Allocations
Runtime AttributeCount(s) Baseline gh-7326 Delta Ratio
Both runtimes all 0 B 0 B 0 B n/a

This PR vs. Before 7303

gh-7326 is substantially faster than baseline in 9 of 10 cases, with unchanged allocations. The only regression is .NET Framework 4.6.2, AttributeCount=0, which is 1.40x the baseline duration. All allocation measurements are 0 B vs 0 B, so allocation ratio is 1.00x across the board.

Duration ratio = gh-7326 / baseline; lower than 1.00x is better.

Runtime AttributeCount Baseline gh-7326 Delta Ratio vs baseline
.NET 10.0 0 1.494 ns 0.3002 ns -1.1938 ns 0.20x
.NET 10.0 4 85.817 ns 7.4083 ns -78.4087 ns 0.09x
.NET 10.0 8 165.621 ns 8.5426 ns -157.0784 ns 0.05x
.NET 10.0 16 332.061 ns 13.3450 ns -318.7160 ns 0.04x
.NET 10.0 32 665.643 ns 18.8867 ns -646.7563 ns 0.03x
.NET Framework 4.6.2 0 10.665 ns 14.9621 ns +4.2971 ns 1.40x
.NET Framework 4.6.2 4 354.221 ns 44.2335 ns -309.9875 ns 0.12x
.NET Framework 4.6.2 8 630.657 ns 44.0871 ns -586.5699 ns 0.07x
.NET Framework 4.6.2 16 1,217.009 ns 43.7270 ns -1,173.2820 ns 0.04x
.NET Framework 4.6.2 32 2,487.241 ns 45.9108 ns -2,441.3302 ns 0.02x

Allocation summary

Runtime AttributeCount(s) Baseline gh-7326 Ratio vs baseline
.NET 10.0 0, 4, 8, 16, 32 0 B 0 B 1.00x
.NET Framework 4.6.2 0, 4, 8, 16, 32 0 B 0 B 1.00x

Overall: .NET 10.0 improved across every case, and .NET Framework 4.6.2 improved sharply once AttributeCount >= 4.

Before 7303 2435127

BenchmarkDotNet v0.15.8, Windows 11 (10.0.26200.8457/25H2/2025Update/HudsonValley2)
13th Gen Intel Core i7-13700H 2.90GHz, 1 CPU, 20 logical and 14 physical cores
.NET SDK 10.0.300
  [Host]               : .NET 10.0.8 (10.0.8, 10.0.826.23019), X64 RyuJIT x86-64-v3
  .NET 10.0            : .NET 10.0.8 (10.0.8, 10.0.826.23019), X64 RyuJIT x86-64-v3
  .NET Framework 4.6.2 : .NET Framework 4.8.1 (4.8.9325.0), X64 RyuJIT VectorSize=256
Method Job Runtime AttributeCount Mean Error StdDev Ratio RatioSD Allocated Alloc Ratio
WriteResource .NET 10.0 .NET 10.0 0 1.494 ns 0.0597 ns 0.0776 ns 1.00 0.07 - NA
WriteResource .NET Framework 4.6.2 .NET Framework 4.6.2 0 10.665 ns 0.1284 ns 0.1072 ns 7.16 0.36 - NA
WriteResource .NET 10.0 .NET 10.0 4 85.817 ns 1.7249 ns 2.0534 ns 1.00 0.03 - NA
WriteResource .NET Framework 4.6.2 .NET Framework 4.6.2 4 354.221 ns 5.5426 ns 5.1846 ns 4.13 0.11 - NA
WriteResource .NET 10.0 .NET 10.0 8 165.621 ns 3.3070 ns 3.0933 ns 1.00 0.03 - NA
WriteResource .NET Framework 4.6.2 .NET Framework 4.6.2 8 630.657 ns 10.6901 ns 8.3462 ns 3.81 0.08 - NA
WriteResource .NET 10.0 .NET 10.0 16 332.061 ns 6.4818 ns 9.2960 ns 1.00 0.04 - NA
WriteResource .NET Framework 4.6.2 .NET Framework 4.6.2 16 1,217.009 ns 18.5761 ns 15.5119 ns 3.67 0.11 - NA
WriteResource .NET 10.0 .NET 10.0 32 665.643 ns 10.6954 ns 8.3503 ns 1.00 0.02 - NA
WriteResource .NET Framework 4.6.2 .NET Framework 4.6.2 32 2,487.241 ns 19.5676 ns 15.2771 ns 3.74 0.05 - NA

main (34dcd84)

BenchmarkDotNet v0.15.8, Windows 11 (10.0.26200.8457/25H2/2025Update/HudsonValley2)
13th Gen Intel Core i7-13700H 2.90GHz, 1 CPU, 20 logical and 14 physical cores
.NET SDK 10.0.300
  [Host]               : .NET 10.0.8 (10.0.8, 10.0.826.23019), X64 RyuJIT x86-64-v3
  .NET 10.0            : .NET 10.0.8 (10.0.8, 10.0.826.23019), X64 RyuJIT x86-64-v3
  .NET Framework 4.6.2 : .NET Framework 4.8.1 (4.8.9325.0), X64 RyuJIT VectorSize=256
Method Job Runtime AttributeCount Mean Error StdDev Median Ratio RatioSD Allocated Alloc Ratio
WriteResource .NET 10.0 .NET 10.0 0 0.9662 ns 0.1688 ns 0.4977 ns 1.207 ns 1.48 1.37 - NA
WriteResource .NET Framework 4.6.2 .NET Framework 4.6.2 0 26.9956 ns 0.2246 ns 0.1754 ns 26.920 ns 41.47 28.25 - NA
WriteResource .NET 10.0 .NET 10.0 4 11.6641 ns 1.9201 ns 5.6613 ns 12.461 ns 1.34 1.03 - NA
WriteResource .NET Framework 4.6.2 .NET Framework 4.6.2 4 26.7247 ns 0.6484 ns 1.7308 ns 26.159 ns 3.06 1.68 - NA
WriteResource .NET 10.0 .NET 10.0 8 6.8801 ns 0.1150 ns 0.0961 ns 6.882 ns 1.00 0.02 - NA
WriteResource .NET Framework 4.6.2 .NET Framework 4.6.2 8 26.5985 ns 0.6073 ns 1.7227 ns 25.862 ns 3.87 0.25 - NA
WriteResource .NET 10.0 .NET 10.0 16 13.1289 ns 1.7294 ns 5.0993 ns 9.840 ns 1.13 0.57 - NA
WriteResource .NET Framework 4.6.2 .NET Framework 4.6.2 16 26.9786 ns 0.5636 ns 0.5535 ns 26.822 ns 2.33 0.71 - NA
WriteResource .NET 10.0 .NET 10.0 32 13.7314 ns 0.2236 ns 0.1867 ns 13.822 ns 1.00 0.02 - NA
WriteResource .NET Framework 4.6.2 .NET Framework 4.6.2 32 28.5463 ns 0.2539 ns 0.2120 ns 28.541 ns 2.08 0.03 - NA

This PR

BenchmarkDotNet v0.15.8, Windows 11 (10.0.26200.8457/25H2/2025Update/HudsonValley2)
13th Gen Intel Core i7-13700H 2.90GHz, 1 CPU, 20 logical and 14 physical cores
.NET SDK 10.0.300
  [Host]               : .NET 10.0.8 (10.0.8, 10.0.826.23019), X64 RyuJIT x86-64-v3
  .NET 10.0            : .NET 10.0.8 (10.0.8, 10.0.826.23019), X64 RyuJIT x86-64-v3
  .NET Framework 4.6.2 : .NET Framework 4.8.1 (4.8.9325.0), X64 RyuJIT VectorSize=256
Method Job Runtime AttributeCount Mean Error StdDev Median Ratio RatioSD Allocated Alloc Ratio
WriteResource .NET 10.0 .NET 10.0 0 0.3002 ns 0.0262 ns 0.0218 ns 0.3074 ns 1.01 0.10 - NA
WriteResource .NET Framework 4.6.2 .NET Framework 4.6.2 0 14.9621 ns 0.1515 ns 0.1265 ns 14.9673 ns 50.10 3.66 - NA
WriteResource .NET 10.0 .NET 10.0 4 7.4083 ns 0.1776 ns 0.2490 ns 7.3286 ns 1.00 0.05 - NA
WriteResource .NET Framework 4.6.2 .NET Framework 4.6.2 4 44.2335 ns 0.8414 ns 0.6569 ns 44.2327 ns 5.98 0.21 - NA
WriteResource .NET 10.0 .NET 10.0 8 8.5426 ns 0.1897 ns 0.4043 ns 8.3700 ns 1.00 0.06 - NA
WriteResource .NET Framework 4.6.2 .NET Framework 4.6.2 8 44.0871 ns 0.9005 ns 0.8844 ns 43.8193 ns 5.17 0.25 - NA
WriteResource .NET 10.0 .NET 10.0 16 13.3450 ns 0.2273 ns 0.1774 ns 13.3795 ns 1.00 0.02 - NA
WriteResource .NET Framework 4.6.2 .NET Framework 4.6.2 16 43.7270 ns 0.3424 ns 0.2673 ns 43.6669 ns 3.28 0.05 - NA
WriteResource .NET 10.0 .NET 10.0 32 18.8867 ns 0.2964 ns 0.2475 ns 18.8287 ns 1.00 0.02 - NA
WriteResource .NET Framework 4.6.2 .NET Framework 4.6.2 32 45.9108 ns 0.9248 ns 0.7723 ns 45.7333 ns 2.43 0.05 - NA

Merge requirement checklist

  • CONTRIBUTING guidelines followed (license requirements, nullable enabled, static analysis, etc.)
  • Unit tests added/updated
  • Appropriate CHANGELOG.md files updated for non-trivial changes
  • Changes in public API reviewed (if applicable)

Follow-up to open-telemetry#7303 to use `ConditionalWeakTable<K, V>` instead of `ConcurrentDictionary`<K, V>.
@github-actions github-actions Bot added the pkg:OpenTelemetry.Exporter.OpenTelemetryProtocol Issues related to OpenTelemetry.Exporter.OpenTelemetryProtocol NuGet package label May 20, 2026
@codecov
Copy link
Copy Markdown

codecov Bot commented May 20, 2026

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 89.90%. Comparing base (576fcc0) to head (5c20c87).
⚠️ Report is 1 commits behind head on main.
✅ All tests successful. No failed tests found.

Additional details and impacted files

Impacted file tree graph

@@            Coverage Diff             @@
##             main    #7326      +/-   ##
==========================================
+ Coverage   89.86%   89.90%   +0.03%     
==========================================
  Files         276      276              
  Lines       14006    14007       +1     
==========================================
+ Hits        12587    12593       +6     
+ Misses       1419     1414       -5     
Flag Coverage Δ
unittests-Project-Experimental 89.83% <100.00%> (+<0.01%) ⬆️
unittests-Project-Stable 89.82% <100.00%> (+0.04%) ⬆️

Flags with carried forward coverage won't be shown. Click here to find out more.

Files with missing lines Coverage Δ
...ation/Serializer/ProtobufOtlpResourceSerializer.cs 86.04% <100.00%> (+0.33%) ⬆️

... and 4 files with indirect coverage changes

@martincostello martincostello marked this pull request as ready for review May 20, 2026 11:39
@martincostello martincostello requested a review from a team as a code owner May 20, 2026 11:39
Copilot AI review requested due to automatic review settings May 20, 2026 11:39
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Updates the OTLP protobuf resource serialization cache to avoid retaining Resource instances indefinitely by switching from a ConcurrentDictionary to a ConditionalWeakTable, allowing cached entries to be reclaimed when the Resource key is no longer referenced.

Changes:

  • Replaced the resource-bytes cache from ConcurrentDictionary<Resource, byte[]> to ConditionalWeakTable<Resource, byte[]>.
  • Added framework-conditional cache access (GetOrAdd on net10.0+, GetValue otherwise).

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Copy link
Copy Markdown
Member

@cijothomas cijothomas left a comment

Choose a reason for hiding this comment

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

Thanks. If possible, lets add a test to validate that we don't hold up Resource, if this is easy to test.

@Kielek Kielek added this pull request to the merge queue May 21, 2026
@Kielek Kielek removed this pull request from the merge queue due to a manual request May 21, 2026
Add a unit test that verifies the resource isn't kept alive in the cache.
@martincostello martincostello enabled auto-merge May 21, 2026 08:08
@martincostello martincostello added this pull request to the merge queue May 21, 2026
Merged via the queue into open-telemetry:main with commit 52c320b May 21, 2026
62 checks passed
@martincostello martincostello deleted the gh-7303-follow-up branch May 21, 2026 08:44
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

pkg:OpenTelemetry.Exporter.OpenTelemetryProtocol Issues related to OpenTelemetry.Exporter.OpenTelemetryProtocol NuGet package

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants