Skip to content

Activity R&D#8357

Draft
andrewlock wants to merge 23 commits into
masterfrom
andrew/r_and_d/activity
Draft

Activity R&D#8357
andrewlock wants to merge 23 commits into
masterfrom
andrew/r_and_d/activity

Conversation

@andrewlock
Copy link
Copy Markdown
Member

Summary of changes

Reason for change

Implementation details

Test coverage

Other details

@pr-commenter
Copy link
Copy Markdown

pr-commenter Bot commented Mar 24, 2026

Benchmarks

Benchmark execution time: 2026-05-14 09:49:35

Comparing candidate commit 48af466 in PR branch andrew/r_and_d/activity with baseline commit cc6678f in branch master.

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 💥💥💥
Check Gitlab CI job log to find if any benchmark has crashed.

Scenarios present only in baseline:

  • Benchmarks.OpenTelemetry.Api.Trace.ActivityBenchmark.StartSpan_SetStatus_Sampled netcoreapp3.1
  • Benchmarks.OpenTelemetry.Api.Trace.TelemetrySpanBenchmark.StartSpan_SetAttributes_Sampled net6.0
  • Benchmarks.OpenTelemetry.Api.Trace.TelemetrySpanBenchmark.StartSpan_SetStatus_Sampled netcoreapp3.1
  • Benchmarks.OpenTelemetry.Api.Trace.ActivityBenchmark.StartSpan_UpdateName_Sampled net6.0
  • Benchmarks.OpenTelemetry.Api.Trace.TelemetrySpanBenchmark.StartSpan_RecordException_Sampled netcoreapp3.1
  • Benchmarks.OpenTelemetry.Api.Trace.ActivityBenchmark.StartSpan_SetAttributes_Sampled netcoreapp3.1
  • Benchmarks.OpenTelemetry.Api.Trace.ActivityBenchmark.StartSpan_AddEvent_Sampled net6.0
  • Benchmarks.OpenTelemetry.Api.Trace.ActivityBenchmark.StartSpan_SetStatus_Sampled net472
  • Benchmarks.OpenTelemetry.Api.Trace.TelemetrySpanBenchmark.StartSpan_GetContext_Sampled net6.0
  • Benchmarks.OpenTelemetry.Api.Trace.TelemetrySpanBenchmark.StartSpan_AddEvent_Sampled net6.0
  • Benchmarks.OpenTelemetry.Api.Trace.ActivityBenchmark.StartSpan_SetAttributes_Sampled net472
  • Benchmarks.OpenTelemetry.Api.Trace.ActivityBenchmark.StartSpan_UpdateName_Sampled netcoreapp3.1
  • Benchmarks.OpenTelemetry.Api.Trace.ActivityBenchmark.StartSpan_AddEvent_Sampled netcoreapp3.1
  • Benchmarks.OpenTelemetry.Api.Trace.TelemetrySpanBenchmark.StartSpan netcoreapp3.1
  • Benchmarks.OpenTelemetry.Api.Trace.ActivityBenchmark.StartSpan net472
  • Benchmarks.OpenTelemetry.Api.Trace.ActivityBenchmark.StartSpan netcoreapp3.1
  • Benchmarks.OpenTelemetry.Api.Trace.ActivityBenchmark.StartSpan_AddEvent_Sampled net472
  • Benchmarks.OpenTelemetry.Api.Trace.TelemetrySpanBenchmark.StartSpan_SetStatus_Sampled net472
  • Benchmarks.OpenTelemetry.Api.Trace.ActivityBenchmark.StartSpan_SetStatus_Sampled net6.0
  • Benchmarks.OpenTelemetry.Api.Trace.TelemetrySpanBenchmark.StartSpan_RecordException_Sampled net472
  • Benchmarks.OpenTelemetry.Api.Trace.TelemetrySpanBenchmark.StartSpan_UpdateName_Sampled netcoreapp3.1
  • Benchmarks.OpenTelemetry.Api.Trace.ActivityBenchmark.StartSpan_GetContext_Sampled net472
  • Benchmarks.OpenTelemetry.Api.Trace.TelemetrySpanBenchmark.StartSpan_SetAttributes_Sampled net472
  • Benchmarks.OpenTelemetry.Api.Trace.ActivityBenchmark.StartSpan_SetAttributes_Sampled net6.0
  • Benchmarks.OpenTelemetry.Api.Trace.TelemetrySpanBenchmark.StartSpan_RecordException_Sampled net6.0
  • Benchmarks.OpenTelemetry.Api.Trace.TelemetrySpanBenchmark.StartSpan net6.0
  • Benchmarks.OpenTelemetry.Api.Trace.TelemetrySpanBenchmark.StartSpan_AddEvent_Sampled net472
  • Benchmarks.OpenTelemetry.Api.Trace.TelemetrySpanBenchmark.StartSpan_GetContext_Sampled net472
  • Benchmarks.OpenTelemetry.Api.Trace.TelemetrySpanBenchmark.StartSpan_SetStatus_Sampled net6.0
  • Benchmarks.OpenTelemetry.Api.Trace.TelemetrySpanBenchmark.StartSpan_UpdateName_Sampled net472
  • Benchmarks.OpenTelemetry.Api.Trace.ActivityBenchmark.StartSpan_GetContext_Sampled net6.0
  • Benchmarks.OpenTelemetry.Api.Trace.ActivityBenchmark.StartSpan_UpdateName_Sampled net472
  • Benchmarks.OpenTelemetry.Api.Trace.ActivityBenchmark.StartSpan_GetContext_Sampled netcoreapp3.1
  • Benchmarks.OpenTelemetry.Api.Trace.TelemetrySpanBenchmark.StartSpan_GetContext_Sampled netcoreapp3.1
  • Benchmarks.OpenTelemetry.Api.Trace.TelemetrySpanBenchmark.StartSpan_UpdateName_Sampled net6.0
  • Benchmarks.OpenTelemetry.Api.Trace.TelemetrySpanBenchmark.StartSpan_SetAttributes_Sampled netcoreapp3.1
  • Benchmarks.OpenTelemetry.Api.Trace.TelemetrySpanBenchmark.StartSpan net472
  • Benchmarks.OpenTelemetry.Api.Trace.TelemetrySpanBenchmark.StartSpan_AddEvent_Sampled netcoreapp3.1
  • Benchmarks.OpenTelemetry.Api.Trace.ActivityBenchmark.StartSpan net6.0

Found 5 performance improvements and 4 performance regressions! Performance is the same for 49 metrics, 14 unstable metrics, 89 known flaky benchmarks, 37 flaky benchmarks without significant changes.

Explanation

This is an A/B test comparing a candidate commit's performance against that of a baseline commit. Performance changes are noted in the tables below as:

  • 🟩 = significantly better candidate vs. baseline
  • 🟥 = significantly worse candidate vs. baseline

We compute a confidence interval (CI) over the relative difference of means between metrics from the candidate and baseline commits, considering the baseline as the reference.

If the CI is entirely outside the configured SIGNIFICANT_IMPACT_THRESHOLD (or the deprecated UNCONFIDENCE_THRESHOLD), the change is considered significant.

Feel free to reach out to #apm-benchmarking-platform on Slack if you have any questions.

More details about the CI and significant changes

You can imagine this CI as a range of values that is likely to contain the true difference of means between the candidate and baseline commits.

CIs of the difference of means are often centered around 0%, because often changes are not that big:

---------------------------------(------|---^--------)-------------------------------->
                              -0.6%    0%  0.3%     +1.2%
                                 |          |        |
         lower bound of the CI --'          |        |
sample mean (center of the CI) -------------'        |
         upper bound of the CI ----------------------'

As described above, a change is considered significant if the CI is entirely outside the configured SIGNIFICANT_IMPACT_THRESHOLD (or the deprecated UNCONFIDENCE_THRESHOLD).

For instance, for an execution time metric, this confidence interval indicates a significantly worse performance:

----------------------------------------|---------|---(---------^---------)---------->
                                       0%        1%  1.3%      2.2%      3.1%
                                                  |   |         |         |
       significant impact threshold --------------'   |         |         |
                      lower bound of CI --------------'         |         |
       sample mean (center of the CI) --------------------------'         |
                      upper bound of CI ----------------------------------'

scenario:Benchmarks.OpenTelemetry.InstrumentedApi.Trace.TracerBenchmark.StartActiveSpan net6.0

  • 🟥 allocated_mem [+143 bytes; +144 bytes] or [+10.108%; +10.117%]

scenario:Benchmarks.OpenTelemetry.InstrumentedApi.Trace.TracerBenchmark.StartRootSpan net6.0

  • 🟩 allocated_mem [-144 bytes; -143 bytes] or [-9.188%; -9.180%]

