Skip to content

perf(sdk): store InstrumentationScope in Arc to avoid clone per processor#3378

Open
bryantbiggs wants to merge 4 commits intoopen-telemetry:mainfrom
bryantbiggs:perf/avoid-scope-clone-per-processor
Open

perf(sdk): store InstrumentationScope in Arc to avoid clone per processor#3378
bryantbiggs wants to merge 4 commits intoopen-telemetry:mainfrom
bryantbiggs:perf/avoid-scope-clone-per-processor

Conversation

@bryantbiggs
Copy link
Contributor

@bryantbiggs bryantbiggs commented Feb 20, 2026

Summary

Two changes to reduce allocation in the tracing pipeline:

  1. SdkTracer now stores its InstrumentationScope in Arc. Since tracers are cloned on every span creation (build_recording_span calls self.clone()), this turns the scope copy from a deep clone (allocates a new Vec<KeyValue> for attributes) into an Arc refcount increment.

  2. The multi-processor on_end path now calls build_export_data() once and clones the result for additional processors, instead of rebuilding export data from scratch per processor. The last processor receives the original via move. Note: the per-processor clone cost is similar either way — this avoids the redundant rebuild, not the clone itself.

This is a non-breaking change. SpanData.instrumentation_scope remains InstrumentationScope, not Arc. The instrumentation_scope() accessor still returns &InstrumentationScope via Deref.

Supersedes #3267 — thanks to @taisho6339 for the original approach and discussion.

Relates to #3368.

Changes

  • SdkTracer.scope: InstrumentationScopeArc<InstrumentationScope> (internal field, not public API)
  • Multi-processor on_end: build once, clone for rest, move for last
  • Added test for multi-processor span delivery

…ssor

SdkTracer now wraps InstrumentationScope in Arc internally, which makes
tracer cloning significantly cheaper (pointer bump instead of deep
clone with attribute Vec allocation).

The multi-processor span export path is also optimized: instead of
calling build_export_data() per processor (which clones the scope from
the tracer each time), SpanData is now built once and cloned for each
additional processor. The last processor receives the original without
a clone.

This is a non-breaking change — SpanData's public
instrumentation_scope field remains InstrumentationScope (not Arc).

Supersedes open-telemetry#3267, thanks to @taisho6339 for the original approach.
@bryantbiggs bryantbiggs requested a review from a team as a code owner February 20, 2026 18:07
@codecov
Copy link

codecov bot commented Feb 20, 2026

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 82.3%. Comparing base (09b85b5) to head (f584bc2).

Additional details and impacted files
@@          Coverage Diff          @@
##            main   #3378   +/-   ##
=====================================
  Coverage   82.2%   82.3%           
=====================================
  Files        128     128           
  Lines      24626   24648   +22     
=====================================
+ Hits       20267   20296   +29     
+ Misses      4359    4352    -7     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

Verifies that when multiple span processors are configured, all
processors receive the span data with correct name and attributes.
Use split_last() instead of index tracking for the multi-processor
on_end loop. Removes unnecessary break statement and makes the
clone-for-rest, move-for-last intent explicit.

Fix changelog entry to describe what actually changed without
overstating the scope clone savings.
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