Skip to content

[SDK] Tracer::StartSpan benchmark and optimizations#4248

Open
dbarker wants to merge 12 commits into
open-telemetry:mainfrom
dbarker:perf_start_span_optimizations
Open

[SDK] Tracer::StartSpan benchmark and optimizations#4248
dbarker wants to merge 12 commits into
open-telemetry:mainfrom
dbarker:perf_start_span_optimizations

Conversation

@dbarker

@dbarker dbarker commented Jul 15, 2026

Copy link
Copy Markdown
Member

Fixes # (issue)
The Tracer::StartSpan function currently performs some unnecessary dynamic memory allocation (allocating a SpanContext and potentially a DefaultSpan) and copying of global static shared pointers by constructing temporary span contexts (which call TraceState::GetDefault()).

This latency can be removed without breaking changes. The added benchmark showed a latency reduction of ~11-23% for the range of span creation scenarios (tracer disabled, span with no parent, implicit parent, and explicit parent).

Changes

  • Adds a benchmark for Tracer::StartSpan. See baseline vs optimized benchmark results
  • Implements several optimizations to reduce dynamic memory allocation and shared_ptr copies (9746077)
    • Adds a Non-recording span implementation with a stack allocated SpanContext
    • Allocate one non recording span per tracer on construction to serve as the fallback noop span
    • Updates the SDK span to take a stack allocated SpanContext
    • Changes the default value of the SpanStartOptions::parent to an empty Context instead of the more expensive SpanContext.
    • use std::make_shared to allocate the span when building with exceptions
    • clean up StartSpan to not instantiate and copy SpanContext if not needed.
    • add GetSpanContext helper to avoid dynamically allocating a DefaultSpan object to get an invalid SpanContext (trace::GetSpan is no longer called).
  • Update tracer test cases to check for a non-recording span using the Span::IsRecording method

For significant contributions please make sure you have completed the following items:

  • CHANGELOG.md updated for non-trivial changes
  • Unit tests have been added
  • Changes in public API reviewed

@codecov

codecov Bot commented Jul 15, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 88.75000% with 9 lines in your changes missing coverage. Please review.
✅ Project coverage is 78.15%. Comparing base (c4af827) to head (a815cbd).

Files with missing lines Patch % Lines
sdk/src/trace/tracer.cc 87.68% 9 Missing ⚠️
Additional details and impacted files

Impacted file tree graph

@@            Coverage Diff             @@
##             main    #4248      +/-   ##
==========================================
- Coverage   78.15%   78.15%   -0.00%     
==========================================
  Files         440      440              
  Lines       18741    18762      +21     
==========================================
+ Hits        14645    14661      +16     
- Misses       4096     4101       +5     
Files with missing lines Coverage Δ
api/include/opentelemetry/trace/default_span.h 36.85% <ø> (+21.06%) ⬆️
sdk/include/opentelemetry/sdk/trace/sampler.h 100.00% <100.00%> (ø)
sdk/include/opentelemetry/sdk/trace/tracer.h 100.00% <ø> (ø)
sdk/src/trace/span.cc 93.58% <100.00%> (ø)
sdk/src/trace/span.h 100.00% <100.00%> (ø)
sdk/src/trace/tracer.cc 81.38% <87.68%> (-6.12%) ⬇️
🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

…te the NonRecordingSpan. Fix iwyu warnings. Cleanup

// clang-format off
//
// ~/build/sdk/test/trace/tracer_benchmark --benchmark_repetitions=5 --benchmark_display_aggregates_only=true

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

The optimizations in this PR result in a 11-23% reduction in latency for StartSpan compared to main.

Benchmark Baseline (mean ± stddev) Optimized (mean ± stddev) Difference %
BM_StartSpanTracerDisabled 6.85 ± 0.025 ns 5.24 ± 0.046 ns -23.5%
BM_StartSpan 187 ± 1.22 ns 152 ± 2.74 ns -18.7%
BM_StartSpanWithScope 255 ± 1.09 ns 218 ± 1.75 ns -14.5%
BM_StartSpanWithImplicitParent 172 ± 0.45 ns 153 ± 0.80 ns -11.0%
BM_StartSpanWithExplicitParentContext 173 ± 0.34 ns 154 ± 0.90 ns -11.0%
BM_StartSpanWithExplicitRootContext 187 ± 0.68 ns 148 ± 0.69 ns -20.9%

// BM_TraceIdRatioBasedSamplerShouldSample_median 4.00 ns 4.00 ns 5
// BM_TraceIdRatioBasedSamplerShouldSample_stddev 0.032 ns 0.032 ns 5
// BM_TraceIdRatioBasedSamplerShouldSample_cv 0.80 % 0.80 % 5
// BM_SpanCreation_mean 205 ns 205 ns 5

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Comparison with main and this PR on span sampler benchmark. The NoopSpanCreation case is where the tracer is enabled but the sampler decides to not sample the span (AlwaysOffSampler).

Benchmark Baseline (mean ± stddev) Optimized (mean ± stddev) Difference %
BM_SpanCreation 235 ± 0.62 ns 205 ± 1.00 ns -12.8%
BM_NoopSpanCreation 59.3 ± 0.42 ns 27.7 ± 0.14 ns -53.3%

@dbarker dbarker marked this pull request as ready for review July 15, 2026 16:52
@dbarker dbarker requested a review from a team as a code owner July 15, 2026 16:52
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.

2 participants