scenario:Benchmarks.OpenTelemetry.InstrumentedApi.Trace.TracerBenchmark.StartSpan_GetCurrentSpan net6.0

  • 🟩 allocated_mem [-144 bytes; -143 bytes] or [-9.188%; -9.180%]

scenario:Benchmarks.Trace.DbCommandBenchmark.ExecuteNonQuery net6.0

  • 🟥 throughput [-328930.725op/s; -291278.957op/s] or [-62.411%; -55.267%]

scenario:Benchmarks.Trace.HttpClientBenchmark.SendAsync net6.0

  • 🟥 throughput [-69990.761op/s; -62860.029op/s] or [-47.546%; -42.702%]
  • 🟩 execution_time [-41.956ms; -24.383ms] or [-20.820%; -12.100%]

scenario:Benchmarks.Trace.NLogBenchmark.EnrichedLog net6.0

  • 🟥 throughput [-54300.107op/s; -38917.171op/s] or [-27.226%; -19.513%]
  • 🟩 execution_time [-77.736ms; -59.055ms] or [-39.313%; -29.866%]

scenario:Benchmarks.Trace.NLogBenchmark.EnrichedLog netcoreapp3.1

  • 🟩 execution_time [-93.991ms; -91.174ms] or [-47.834%; -46.400%]

Known flaky benchmarks

These benchmarks are marked as flaky and will not trigger a failure. Modify FLAKY_BENCHMARKS_REGEX to control which benchmarks are marked as flaky.

scenario:Benchmarks.OpenTelemetry.InstrumentedApi.Trace.ActivityBenchmark.StartSpan net6.0

  • unstable execution_time [-18.912ms; +8.518ms] or [-13.811%; +6.220%]
  • unstable throughput [-18075.712op/s; +7457.846op/s] or [-10.169%; +4.195%]

scenario:Benchmarks.OpenTelemetry.InstrumentedApi.Trace.ActivityBenchmark.StartSpan netcoreapp3.1

  • unstable execution_time [-35.815ms; -11.574ms] or [-31.928%; -10.318%]

scenario:Benchmarks.OpenTelemetry.InstrumentedApi.Trace.ActivityBenchmark.StartSpan_AddEvent_Sampled net6.0

  • unstable execution_time [-15.237ms; +3.509ms] or [-10.714%; +2.467%]
  • unstable throughput [-11039.255op/s; +7219.164op/s] or [-7.548%; +4.936%]

scenario:Benchmarks.OpenTelemetry.InstrumentedApi.Trace.ActivityBenchmark.StartSpan_AddEvent_Sampled netcoreapp3.1

  • unstable execution_time [-36.164ms; -11.096ms] or [-32.739%; -10.045%]

scenario:Benchmarks.OpenTelemetry.InstrumentedApi.Trace.ActivityBenchmark.StartSpan_GetContext_Sampled net6.0

  • unstable execution_time [+11.560ms; +48.372ms] or [+8.414%; +35.211%]
  • unstable throughput [-42168.668op/s; -13934.294op/s] or [-24.234%; -8.008%]

scenario:Benchmarks.OpenTelemetry.InstrumentedApi.Trace.ActivityBenchmark.StartSpan_GetContext_Sampled netcoreapp3.1

  • unstable execution_time [-34.206ms; -9.180ms] or [-30.807%; -8.267%]

scenario:Benchmarks.OpenTelemetry.InstrumentedApi.Trace.ActivityBenchmark.StartSpan_SetAttributes_Sampled net6.0

  • unstable execution_time [-14480.140µs; +12969.640µs] or [-8.998%; +8.059%]
  • unstable throughput [+6346.801op/s; +23196.755op/s] or [+5.211%; +19.046%]

scenario:Benchmarks.OpenTelemetry.InstrumentedApi.Trace.ActivityBenchmark.StartSpan_SetAttributes_Sampled netcoreapp3.1

  • unstable execution_time [+10.529ms; +34.523ms] or [+11.402%; +37.385%]

scenario:Benchmarks.OpenTelemetry.InstrumentedApi.Trace.ActivityBenchmark.StartSpan_SetStatus_Sampled net6.0

  • unstable execution_time [-31.080ms; +16.873ms] or [-18.142%; +9.849%]
  • unstable throughput [-16037.267op/s; +17089.970op/s] or [-10.591%; +11.286%]

scenario:Benchmarks.OpenTelemetry.InstrumentedApi.Trace.ActivityBenchmark.StartSpan_SetStatus_Sampled netcoreapp3.1

  • unstable execution_time [-17779.632µs; +17152.676µs] or [-15.818%; +15.260%]

scenario:Benchmarks.OpenTelemetry.InstrumentedApi.Trace.ActivityBenchmark.StartSpan_UpdateName_Sampled net6.0

  • unstable execution_time [-50.084ms; -13.095ms] or [-29.860%; -7.807%]
  • unstable throughput [+22665.918op/s; +46557.957op/s] or [+14.522%; +29.830%]

scenario:Benchmarks.OpenTelemetry.InstrumentedApi.Trace.ActivityBenchmark.StartSpan_UpdateName_Sampled netcoreapp3.1

  • unstable execution_time [-81.948ms; -52.706ms] or [-52.638%; -33.854%]

scenario:Benchmarks.OpenTelemetry.InstrumentedApi.Trace.TelemetrySpanBenchmark.StartSpan net6.0

  • unstable execution_time [-42.756ms; +3.361ms] or [-24.652%; +1.938%]
  • unstable throughput [+1260.227op/s; +32460.002op/s] or [+0.833%; +21.451%]

scenario:Benchmarks.OpenTelemetry.InstrumentedApi.Trace.TelemetrySpanBenchmark.StartSpan netcoreapp3.1

  • unstable execution_time [+3.211ms; +28.119ms] or [+3.429%; +30.021%]

scenario:Benchmarks.OpenTelemetry.InstrumentedApi.Trace.TelemetrySpanBenchmark.StartSpan_AddEvent_Sampled net6.0

  • unstable execution_time [-44.247ms; -26.316ms] or [-27.863%; -16.571%]
  • unstable throughput [+37644.941op/s; +61630.644op/s] or [+30.220%; +49.476%]

scenario:Benchmarks.OpenTelemetry.InstrumentedApi.Trace.TelemetrySpanBenchmark.StartSpan_AddEvent_Sampled netcoreapp3.1

  • unstable execution_time [-36.840ms; -10.782ms] or [-33.387%; -9.771%]

scenario:Benchmarks.OpenTelemetry.InstrumentedApi.Trace.TelemetrySpanBenchmark.StartSpan_GetContext_Sampled net6.0

  • unstable execution_time [-44.737ms; -21.092ms] or [-29.093%; -13.716%]
  • unstable throughput [-14640.338op/s; +20712.162op/s] or [-8.584%; +12.145%]

scenario:Benchmarks.OpenTelemetry.InstrumentedApi.Trace.TelemetrySpanBenchmark.StartSpan_GetContext_Sampled netcoreapp3.1

  • unstable execution_time [-19.549ms; +16.144ms] or [-17.695%; +14.613%]

scenario:Benchmarks.OpenTelemetry.InstrumentedApi.Trace.TelemetrySpanBenchmark.StartSpan_RecordException_Sampled net6.0

  • unstable execution_time [-15.481ms; +22.723ms] or [-11.730%; +17.217%]
  • unstable throughput [-12147.598op/s; +16661.486op/s] or [-10.556%; +14.478%]

scenario:Benchmarks.OpenTelemetry.InstrumentedApi.Trace.TelemetrySpanBenchmark.StartSpan_RecordException_Sampled netcoreapp3.1

  • unstable execution_time [-58.635ms; -27.419ms] or [-43.947%; -20.550%]

scenario:Benchmarks.OpenTelemetry.InstrumentedApi.Trace.TelemetrySpanBenchmark.StartSpan_SetAttributes_Sampled net6.0

  • unstable execution_time [+25.606ms; +44.613ms] or [+18.514%; +32.256%]
  • 🟥 throughput [-33036.871op/s; -20702.246op/s] or [-25.300%; -15.854%]

scenario:Benchmarks.OpenTelemetry.InstrumentedApi.Trace.TelemetrySpanBenchmark.StartSpan_SetAttributes_Sampled netcoreapp3.1

  • unstable execution_time [+28.677ms; +59.286ms] or [+31.768%; +65.675%]

scenario:Benchmarks.OpenTelemetry.InstrumentedApi.Trace.TelemetrySpanBenchmark.StartSpan_SetStatus_Sampled net6.0

  • 🟥 allocated_mem [+143 bytes; +144 bytes] or [+9.570%; +9.578%]
  • unstable execution_time [-64.483ms; -28.662ms] or [-35.187%; -15.640%]
  • unstable throughput [+6096.438op/s; +31577.995op/s] or [+4.710%; +24.397%]

