Skip to content

Commit 1c0d427

Browse files
committed
refactor: avoid instrumentation_scope clone for every single span processor
1 parent df412fe commit 1c0d427

File tree

5 files changed

+55
-6
lines changed

5 files changed

+55
-6
lines changed

opentelemetry-sdk/benches/span.rs

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,11 @@ use opentelemetry_sdk::{
3232
use pprof::criterion::{Output, PProfProfiler};
3333

3434
fn criterion_benchmark(c: &mut Criterion) {
35+
// Benchmark to measure the effect of Arc<InstrumentationScope> optimization
36+
// This benchmark creates spans with multiple processors to demonstrate
37+
// the performance improvement when using Arc instead of cloning InstrumentationScope
38+
span_end_multiple_processors(c);
39+
3540
trace_benchmark_group(c, "span-creation-simple", |tracer| {
3641
// Simple span creation
3742
// There is not ability to specify anything other than the name.
@@ -134,6 +139,43 @@ impl SpanExporter for VoidExporter {
134139
}
135140
}
136141

142+
/// Benchmark span end with multiple processors
143+
///
144+
/// This benchmark demonstrates the performance benefit of using Arc instead of cloning
145+
/// InstrumentationScope for each processor. The benefit scales with the number of processors:
146+
/// - With 1 processor: minimal difference (baseline)
147+
/// - With 3+ processors: significant improvement due to Arc::clone vs full clone
148+
fn span_end_multiple_processors(c: &mut Criterion) {
149+
let mut group = c.benchmark_group("span-end-multiple-processors");
150+
151+
// Test with different numbers of processors to show the scaling benefit
152+
for num_processors in [1, 3, 5, 10] {
153+
group.bench_function(format!("{}-processors", num_processors), |b| {
154+
// Build provider with multiple simple exporters
155+
let mut builder =
156+
sdktrace::SdkTracerProvider::builder().with_sampler(sdktrace::Sampler::AlwaysOn);
157+
158+
for _ in 0..num_processors {
159+
builder = builder.with_simple_exporter(VoidExporter);
160+
}
161+
162+
let provider = builder.build();
163+
let tracer = provider.tracer("benchmark-tracer");
164+
165+
b.iter(|| {
166+
// Create and immediately end a span
167+
// This triggers build_export_data which clones the instrumentation scope
168+
// With Arc, this is just an atomic increment per processor
169+
// Without Arc, this would be a full 120-byte clone per processor
170+
let mut span = tracer.start("test-span");
171+
span.end();
172+
});
173+
});
174+
}
175+
176+
group.finish();
177+
}
178+
137179
fn trace_benchmark_group<F: Fn(&sdktrace::SdkTracer)>(c: &mut Criterion, name: &str, f: F) {
138180
let mut group = c.benchmark_group(name);
139181

opentelemetry-sdk/src/testing/trace/span_exporters.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ use opentelemetry::{
1010
InstrumentationScope,
1111
};
1212
use std::fmt::{Display, Formatter};
13+
use std::sync::Arc;
1314

1415
pub fn new_test_export_span_data() -> SpanData {
1516
SpanData {
@@ -31,7 +32,7 @@ pub fn new_test_export_span_data() -> SpanData {
3132
events: SpanEvents::default(),
3233
links: SpanLinks::default(),
3334
status: Status::Unset,
34-
instrumentation_scope: InstrumentationScope::default(),
35+
instrumentation_scope: Arc::new(InstrumentationScope::default()),
3536
}
3637
}
3738

opentelemetry-sdk/src/trace/export.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ use opentelemetry::trace::{SpanContext, SpanId, SpanKind, Status};
55
use opentelemetry::{InstrumentationScope, KeyValue};
66
use std::borrow::Cow;
77
use std::fmt::Debug;
8+
use std::sync::Arc;
89
use std::time::{Duration, SystemTime};
910

1011
/// `SpanExporter` defines the interface that protocol-specific exporters must
@@ -104,5 +105,5 @@ pub struct SpanData {
104105
/// Span status
105106
pub status: Status,
106107
/// Instrumentation scope that produced this span
107-
pub instrumentation_scope: InstrumentationScope,
108+
pub instrumentation_scope: Arc<InstrumentationScope>,
108109
}

opentelemetry-sdk/src/trace/span.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ use crate::trace::SpanLimits;
1212
use opentelemetry::trace::{Event, Link, SpanContext, SpanId, SpanKind, Status};
1313
use opentelemetry::KeyValue;
1414
use std::borrow::Cow;
15+
use std::sync::Arc;
1516
use std::time::SystemTime;
1617

1718
/// Single operation within a trace.
@@ -266,7 +267,7 @@ fn build_export_data(
266267
events: data.events,
267268
links: data.links,
268269
status: data.status,
269-
instrumentation_scope: tracer.instrumentation_scope().clone(),
270+
instrumentation_scope: Arc::clone(tracer.instrumentation_scope()),
270271
}
271272
}
272273

opentelemetry-sdk/src/trace/tracer.rs

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -20,11 +20,12 @@ use opentelemetry::{
2020
Context, InstrumentationScope, KeyValue,
2121
};
2222
use std::fmt;
23+
use std::sync::Arc;
2324

2425
/// `Tracer` implementation to create and manage spans
2526
#[derive(Clone)]
2627
pub struct SdkTracer {
27-
scope: InstrumentationScope,
28+
scope: Arc<InstrumentationScope>,
2829
provider: SdkTracerProvider,
2930
}
3031

@@ -42,7 +43,10 @@ impl fmt::Debug for SdkTracer {
4243
impl SdkTracer {
4344
/// Create a new tracer (used internally by `TracerProvider`s).
4445
pub(crate) fn new(scope: InstrumentationScope, provider: SdkTracerProvider) -> Self {
45-
SdkTracer { scope, provider }
46+
SdkTracer {
47+
scope: Arc::new(scope),
48+
provider,
49+
}
4650
}
4751

4852
/// TracerProvider associated with this tracer.
@@ -51,7 +55,7 @@ impl SdkTracer {
5155
}
5256

5357
/// Instrumentation scope of this tracer.
54-
pub(crate) fn instrumentation_scope(&self) -> &InstrumentationScope {
58+
pub(crate) fn instrumentation_scope(&self) -> &Arc<InstrumentationScope> {
5559
&self.scope
5660
}
5761

0 commit comments

Comments
 (0)