Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion opentelemetry-otlp/src/exporter/http/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1255,6 +1255,7 @@ mod tests {
use super::super::OtlpHttpClient;
use opentelemetry_http::{Bytes, HttpClient};
use std::collections::HashMap;
use std::sync::Arc;

#[derive(Debug)]
struct MockHttpClient;
Expand Down Expand Up @@ -1317,7 +1318,7 @@ mod tests {
events: SpanEvents::default(),
links: SpanLinks::default(),
status: Status::Unset,
instrumentation_scope: opentelemetry::InstrumentationScope::default(),
instrumentation_scope: Arc::new(opentelemetry::InstrumentationScope::default()),
}
}

Expand Down
14 changes: 9 additions & 5 deletions opentelemetry-proto/src/transform/trace.rs
Original file line number Diff line number Diff line change
Expand Up @@ -125,7 +125,7 @@ pub mod tonic {
.schema_url()
.map(ToOwned::to_owned)
.unwrap_or_default(),
scope: Some((source_span.instrumentation_scope, None).into()),
scope: Some((source_span.instrumentation_scope.as_ref().clone(), None).into()),
spans: vec![Span {
trace_id: source_span.span_context.trace_id().to_bytes().to_vec(),
span_id: source_span.span_context.span_id().to_bytes().to_vec(),
Expand Down Expand Up @@ -223,6 +223,7 @@ mod span_flags_tests {
use opentelemetry::InstrumentationScope;
use opentelemetry_sdk::trace::SpanData;
use std::borrow::Cow;
use std::sync::Arc;

#[test]
fn test_build_span_flags_local_parent() {
Expand Down Expand Up @@ -272,7 +273,7 @@ mod span_flags_tests {
events: opentelemetry_sdk::trace::SpanEvents::default(),
links: opentelemetry_sdk::trace::SpanLinks::default(),
status: opentelemetry::trace::Status::Unset,
instrumentation_scope: InstrumentationScope::builder("test").build(),
instrumentation_scope: Arc::new(InstrumentationScope::builder("test").build()),
};

let otlp_span: Span = span_data.into();
Expand Down Expand Up @@ -300,7 +301,7 @@ mod span_flags_tests {
events: opentelemetry_sdk::trace::SpanEvents::default(),
links: opentelemetry_sdk::trace::SpanLinks::default(),
status: opentelemetry::trace::Status::Unset,
instrumentation_scope: InstrumentationScope::builder("test").build(),
instrumentation_scope: Arc::new(InstrumentationScope::builder("test").build()),
};

let otlp_span: Span = span_data.into();
Expand All @@ -325,6 +326,7 @@ mod tests {
use opentelemetry_sdk::trace::SpanData;
use opentelemetry_sdk::trace::{SpanEvents, SpanLinks};
use std::borrow::Cow;
use std::sync::Arc;
use std::time::Duration;

fn create_test_span_data(instrumentation_name: &'static str) -> SpanData {
Expand All @@ -349,7 +351,9 @@ mod tests {
events: SpanEvents::default(),
links: SpanLinks::default(),
status: Status::Unset,
instrumentation_scope: InstrumentationScope::builder(instrumentation_name).build(),
instrumentation_scope: Arc::new(
InstrumentationScope::builder(instrumentation_name).build(),
),
}
}

Expand Down Expand Up @@ -503,7 +507,7 @@ mod tests {
events: SpanEvents::default(),
links: SpanLinks::default(),
status: Status::Unset,
instrumentation_scope,
instrumentation_scope: Arc::new(instrumentation_scope),
};

let resource: ResourceAttributesWithSchema = (&resource).into();
Expand Down
3 changes: 3 additions & 0 deletions opentelemetry-sdk/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,11 @@
- Added `Resource::get_ref(&self, key: &Key) -> Option<&Value>` to allow retrieving a reference to a resource value without cloning.
- **Breaking** Removed the following public hidden methods from the `SdkTracer` [#3227][3227]:
- `id_generator`, `should_sample`
- **Breaking** **Performance**: Changed `SpanData::instrumentation_scope` from `InstrumentationScope` to `Arc<InstrumentationScope>` ([#3267][3267]).
This reduces unnecessary clones when multiple `SpanProcessor`s are configured. Performance improvement scales with the number of processors.

[3227]: https://github.com/open-telemetry/opentelemetry-rust/pull/3227
[3267]: https://github.com/open-telemetry/opentelemetry-rust/pull/3267

## 0.31.0

Expand Down
42 changes: 42 additions & 0 deletions opentelemetry-sdk/benches/span.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,11 @@ use opentelemetry_sdk::{
use pprof::criterion::{Output, PProfProfiler};

fn criterion_benchmark(c: &mut Criterion) {
// Benchmark to measure the effect of Arc<InstrumentationScope> optimization
// This benchmark creates spans with multiple processors to demonstrate
// the performance improvement when using Arc instead of cloning InstrumentationScope
span_end_multiple_processors(c);

trace_benchmark_group(c, "span-creation-simple", |tracer| {
// Simple span creation
// There is not ability to specify anything other than the name.
Expand Down Expand Up @@ -134,6 +139,43 @@ impl SpanExporter for VoidExporter {
}
}

/// Benchmark span end with multiple processors
///
/// This benchmark demonstrates the performance benefit of using Arc instead of cloning
/// InstrumentationScope for each processor. The benefit scales with the number of processors:
/// - With 1 processor: minimal difference (baseline)
/// - With 3+ processors: significant improvement due to Arc::clone vs full clone
fn span_end_multiple_processors(c: &mut Criterion) {
let mut group = c.benchmark_group("span-end-multiple-processors");

// Test with different numbers of processors to show the scaling benefit
for num_processors in [1, 3, 5, 10] {
group.bench_function(format!("{}-processors", num_processors), |b| {
// Build provider with multiple simple exporters
let mut builder =
sdktrace::SdkTracerProvider::builder().with_sampler(sdktrace::Sampler::AlwaysOn);

for _ in 0..num_processors {
builder = builder.with_simple_exporter(VoidExporter);
}

let provider = builder.build();
let tracer = provider.tracer("benchmark-tracer");

b.iter(|| {
// Create and immediately end a span
// This triggers build_export_data which clones the instrumentation scope
// With Arc, this is just an atomic increment per processor
// Without Arc, this would be a full 120-byte clone per processor
let mut span = tracer.start("test-span");
span.end();
});
});
}

group.finish();
}

fn trace_benchmark_group<F: Fn(&sdktrace::SdkTracer)>(c: &mut Criterion, name: &str, f: F) {
let mut group = c.benchmark_group(name);

Expand Down
3 changes: 2 additions & 1 deletion opentelemetry-sdk/src/testing/trace/span_exporters.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ use opentelemetry::{
InstrumentationScope,
};
use std::fmt::{Display, Formatter};
use std::sync::Arc;

pub fn new_test_export_span_data() -> SpanData {
SpanData {
Expand All @@ -31,7 +32,7 @@ pub fn new_test_export_span_data() -> SpanData {
events: SpanEvents::default(),
links: SpanLinks::default(),
status: Status::Unset,
instrumentation_scope: InstrumentationScope::default(),
instrumentation_scope: Arc::new(InstrumentationScope::default()),
}
}

Expand Down
3 changes: 2 additions & 1 deletion opentelemetry-sdk/src/trace/export.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ use opentelemetry::trace::{SpanContext, SpanId, SpanKind, Status};
use opentelemetry::{InstrumentationScope, KeyValue};
use std::borrow::Cow;
use std::fmt::Debug;
use std::sync::Arc;
use std::time::{Duration, SystemTime};

/// `SpanExporter` defines the interface that protocol-specific exporters must
Expand Down Expand Up @@ -104,5 +105,5 @@ pub struct SpanData {
/// Span status
pub status: Status,
/// Instrumentation scope that produced this span
pub instrumentation_scope: InstrumentationScope,
pub instrumentation_scope: Arc<InstrumentationScope>,
}
3 changes: 2 additions & 1 deletion opentelemetry-sdk/src/trace/span.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ use crate::trace::SpanLimits;
use opentelemetry::trace::{Event, Link, SpanContext, SpanId, SpanKind, Status};
use opentelemetry::KeyValue;
use std::borrow::Cow;
use std::sync::Arc;
use std::time::SystemTime;

/// Single operation within a trace.
Expand Down Expand Up @@ -266,7 +267,7 @@ fn build_export_data(
events: data.events,
links: data.links,
status: data.status,
instrumentation_scope: tracer.instrumentation_scope().clone(),
instrumentation_scope: Arc::clone(tracer.instrumentation_scope()),
}
}

Expand Down
10 changes: 7 additions & 3 deletions opentelemetry-sdk/src/trace/tracer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,11 +20,12 @@ use opentelemetry::{
Context, InstrumentationScope, KeyValue,
};
use std::fmt;
use std::sync::Arc;

/// `Tracer` implementation to create and manage spans
#[derive(Clone)]
pub struct SdkTracer {
scope: InstrumentationScope,
scope: Arc<InstrumentationScope>,
provider: SdkTracerProvider,
}

Expand All @@ -42,7 +43,10 @@ impl fmt::Debug for SdkTracer {
impl SdkTracer {
/// Create a new tracer (used internally by `TracerProvider`s).
pub(crate) fn new(scope: InstrumentationScope, provider: SdkTracerProvider) -> Self {
SdkTracer { scope, provider }
SdkTracer {
scope: Arc::new(scope),
provider,
}
}

/// TracerProvider associated with this tracer.
Expand All @@ -51,7 +55,7 @@ impl SdkTracer {
}

/// Instrumentation scope of this tracer.
pub(crate) fn instrumentation_scope(&self) -> &InstrumentationScope {
pub(crate) fn instrumentation_scope(&self) -> &Arc<InstrumentationScope> {
&self.scope
}

Expand Down