scenario:Benchmarks.OpenTelemetry.InstrumentedApi.Trace.TelemetrySpanBenchmark.StartSpan_UpdateName_Sampled net6.0

  • unstable execution_time [-58.507ms; -38.137ms] or [-34.161%; -22.268%]
  • unstable throughput [+23619.390op/s; +42105.119op/s] or [+15.893%; +28.332%]

scenario:Benchmarks.OpenTelemetry.InstrumentedApi.Trace.TelemetrySpanBenchmark.StartSpan_UpdateName_Sampled netcoreapp3.1

  • unstable execution_time [+11.020ms; +36.782ms] or [+12.702%; +42.397%]

scenario:Benchmarks.Trace.ActivityBenchmark.StartStopWithChild net472

  • 🟥 throughput [-9272.530op/s; -8656.260op/s] or [-10.994%; -10.264%]

scenario:Benchmarks.Trace.ActivityBenchmark.StartStopWithChild net6.0

  • 🟩 execution_time [-71.880ms; -51.973ms] or [-35.861%; -25.929%]
  • 🟥 throughput [-42267.849op/s; -31039.512op/s] or [-35.528%; -26.090%]

scenario:Benchmarks.Trace.ActivityBenchmark.StartStopWithChild netcoreapp3.1

  • unstable execution_time [-51.010ms; -22.338ms] or [-25.657%; -11.235%]
  • 🟥 throughput [-6634.788op/s; -5283.877op/s] or [-6.746%; -5.373%]

scenario:Benchmarks.Trace.AgentWriterBenchmark.WriteAndFlushEnrichedTraces net472

  • 🟥 execution_time [+319.759ms; +336.907ms] or [+158.676%; +167.185%]
  • 🟥 throughput [-54.016op/s; -40.529op/s] or [-9.719%; -7.292%]

scenario:Benchmarks.Trace.AgentWriterBenchmark.WriteAndFlushEnrichedTraces net6.0

  • 🟥 execution_time [+97.459ms; +99.052ms] or [+76.998%; +78.258%]
  • 🟩 throughput [+92.725op/s; +98.594op/s] or [+12.225%; +12.999%]

scenario:Benchmarks.Trace.AgentWriterBenchmark.WriteAndFlushEnrichedTraces netcoreapp3.1

  • 🟥 execution_time [+84.226ms; +85.257ms] or [+74.537%; +75.450%]

scenario:Benchmarks.Trace.Asm.AppSecBodyBenchmark.AllCycleMoreComplexBody net472

  • 🟥 allocated_mem [+1.308KB; +1.308KB] or [+27.528%; +27.540%]

scenario:Benchmarks.Trace.Asm.AppSecBodyBenchmark.AllCycleMoreComplexBody net6.0

  • 🟥 allocated_mem [+439 bytes; +440 bytes] or [+9.299%; +9.310%]
  • 🟩 execution_time [-55.549ms; -37.909ms] or [-25.943%; -17.705%]
  • 🟥 throughput [-27478.772op/s; -14619.710op/s] or [-20.058%; -10.672%]

scenario:Benchmarks.Trace.Asm.AppSecBodyBenchmark.AllCycleMoreComplexBody netcoreapp3.1

  • 🟥 allocated_mem [+1.272KB; +1.272KB] or [+27.500%; +27.510%]
  • unstable execution_time [-65.576ms; -44.175ms] or [-31.227%; -21.036%]

scenario:Benchmarks.Trace.Asm.AppSecBodyBenchmark.AllCycleSimpleBody net472

  • 🟥 allocated_mem [+1.307KB; +1.307KB] or [+105.743%; +105.758%]
  • 🟥 throughput [-267013.836op/s; -261570.890op/s] or [-27.263%; -26.708%]

scenario:Benchmarks.Trace.Asm.AppSecBodyBenchmark.AllCycleSimpleBody net6.0

  • 🟥 allocated_mem [+439 bytes; +440 bytes] or [+35.945%; +35.954%]
  • unstable execution_time [-63.777ms; -31.275ms] or [-28.442%; -13.947%]
  • unstable throughput [-361405.815op/s; -225726.421op/s] or [-38.610%; -24.115%]

scenario:Benchmarks.Trace.Asm.AppSecBodyBenchmark.AllCycleSimpleBody netcoreapp3.1

  • 🟥 allocated_mem [+1.272KB; +1.272KB] or [+105.288%; +105.304%]
  • unstable execution_time [-77.294ms; -55.673ms] or [-38.582%; -27.789%]
  • 🟥 throughput [-135341.723op/s; -117148.082op/s] or [-19.446%; -16.832%]

scenario:Benchmarks.Trace.Asm.AppSecBodyBenchmark.ObjectExtractorMoreComplexBody net6.0

  • 🟩 execution_time [-61.551ms; -58.016ms] or [-31.056%; -29.272%]

scenario:Benchmarks.Trace.Asm.AppSecBodyBenchmark.ObjectExtractorMoreComplexBody netcoreapp3.1

  • 🟩 execution_time [-68.674ms; -64.636ms] or [-35.012%; -32.954%]
  • 🟩 throughput [+8505.768op/s; +11198.673op/s] or [+6.776%; +8.921%]

scenario:Benchmarks.Trace.Asm.AppSecBodyBenchmark.ObjectExtractorSimpleBody net6.0

  • 🟩 execution_time [-69.935ms; -52.915ms] or [-34.578%; -26.162%]
  • unstable throughput [+22057.500op/s; +332705.625op/s] or [+0.735%; +11.094%]

scenario:Benchmarks.Trace.Asm.AppSecBodyBenchmark.ObjectExtractorSimpleBody netcoreapp3.1

  • unstable execution_time [-68.140ms; -40.231ms] or [-31.410%; -18.545%]
  • 🟩 throughput [+170853.239op/s; +233360.543op/s] or [+6.782%; +9.263%]

scenario:Benchmarks.Trace.Asm.AppSecEncoderBenchmark.EncodeArgs net472

  • 🟥 execution_time [+300.705ms; +314.637ms] or [+150.252%; +157.213%]

scenario:Benchmarks.Trace.Asm.AppSecEncoderBenchmark.EncodeArgs net6.0

  • unstable execution_time [+144.884ms; +182.848ms] or [+73.065%; +92.210%]

scenario:Benchmarks.Trace.Asm.AppSecEncoderBenchmark.EncodeArgs netcoreapp3.1

  • 🟥 execution_time [+300.560ms; +307.454ms] or [+151.399%; +154.871%]

scenario:Benchmarks.Trace.Asm.AppSecEncoderBenchmark.EncodeLegacyArgs net472

  • 🟥 execution_time [+296.981ms; +310.495ms] or [+145.865%; +152.503%]

scenario:Benchmarks.Trace.Asm.AppSecEncoderBenchmark.EncodeLegacyArgs net6.0

  • 🟥 execution_time [+257.370ms; +275.658ms] or [+125.819%; +134.759%]

scenario:Benchmarks.Trace.Asm.AppSecEncoderBenchmark.EncodeLegacyArgs netcoreapp3.1

  • 🟥 execution_time [+286.044ms; +298.233ms] or [+142.965%; +149.057%]

scenario:Benchmarks.Trace.Asm.AppSecWafBenchmark.RunWafRealisticBenchmarkWithAttack net6.0

  • 🟥 execution_time [+17.931µs; +41.535µs] or [+5.724%; +13.260%]
  • 🟥 throughput [-391.191op/s; -192.509op/s] or [-12.195%; -6.001%]

scenario:Benchmarks.Trace.AspNetCoreBenchmark.SendRequest net472

  • 🟥 execution_time [+300.061ms; +300.842ms] or [+149.761%; +150.151%]

scenario:Benchmarks.Trace.AspNetCoreBenchmark.SendRequest net6.0

  • unstable execution_time [+360.873ms; +380.636ms] or [+392.103%; +413.576%]
  • 🟥 throughput [-6938.656op/s; -6711.151op/s] or [-57.016%; -55.146%]

scenario:Benchmarks.Trace.AspNetCoreBenchmark.SendRequest netcoreapp3.1

  • unstable execution_time [+259.196ms; +300.084ms] or [+196.805%; +227.851%]
  • 🟥 throughput [-1343.949op/s; -1107.566op/s] or [-13.010%; -10.722%]

scenario:Benchmarks.Trace.CIVisibilityProtocolWriterBenchmark.WriteAndFlushEnrichedTraces net472

  • 🟥 execution_time [+298.925ms; +313.032ms] or [+137.443%; +143.929%]
  • 🟥 throughput [-683.149op/s; -666.518op/s] or [-61.900%; -60.393%]

scenario:Benchmarks.Trace.CIVisibilityProtocolWriterBenchmark.WriteAndFlushEnrichedTraces net6.0

  • unstable execution_time [-47.512ms; +86.645ms] or [-20.248%; +36.924%]
  • 🟥 throughput [-719.438op/s; -624.439op/s] or [-47.987%; -41.650%]

scenario:Benchmarks.Trace.CIVisibilityProtocolWriterBenchmark.WriteAndFlushEnrichedTraces netcoreapp3.1

  • 🟥 allocated_mem [+2.305KB; +2.308KB] or [+5.442%; +5.450%]
  • 🟥 execution_time [+336.186ms; +343.638ms] or [+201.078%; +205.535%]
  • 🟥 throughput [-727.180op/s; -693.132op/s] or [-50.633%; -48.262%]

scenario:Benchmarks.Trace.CharSliceBenchmark.OptimizedCharSliceWithPool net6.0

  • 🟩 execution_time [-63.824µs; -55.863µs] or [-5.919%; -5.181%]
  • 🟩 throughput [+50.931op/s; +58.296op/s] or [+5.491%; +6.286%]

scenario:Benchmarks.Trace.CharSliceBenchmark.OriginalCharSlice net6.0

  • 🟩 execution_time [-194.028µs; -183.318µs] or [-9.829%; -9.286%]
  • 🟩 throughput [+51.920op/s; +55.262op/s] or [+10.249%; +10.909%]

scenario:Benchmarks.Trace.ElasticsearchBenchmark.CallElasticsearch net472

  • 🟥 execution_time [+304.638ms; +313.914ms] or [+153.410%; +158.081%]

scenario:Benchmarks.Trace.ElasticsearchBenchmark.CallElasticsearch net6.0

  • 🟥 execution_time [+250.153ms; +260.332ms] or [+125.352%; +130.453%]

scenario:Benchmarks.Trace.ElasticsearchBenchmark.CallElasticsearch netcoreapp3.1

  • 🟥 execution_time [+305.584ms; +314.102ms] or [+153.513%; +157.792%]
  • 🟩 throughput [+25286.658op/s; +38536.928op/s] or [+5.327%; +8.118%]

scenario:Benchmarks.Trace.ElasticsearchBenchmark.CallElasticsearchAsync net472

  • 🟥 execution_time [+306.248ms; +320.895ms] or [+153.787%; +161.143%]

scenario:Benchmarks.Trace.ElasticsearchBenchmark.CallElasticsearchAsync net6.0

  • unstable execution_time [+152.444ms; +198.474ms] or [+75.377%; +98.137%]

scenario:Benchmarks.Trace.ElasticsearchBenchmark.CallElasticsearchAsync netcoreapp3.1

  • 🟥 execution_time [+306.116ms; +313.054ms] or [+155.153%; +158.669%]

scenario:Benchmarks.Trace.GraphQLBenchmark.ExecuteAsync net472

  • 🟥 execution_time [+305.271ms; +316.344ms] or [+153.218%; +158.776%]

scenario:Benchmarks.Trace.GraphQLBenchmark.ExecuteAsync net6.0

  • unstable execution_time [+223.418ms; +286.223ms] or [+111.354%; +142.656%]

scenario:Benchmarks.Trace.GraphQLBenchmark.ExecuteAsync netcoreapp3.1

  • 🟥 execution_time [+301.518ms; +308.620ms] or [+150.002%; +153.536%]

scenario:Benchmarks.Trace.ILoggerBenchmark.EnrichedLog net6.0

  • 🟩 execution_time [-105.187ms; -84.988ms] or [-48.912%; -39.520%]
  • 🟥 throughput [-67712.950op/s; -31517.574op/s] or [-18.576%; -8.646%]

scenario:Benchmarks.Trace.ILoggerBenchmark.EnrichedLog netcoreapp3.1

  • 🟩 execution_time [-98.200ms; -94.022ms] or [-49.258%; -47.163%]

scenario:Benchmarks.Trace.Iast.StringAspectsBenchmark.StringConcatAspectBenchmark net6.0

  • 🟩 allocated_mem [-26.544KB; -26.519KB] or [-9.683%; -9.674%]
  • unstable execution_time [-79.298µs; -25.243µs] or [-15.673%; -4.989%]
  • 🟩 throughput [+122.671op/s; +316.889op/s] or [+6.121%; +15.813%]

scenario:Benchmarks.Trace.Iast.StringAspectsBenchmark.StringConcatAspectBenchmark netcoreapp3.1

  • 🟩 throughput [+104.708op/s; +262.748op/s] or [+5.982%; +15.011%]

scenario:Benchmarks.Trace.Iast.StringAspectsBenchmark.StringConcatBenchmark net6.0

  • unstable execution_time [+4.252µs; +9.506µs] or [+10.051%; +22.470%]
  • 🟥 throughput [-4125.336op/s; -2094.704op/s] or [-17.366%; -8.818%]

scenario:Benchmarks.Trace.Iast.StringAspectsBenchmark.StringConcatBenchmark netcoreapp3.1

  • unstable execution_time [-12.292µs; -2.338µs] or [-19.071%; -3.627%]
  • unstable throughput [+839.560op/s; +2827.332op/s] or [+5.151%; +17.347%]

scenario:Benchmarks.Trace.Log4netBenchmark.EnrichedLog net472

  • 🟥 execution_time [+303.262ms; +315.964ms] or [+153.286%; +159.706%]

scenario:Benchmarks.Trace.Log4netBenchmark.EnrichedLog net6.0

  • 🟥 execution_time [+277.663ms; +296.455ms] or [+141.329%; +150.895%]

scenario:Benchmarks.Trace.Log4netBenchmark.EnrichedLog netcoreapp3.1

  • 🟥 execution_time [+301.232ms; +306.133ms] or [+150.804%; +153.257%]

scenario:Benchmarks.Trace.RedisBenchmark.SendReceive net6.0

  • unstable execution_time [-61.282ms; -29.856ms] or [-30.632%; -14.923%]
  • unstable throughput [-206486.274op/s; -130884.140op/s] or [-39.084%; -24.774%]

scenario:Benchmarks.Trace.RedisBenchmark.SendReceive netcoreapp3.1

  • unstable execution_time [-30.526ms; -5.952ms] or [-15.474%; -3.017%]

scenario:Benchmarks.Trace.SerilogBenchmark.EnrichedLog net472

  • 🟥 execution_time [+301.371ms; +315.391ms] or [+150.206%; +157.194%]

scenario:Benchmarks.Trace.SerilogBenchmark.EnrichedLog net6.0

  • unstable execution_time [+263.129ms; +297.867ms] or [+132.131%; +149.575%]

scenario:Benchmarks.Trace.SerilogBenchmark.EnrichedLog netcoreapp3.1

  • 🟥 execution_time [+303.619ms; +309.257ms] or [+153.976%; +156.835%]

scenario:Benchmarks.Trace.SingleSpanAspNetCoreBenchmark.SingleSpanAspNetCore net472

  • 🟥 execution_time [+300.423ms; +301.576ms] or [+149.853%; +150.428%]
  • 🟩 throughput [+60274917.587op/s; +60700552.009op/s] or [+43.896%; +44.206%]

scenario:Benchmarks.Trace.SingleSpanAspNetCoreBenchmark.SingleSpanAspNetCore net6.0

  • unstable execution_time [+373.935ms; +385.838ms] or [+465.054%; +479.858%]
  • 🟥 throughput [-7435.603op/s; -7232.005op/s] or [-57.481%; -55.907%]

scenario:Benchmarks.Trace.SingleSpanAspNetCoreBenchmark.SingleSpanAspNetCore netcoreapp3.1

  • 🟥 execution_time [+302.678ms; +305.956ms] or [+150.969%; +152.604%]
  • 🟥 throughput [-30735878.904op/s; -29250245.181op/s] or [-13.614%; -12.956%]

scenario:Benchmarks.Trace.SpanBenchmark.StartFinishScope net6.0

  • unstable execution_time [-77.126ms; -53.020ms] or [-37.775%; -25.968%]
  • unstable throughput [-205302.485op/s; -51815.759op/s] or [-19.168%; -4.838%]

scenario:Benchmarks.Trace.SpanBenchmark.StartFinishScope netcoreapp3.1

  • 🟩 execution_time [-96.815ms; -92.684ms] or [-48.989%; -46.899%]

scenario:Benchmarks.Trace.SpanBenchmark.StartFinishSpan net6.0

  • 🟩 execution_time [-89.284ms; -85.148ms] or [-46.518%; -44.363%]
  • 🟩 throughput [+81562.427op/s; +111076.603op/s] or [+6.313%; +8.598%]

scenario:Benchmarks.Trace.SpanBenchmark.StartFinishSpan netcoreapp3.1

  • unstable execution_time [-54.771ms; -28.106ms] or [-26.910%; -13.809%]
  • 🟩 throughput [+70923.042op/s; +85873.435op/s] or [+7.044%; +8.529%]

scenario:Benchmarks.Trace.SpanBenchmark.StartFinishTwoScopes net6.0

  • unstable execution_time [-89.546ms; -66.984ms] or [-44.719%; -33.451%]
  • 🟩 throughput [+43035.526op/s; +48669.186op/s] or [+7.814%; +8.837%]

scenario:Benchmarks.Trace.SpanBenchmark.StartFinishTwoScopes netcoreapp3.1

  • unstable execution_time [-51.582ms; -23.801ms] or [-25.917%; -11.959%]

scenario:Benchmarks.Trace.TraceAnnotationsBenchmark.RunOnMethodBegin net6.0

  • unstable execution_time [-94.713ms; -70.436ms] or [-47.373%; -35.230%]
  • unstable throughput [-123398.766op/s; -12426.447op/s] or [-13.787%; -1.388%]

Known flaky benchmarks without significant changes:

  • scenario:Benchmarks.OpenTelemetry.InstrumentedApi.Trace.ActivityBenchmark.StartSpan net472
  • scenario:Benchmarks.OpenTelemetry.InstrumentedApi.Trace.ActivityBenchmark.StartSpan_AddEvent_Sampled net472
  • scenario:Benchmarks.OpenTelemetry.InstrumentedApi.Trace.ActivityBenchmark.StartSpan_GetContext_Sampled net472
  • scenario:Benchmarks.OpenTelemetry.InstrumentedApi.Trace.ActivityBenchmark.StartSpan_SetAttributes_Sampled net472
  • scenario:Benchmarks.OpenTelemetry.InstrumentedApi.Trace.ActivityBenchmark.StartSpan_SetStatus_Sampled net472
  • scenario:Benchmarks.OpenTelemetry.InstrumentedApi.Trace.ActivityBenchmark.StartSpan_UpdateName_Sampled net472
  • scenario:Benchmarks.OpenTelemetry.InstrumentedApi.Trace.TelemetrySpanBenchmark.StartSpan net472
  • scenario:Benchmarks.OpenTelemetry.InstrumentedApi.Trace.TelemetrySpanBenchmark.StartSpan_AddEvent_Sampled net472
  • scenario:Benchmarks.OpenTelemetry.InstrumentedApi.Trace.TelemetrySpanBenchmark.StartSpan_GetContext_Sampled net472
  • scenario:Benchmarks.OpenTelemetry.InstrumentedApi.Trace.TelemetrySpanBenchmark.StartSpan_RecordException_Sampled net472
  • scenario:Benchmarks.OpenTelemetry.InstrumentedApi.Trace.TelemetrySpanBenchmark.StartSpan_SetAttributes_Sampled net472
  • scenario:Benchmarks.OpenTelemetry.InstrumentedApi.Trace.TelemetrySpanBenchmark.StartSpan_SetStatus_Sampled net472
  • scenario:Benchmarks.OpenTelemetry.InstrumentedApi.Trace.TelemetrySpanBenchmark.StartSpan_SetStatus_Sampled netcoreapp3.1
  • scenario:Benchmarks.OpenTelemetry.InstrumentedApi.Trace.TelemetrySpanBenchmark.StartSpan_UpdateName_Sampled net472
  • scenario:Benchmarks.Trace.Asm.AppSecBodyBenchmark.ObjectExtractorMoreComplexBody net472
  • scenario:Benchmarks.Trace.Asm.AppSecBodyBenchmark.ObjectExtractorSimpleBody net472
  • scenario:Benchmarks.Trace.Asm.AppSecWafBenchmark.RunWafRealisticBenchmark net472
  • scenario:Benchmarks.Trace.Asm.AppSecWafBenchmark.RunWafRealisticBenchmark net6.0
  • scenario:Benchmarks.Trace.Asm.AppSecWafBenchmark.RunWafRealisticBenchmark netcoreapp3.1
  • scenario:Benchmarks.Trace.Asm.AppSecWafBenchmark.RunWafRealisticBenchmarkWithAttack net472
  • scenario:Benchmarks.Trace.Asm.AppSecWafBenchmark.RunWafRealisticBenchmarkWithAttack netcoreapp3.1
  • scenario:Benchmarks.Trace.CharSliceBenchmark.OptimizedCharSlice net472
  • scenario:Benchmarks.Trace.CharSliceBenchmark.OptimizedCharSlice net6.0
  • scenario:Benchmarks.Trace.CharSliceBenchmark.OptimizedCharSlice netcoreapp3.1
  • scenario:Benchmarks.Trace.CharSliceBenchmark.OptimizedCharSliceWithPool net472
  • scenario:Benchmarks.Trace.CharSliceBenchmark.OptimizedCharSliceWithPool netcoreapp3.1
  • scenario:Benchmarks.Trace.CharSliceBenchmark.OriginalCharSlice net472
  • scenario:Benchmarks.Trace.CharSliceBenchmark.OriginalCharSlice netcoreapp3.1
  • scenario:Benchmarks.Trace.ILoggerBenchmark.EnrichedLog net472
  • scenario:Benchmarks.Trace.Iast.StringAspectsBenchmark.StringConcatAspectBenchmark net472
  • scenario:Benchmarks.Trace.Iast.StringAspectsBenchmark.StringConcatBenchmark net472
  • scenario:Benchmarks.Trace.RedisBenchmark.SendReceive net472
  • scenario:Benchmarks.Trace.SpanBenchmark.StartFinishScope net472
  • scenario:Benchmarks.Trace.SpanBenchmark.StartFinishSpan net472
  • scenario:Benchmarks.Trace.SpanBenchmark.StartFinishTwoScopes net472
  • scenario:Benchmarks.Trace.TraceAnnotationsBenchmark.RunOnMethodBegin net472
  • scenario:Benchmarks.Trace.TraceAnnotationsBenchmark.RunOnMethodBegin netcoreapp3.1

@andrewlock andrewlock force-pushed the andrew/r_and_d/activity branch from 8a21358 to b35deb9 Compare March 24, 2026 13:38
@andrewlock andrewlock force-pushed the andrew/r_and_d/activity branch from 489f411 to abb2a10 Compare May 7, 2026 12:01
andrewlock and others added 16 commits May 11, 2026 09:13
…replacement

Introduces a new opt-in approach (DD_TRACE_OTEL_ACTIVITY_INTERCEPTION_ENABLED=true)
that intercepts System.Diagnostics.Activity methods via CallTarget instead of using
the managed ActivityListener. Goals: reduce memory usage by eliminating Activity's
internal tag storage duplication and the ConcurrentDictionary span lookup.

New CallTarget integrations:
- ActivityStartIntegration: intercepts Activity.Start() to create a Span and link
  it to the Activity via GetCustomProperty/SetCustomProperty ('__dd_span__' key)
- ActivityStopIntegration: intercepts Activity.Stop() to finish the Span with
  correct timing, extracting links/events/status at stop time
- ActivityAddTagStringIntegration: intercepts AddTag(string, string?) to write
  directly to the Span and skip Activity's internal tag list
- ActivityAddTagObjectIntegration: intercepts AddTag(string, object?) similarly
- ActivitySetTagIntegration: intercepts SetTag(string, object?) similarly
- ActivitySetStatusIntegration: intercepts SetStatus() to map OTel status to
  Datadog error tags, bypassing Activity's internal status field
- ActivityDisplayNameIntegration: intercepts set_DisplayName to set Span.ResourceName

Supporting infrastructure:
- ActivityCustomPropertyAccessor<TTarget>: zero-allocation cached delegates for
  reading/writing the Scope via Activity's custom property API
- ActivitySourceFilter: shared filter for source names already handled by other
  Datadog integrations (mirrors IgnoreActivityHandler.SourcesNames)

Configuration changes:
- Added DD_TRACE_OTEL_ACTIVITY_INTERCEPTION_ENABLED feature flag to TracerSettings,
  supported-configurations.yaml, and generated ConfigurationKeys
- Instrumentation.cs: skips managed ActivityListener when interception is enabled
- MutableSettings.cs: keeps OpenTelemetry integration enabled under either mode

Other changes:
- IActivity5: added GetCustomProperty/SetCustomProperty to the duck type interface
- OtlpHelpers: added ExtractLinksAndEventsFromActivity helper for stop integration
- ResourceAttributeProcessorHelper: uses custom property lookup when interception
  is enabled, falling back to ConcurrentDictionary for the legacy listener path

All integrations registered in InstrumentationDefinitions.g.cs.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Phase 1 — Fix IsAllDataRequested:
ActivityStartIntegration.CreateAndLinkScope now sets activity5.IsAllDataRequested = true
after linking the Span. The managed ActivityListener did this implicitly via its Sample
callback returning AllData; since we skip the listener when interception is enabled, we
must set it explicitly so user code guarded by `if (activity.IsAllDataRequested)` runs.

Phase 2 — Getter interceptions (redirect reads to the Span):
The setter integrations use skipMethodBody to bypass Activity's internal storage, which
means reading from the Activity would return stale/empty values. Five new getter
integrations restore correct observable state by reading from the linked Span:

- ActivityDisplayNameGetterIntegration: get_DisplayName → span.ResourceName ?? span.OperationName
- ActivityStatusGetterIntegration: get_Status → ActivityStatusCode reconstructed from
  span's "otel.status_code" tag (Enum.ToObject handles the foreign enum conversion)
- ActivityStatusDescriptionGetterIntegration: get_StatusDescription → span's
  "otel.status_description" tag
- ActivityTagsGetterIntegration: get_Tags → all span string tags enumerated via
  ITags.EnumerateTags into List<KVP<string,string?>>
- ActivityTagObjectsGetterIntegration: get_TagObjects → same but boxed as object?,
  matching Activity.TagObjects' IEnumerable<KVP<string,object?>> contract

Note on TagObjects: numeric tags set via SetMetric are not reflected since the Span
stores them separately from string tags. This is an acceptable R&D limitation.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Add the 5 new Activity getter integration classes to the GetIntegrationId
switch in the generated InstrumentationDefinitions file so they are
correctly mapped to IntegrationId.OpenTelemetry at runtime.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Add SubmitsTracesWithInterception to both NetActivitySdkTests and
OpenTelemetrySdkTests. Each variant enables DD_TRACE_OTEL_ACTIVITY_INTERCEPTION_ENABLED
instead of DD_TRACE_OTEL_ENABLED and shares the same snapshot file as
the existing SubmitsTraces test, asserting that the CallTarget-based
interception approach produces output identical to the managed
ActivityListener approach.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Replace the global _cachedResource with a __dd_resource__ custom property set in
ResourceAttributeProcessor.OnStart. ActivityStartIntegration reads it back after
Activity.Start() returns, before copying activity tags so reserved tags like
service.name=ServiceNameOverride can correctly override the resource. Each
TracerProvider's processor stashes its own resource on its activities, so apps
that build multiple TracerProviders (e.g. one with no service name resource)
no longer have the first provider's resource leak across.

Move the property-key constants out of the generic accessor into a
non-generic ActivityCustomPropertyKeys helper so they can be referenced
from non-generic call sites.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
ActivitySource.StartActivity returns null when no listener is interested in
a source, so unsubscribed sources (e.g. an ActivitySource the application
creates but never adds to a TracerProvider) never produce activities and
our CallTarget intercept on Activity.Start never fires. Re-enable the
managed listener in interception mode so the listener's ShouldListenTo
keeps those sources alive; ActivityListenerHandler short-circuits its
ActivityStarted/ActivityStopped callbacks in interception mode so it
doesn't create duplicate Datadog spans alongside the interception path.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…erception

In the interception path SetStatus is intercepted before the user calls
RecordException, so the exception attributes never reach the span via
ActivitySetStatusIntegration. Mirror the managed listener behaviour by
calling OtlpHelpers.ExtractExceptionAttributes from ActivityStopIntegration
when the span is in Error state, so error.type / error.msg / error.stack
get populated from the exception event in Activity.Events.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…pshot

Bring the test back from a span-count check to a full Verify-snapshot
assertion. Use a dedicated .Interception snapshot rather than sharing
with SubmitsTraces because of one known parentage gap:
StartActiveSpan(name, kind, parentTelemetrySpan) creates a child
Activity with Activity.Parent = null (OTel passes the parent context
as ActivityContext, not as an Activity reference), so the interception
path treats it as a remote parent and the child becomes the root of a
fresh TraceContext (extra runtime-id tag and Metrics block). This is
the same parentage class as the W3C-only-parent gap in NetActivitySdk
and is tracked alongside it; the test docstring records it.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
OTel's `Tracer.StartActiveSpan(name, kind, parentTelemetrySpan, …)` /
`Tracer.StartSpan(name, kind, parentTelemetrySpan, …)` lower the parent
TelemetrySpan into an `ActivityContext` (TraceId+SpanId) before calling
`ActivitySource.StartActivity`, so by the time our `Activity.Start`
intercept fires the in-process parent reference is gone — the child
activity has `Activity.Parent == null` despite having a `RawParentSpanId`.
Previously this fell through to the remote-parent branch, creating a
fresh `TraceContext` for the child; the child became a local trace root
with an extra `runtime-id` tag, an independent sampling decision, and
shipped as its own segment.

Fix: instrument the two `TelemetrySpan`-parent overloads themselves.
`OnMethodBegin` resolves the parent's Datadog `Scope` (duck-cast into
the `Activity` field on `TelemetrySpan` via [DuckField], then read
`__dd_span__` via the existing `IActivity5` duck type) and pushes it on
a thread-local stack. `ActivityStartIntegration.CreateAndLinkScope`
peeks that stack only on the cold path that already failed the
`Activity.Parent` lookup, so common activities (root spans, normal
in-process children) read nothing extra. The OTel-API integration's
`OnMethodEnd` pops, gated on a sentinel marker so a config flip
mid-call doesn't leak a stack entry.

ThreadStatic was chosen over AsyncLocal because the chain
`OTel-API → ActivitySource.StartActivity → Activity.Start →
ActivityStartIntegration.OnMethodEnd` is fully synchronous on the same
thread, so AsyncLocal's execution-context propagation buys nothing.

After the fix the OpenTelemetrySdk interception snapshot is byte-identical
to the managed-listener snapshot, so the dedicated `.Interception`
snapshot is removed and the test shares `OpenTelemetrySdkTests.verified.txt`.
The `SetParentId(traceId, spanId)` case in NetActivitySdk remains a
deliberate remote-parent path (the API itself is context-only) and keeps
its own `.Interception` snapshot.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
`Datadog.Trace.OpenTelemetry.Sdk.Initialize()` swaps OTel's no-op default
text-map propagator for a `CompositeTextMapPropagator` containing
`TraceContextPropagator + BaggagePropagator`. It was gated on
`IsActivityListenerEnabled` only, so in interception mode the default
propagator stayed as the no-op — meaning user calls to
`DefaultTextMapPropagator.Inject(...)` silently wrote nothing even when
`OpenTelemetry.Baggage.Current` was populated. Same class of bug as the
`ActivityListener.Initialize()` gate we already fixed; same fix.

Symptom: `NetActivitySdkTests`'s `RunOpenTelemetryApiInject` sets baggage,
injects via the propagator into a headers dict, then stamps the resulting
`baggage` HTTP header back as a span tag. In interception mode the tag
ended up null and was dropped from the snapshot. After the fix the tag
appears as `key=value` (added to the dedicated `.Interception` snapshot
to match listener-path output, modulo the deliberate
`SetParentId(traceId, spanId)` remote-parent divergence).

Verified across the full Windows TFM matrix:
net48, netcoreapp3.1, net6.0, net7.0, net8.0, net9.0, net10.0 — all pass
both `OpenTelemetrySdkTests.SubmitsTracesWithInterception` and
`NetActivitySdkTests.SubmitsTracesWithInterception`.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Without this, every Activity.SetTag / AddTag / set_DisplayName / SetStatus
/ getter call across the host process — for users who haven't opted into
interception — would still hit Activity.GetCustomProperty("__dd_span__")
in the integration's OnMethodBegin. That's a Dictionary lookup on every
intercepted call, paid by every modern .NET 6+ process whose JIT applies
our CallTarget rewrites.

Add `if (!IsActivityInterceptionEnabled) return GetDefault();` at the top
of each Activity-direct intercept (ActivityStartIntegration and
ActivityStopIntegration already had it). Brings non-interception per-call
cost down to a single bool field read.

Verified across the full Windows TFM matrix that interception tests
(SubmitsTracesWithInterception) still pass on net48, netcoreapp3.1,
net6.0, net7.0, net8.0, net9.0, net10.0; and listener-path tests
(SubmitsTraces, SubmitsTracesWithActivitySource) still pass on net6.0.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@andrewlock andrewlock force-pushed the andrew/r_and_d/activity branch from 7e70a43 to 7832b66 Compare May 11, 2026 08:28
@dd-trace-dotnet-ci-bot
Copy link
Copy Markdown

dd-trace-dotnet-ci-bot Bot commented May 11, 2026

Execution-Time Benchmarks Report ⏱️

Execution-time results for samples comparing This PR (8357) 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
duration73.58 ± (73.62 - 74.14) ms72.73 ± (72.70 - 73.03) ms-1.2%
.NET Framework 4.8 - Bailout
duration76.79 ± (76.66 - 77.04) ms75.96 ± (75.91 - 76.29) ms-1.1%
.NET Framework 4.8 - CallTarget+Inlining+NGEN
duration1100.29 ± (1099.28 - 1107.42) ms1106.01 ± (1103.38 - 1111.08) ms+0.5%✅⬆️
.NET Core 3.1 - Baseline
process.internal_duration_ms22.46 ± (22.41 - 22.51) ms22.45 ± (22.41 - 22.49) ms-0.0%
process.time_to_main_ms84.74 ± (84.49 - 84.98) ms83.60 ± (83.42 - 83.79) ms-1.3%
runtime.dotnet.exceptions.count0 ± (0 - 0)0 ± (0 - 0)+0.0%
runtime.dotnet.mem.committed10.91 ± (10.91 - 10.92) MB10.90 ± (10.90 - 10.90) MB-0.1%
runtime.dotnet.threads.count12 ± (12 - 12)12 ± (12 - 12)+0.0%
.NET Core 3.1 - Bailout
process.internal_duration_ms22.24 ± (22.20 - 22.28) ms22.31 ± (22.27 - 22.35) ms+0.3%✅⬆️
process.time_to_main_ms84.88 ± (84.67 - 85.09) ms84.97 ± (84.76 - 85.17) ms+0.1%✅⬆️
runtime.dotnet.exceptions.count0 ± (0 - 0)0 ± (0 - 0)+0.0%
runtime.dotnet.mem.committed10.90 ± (10.89 - 10.90) MB10.93 ± (10.93 - 10.93) MB+0.3%✅⬆️
runtime.dotnet.threads.count13 ± (13 - 13)13 ± (13 - 13)+0.0%
.NET Core 3.1 - CallTarget+Inlining+NGEN
process.internal_duration_ms214.06 ± (213.08 - 215.03) ms213.21 ± (212.22 - 214.20) ms-0.4%
process.time_to_main_ms541.13 ± (539.78 - 542.48) ms538.09 ± (536.63 - 539.54) ms-0.6%
runtime.dotnet.exceptions.count0 ± (0 - 0)0 ± (0 - 0)+0.0%
runtime.dotnet.mem.committed48.45 ± (48.42 - 48.48) MB48.50 ± (48.45 - 48.54) MB+0.1%✅⬆️
runtime.dotnet.threads.count28 ± (28 - 28)28 ± (28 - 28)-0.1%
.NET 6 - Baseline
process.internal_duration_ms21.31 ± (21.25 - 21.36) ms21.28 ± (21.24 - 21.32) ms-0.1%
process.time_to_main_ms74.49 ± (74.25 - 74.72) ms73.42 ± (73.26 - 73.57) ms-1.4%
runtime.dotnet.exceptions.count0 ± (0 - 0)0 ± (0 - 0)+0.0%
runtime.dotnet.mem.committed10.63 ± (10.63 - 10.63) MB10.62 ± (10.62 - 10.62) MB-0.1%
runtime.dotnet.threads.count10 ± (10 - 10)10 ± (10 - 10)+0.0%
.NET 6 - Bailout
process.internal_duration_ms21.12 ± (21.09 - 21.15) ms21.53 ± (21.48 - 21.59) ms+2.0%✅⬆️
process.time_to_main_ms74.59 ± (74.45 - 74.73) ms76.23 ± (75.95 - 76.51) ms+2.2%✅⬆️
runtime.dotnet.exceptions.count0 ± (0 - 0)0 ± (0 - 0)+0.0%
runtime.dotnet.mem.committed10.74 ± (10.74 - 10.74) MB10.74 ± (10.74 - 10.74) MB+0.0%✅⬆️
runtime.dotnet.threads.count11 ± (11 - 11)11 ± (11 - 11)+0.0%
.NET 6 - CallTarget+Inlining+NGEN
process.internal_duration_ms377.12 ± (374.87 - 379.37) ms377.91 ± (376.03 - 379.79) ms+0.2%✅⬆️
process.time_to_main_ms539.17 ± (537.94 - 540.40) ms537.79 ± (536.61 - 538.98) ms-0.3%
runtime.dotnet.exceptions.count0 ± (0 - 0)0 ± (0 - 0)+0.0%
runtime.dotnet.mem.committed49.76 ± (49.73 - 49.78) MB50.11 ± (50.09 - 50.14) MB+0.7%✅⬆️
runtime.dotnet.threads.count28 ± (28 - 28)28 ± (28 - 28)-0.2%
.NET 8 - Baseline
process.internal_duration_ms19.56 ± (19.52 - 19.61) ms19.55 ± (19.50 - 19.60) ms-0.1%
process.time_to_main_ms73.67 ± (73.43 - 73.92) ms74.14 ± (73.86 - 74.42) ms+0.6%✅⬆️
runtime.dotnet.exceptions.count0 ± (0 - 0)0 ± (0 - 0)+0.0%
runtime.dotnet.mem.committed7.67 ± (7.67 - 7.68) MB7.67 ± (7.66 - 7.67) MB-0.1%
runtime.dotnet.threads.count10 ± (10 - 10)10 ± (10 - 10)+0.0%
.NET 8 - Bailout
process.internal_duration_ms19.60 ± (19.55 - 19.65) ms19.24 ± (19.20 - 19.27) ms-1.8%
process.time_to_main_ms75.53 ± (75.31 - 75.76) ms72.93 ± (72.77 - 73.10) ms-3.4%
runtime.dotnet.exceptions.count0 ± (0 - 0)0 ± (0 - 0)+0.0%
runtime.dotnet.mem.committed7.72 ± (7.72 - 7.73) MB7.71 ± (7.70 - 7.71) MB-0.2%
runtime.dotnet.threads.count11 ± (11 - 11)11 ± (11 - 11)+0.0%
.NET 8 - CallTarget+Inlining+NGEN
process.internal_duration_ms298.35 ± (296.17 - 300.53) ms293.57 ± (291.42 - 295.72) ms-1.6%
process.time_to_main_ms492.35 ± (491.39 - 493.30) ms497.43 ± (496.21 - 498.65) ms+1.0%✅⬆️
runtime.dotnet.exceptions.count0 ± (0 - 0)0 ± (0 - 0)+0.0%
runtime.dotnet.mem.committed36.90 ± (36.88 - 36.93) MB37.22 ± (37.19 - 37.25) MB+0.9%✅⬆️
runtime.dotnet.threads.count27 ± (27 - 27)27 ± (27 - 27)-0.2%

HttpMessageHandler

Metric Master (Mean ± 95% CI) Current (Mean ± 95% CI) Change Status
.NET Framework 4.8 - Baseline
duration198.91 ± (198.64 - 199.62) ms200.63 ± (199.87 - 201.02) ms+0.9%✅⬆️
.NET Framework 4.8 - Bailout
duration202.10 ± (202.12 - 202.84) ms203.00 ± (202.37 - 203.48) ms+0.4%✅⬆️
.NET Framework 4.8 - CallTarget+Inlining+NGEN
duration1194.39 ± (1193.89 - 1199.95) ms1198.15 ± (1201.30 - 1208.97) ms+0.3%✅⬆️
.NET Core 3.1 - Baseline
process.internal_duration_ms194.41 ± (193.97 - 194.85) ms194.51 ± (194.09 - 194.92) ms+0.0%✅⬆️
process.time_to_main_ms83.70 ± (83.40 - 84.00) ms84.17 ± (83.84 - 84.50) ms+0.6%✅⬆️
runtime.dotnet.exceptions.count3 ± (3 - 3)3 ± (3 - 3)+0.0%
runtime.dotnet.mem.committed16.07 ± (16.05 - 16.09) MB16.00 ± (15.98 - 16.03) MB-0.4%
runtime.dotnet.threads.count20 ± (19 - 20)20 ± (20 - 20)+0.2%✅⬆️
.NET Core 3.1 - Bailout
process.internal_duration_ms193.95 ± (193.53 - 194.37) ms193.39 ± (192.93 - 193.85) ms-0.3%
process.time_to_main_ms84.87 ± (84.66 - 85.08) ms84.98 ± (84.68 - 85.28) ms+0.1%✅⬆️
runtime.dotnet.exceptions.count3 ± (3 - 3)3 ± (3 - 3)+0.0%
runtime.dotnet.mem.committed16.16 ± (16.13 - 16.18) MB16.09 ± (16.07 - 16.11) MB-0.4%
runtime.dotnet.threads.count21 ± (20 - 21)21 ± (21 - 21)+0.1%✅⬆️
.NET Core 3.1 - CallTarget+Inlining+NGEN
process.internal_duration_ms387.35 ± (386.09 - 388.61) ms384.07 ± (382.87 - 385.27) ms-0.8%
process.time_to_main_ms533.12 ± (531.91 - 534.33) ms532.95 ± (531.95 - 533.94) ms-0.0%
runtime.dotnet.exceptions.count3 ± (3 - 3)3 ± (3 - 3)+0.0%
runtime.dotnet.mem.committed57.62 ± (57.41 - 57.83) MB57.33 ± (57.17 - 57.48) MB-0.5%
runtime.dotnet.threads.count30 ± (30 - 30)30 ± (30 - 30)-0.1%
.NET 6 - Baseline
process.internal_duration_ms196.17 ± (195.75 - 196.60) ms198.71 ± (198.23 - 199.18) ms+1.3%✅⬆️
process.time_to_main_ms71.67 ± (71.43 - 71.90) ms72.71 ± (72.37 - 73.06) ms+1.5%✅⬆️
runtime.dotnet.exceptions.count4 ± (4 - 4)4 ± (4 - 4)+0.0%
runtime.dotnet.mem.committed16.38 ± (16.35 - 16.41) MB16.33 ± (16.30 - 16.35) MB-0.3%
runtime.dotnet.threads.count19 ± (19 - 19)19 ± (19 - 19)+0.3%✅⬆️
.NET 6 - Bailout
process.internal_duration_ms196.64 ± (196.12 - 197.16) ms198.65 ± (198.25 - 199.05) ms+1.0%✅⬆️
process.time_to_main_ms73.19 ± (72.94 - 73.43) ms73.82 ± (73.62 - 74.02) ms+0.9%✅⬆️
runtime.dotnet.exceptions.count4 ± (4 - 4)4 ± (4 - 4)+0.0%
runtime.dotnet.mem.committed16.44 ± (16.42 - 16.47) MB16.37 ± (16.34 - 16.40) MB-0.4%
runtime.dotnet.threads.count20 ± (20 - 20)20 ± (20 - 20)+0.1%✅⬆️
.NET 6 - CallTarget+Inlining+NGEN
process.internal_duration_ms596.52 ± (593.98 - 599.06) ms596.20 ± (593.79 - 598.61) ms-0.1%
process.time_to_main_ms534.37 ± (533.37 - 535.36) ms536.87 ± (535.82 - 537.93) ms+0.5%✅⬆️
runtime.dotnet.exceptions.count4 ± (4 - 4)4 ± (4 - 4)+0.0%
runtime.dotnet.mem.committed61.33 ± (61.21 - 61.44) MB61.74 ± (61.64 - 61.85) MB+0.7%✅⬆️
runtime.dotnet.threads.count31 ± (31 - 31)31 ± (31 - 31)-0.1%
.NET 8 - Baseline
process.internal_duration_ms196.83 ± (196.41 - 197.25) ms195.50 ± (195.01 - 195.99) ms-0.7%
process.time_to_main_ms72.00 ± (71.75 - 72.26) ms71.84 ± (71.58 - 72.11) ms-0.2%
runtime.dotnet.exceptions.count4 ± (4 - 4)4 ± (4 - 4)+0.0%
runtime.dotnet.mem.committed11.72 ± (11.70 - 11.75) MB11.70 ± (11.68 - 11.72) MB-0.2%
runtime.dotnet.threads.count18 ± (18 - 18)18 ± (18 - 18)+0.2%✅⬆️
.NET 8 - Bailout
process.internal_duration_ms195.94 ± (195.58 - 196.29) ms195.28 ± (194.79 - 195.77) ms-0.3%
process.time_to_main_ms73.21 ± (73.04 - 73.37) ms72.65 ± (72.40 - 72.91) ms-0.8%
runtime.dotnet.exceptions.count4 ± (4 - 4)4 ± (4 - 4)+0.0%
runtime.dotnet.mem.committed11.77 ± (11.75 - 11.78) MB11.73 ± (11.71 - 11.75) MB-0.3%
runtime.dotnet.threads.count19 ± (19 - 19)19 ± (19 - 19)-0.2%
.NET 8 - CallTarget+Inlining+NGEN
process.internal_duration_ms513.60 ± (510.93 - 516.27) ms512.53 ± (509.93 - 515.13) ms-0.2%
process.time_to_main_ms489.99 ± (489.34 - 490.63) ms492.63 ± (491.79 - 493.47) ms+0.5%✅⬆️
runtime.dotnet.exceptions.count4 ± (4 - 4)4 ± (4 - 4)+0.0%
runtime.dotnet.mem.committed50.59 ± (50.55 - 50.62) MB50.85 ± (50.80 - 50.89) MB+0.5%✅⬆️
runtime.dotnet.threads.count30 ± (30 - 30)30 ± (30 - 30)-0.0%
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 (8357) - mean (73ms)  : 70, 75
    master - mean (74ms)  : 70, 78

    section Bailout
    This PR (8357) - mean (76ms)  : 74, 78
    master - mean (77ms)  : 75, 79

    section CallTarget+Inlining+NGEN
    This PR (8357) - mean (1,107ms)  : 1049, 1165
    master - mean (1,103ms)  : 1043, 1164

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 (8357) - mean (113ms)  : 110, 116
    master - mean (114ms)  : 109, 120

    section Bailout
    This PR (8357) - mean (114ms)  : 111, 116
    master - mean (114ms)  : 110, 117

    section CallTarget+Inlining+NGEN
    This PR (8357) - mean (789ms)  : 767, 811
    master - mean (793ms)  : 762, 823

Loading
FakeDbCommand (.NET 6)
gantt
    title Execution time (ms) FakeDbCommand (.NET 6)
    dateFormat  x
    axisFormat %Q
    todayMarker off
    section Baseline
    This PR (8357) - mean (101ms)  : 97, 105
    master - mean (102ms)  : 97, 108

    section Bailout
    This PR (8357) - mean (104ms)  : 99, 109
    master - mean (102ms)  : 100, 104

    section CallTarget+Inlining+NGEN
    This PR (8357) - mean (944ms)  : 914, 975
    master - mean (947ms)  : 902, 991

Loading
FakeDbCommand (.NET 8)
gantt
    title Execution time (ms) FakeDbCommand (.NET 8)
    dateFormat  x
    axisFormat %Q
    todayMarker off
    section Baseline
    This PR (8357) - mean (102ms)  : 95, 108
    master - mean (101ms)  : 96, 107

    section Bailout
    This PR (8357) - mean (100ms)  : 97, 102
    master - mean (103ms)  : 98, 108

    section CallTarget+Inlining+NGEN
    This PR (8357) - mean (821ms)  : 785, 857
    master - mean (821ms)  : 778, 864

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 (8357) - mean (200ms)  : 195, 206
    master - mean (199ms)  : 193, 205

    section Bailout
    This PR (8357) - mean (203ms)  : 196, 210
    master - mean (202ms)  : 199, 206

    section CallTarget+Inlining+NGEN
    This PR (8357) - mean (1,205ms)  : 1148, 1262
    master - mean (1,197ms)  : 1153, 1240

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 (8357) - mean (287ms)  : 281, 294
    master - mean (287ms)  : 278, 296

    section Bailout
    This PR (8357) - mean (287ms)  : 281, 294
    master - mean (287ms)  : 281, 294

    section CallTarget+Inlining+NGEN
    This PR (8357) - mean (956ms)  : 938, 975
    master - mean (961ms)  : 945, 978

Loading
HttpMessageHandler (.NET 6)
gantt
    title Execution time (ms) HttpMessageHandler (.NET 6)
    dateFormat  x
    axisFormat %Q
    todayMarker off
    section Baseline
    This PR (8357) - mean (280ms)  : 272, 289
    master - mean (276ms)  : 268, 285

    section Bailout
    This PR (8357) - mean (281ms)  : 276, 285
    master - mean (278ms)  : 272, 284

    section CallTarget+Inlining+NGEN
    This PR (8357) - mean (1,159ms)  : 1123, 1195
    master - mean (1,157ms)  : 1125, 1190

Loading
HttpMessageHandler (.NET 8)
gantt
    title Execution time (ms) HttpMessageHandler (.NET 8)
    dateFormat  x
    axisFormat %Q
    todayMarker off
    section Baseline
    This PR (8357) - mean (278ms)  : 271, 284
    master - mean (279ms)  : 274, 285

    section Bailout
    This PR (8357) - mean (278ms)  : 271, 286
    master - mean (279ms)  : 275, 284

    section CallTarget+Inlining+NGEN
    This PR (8357) - mean (1,037ms)  : 998, 1076
    master - mean (1,037ms)  : 995, 1078

Loading

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant