Skip to content

Commit d79a782

Browse files
committed
perf: Optimize cloning of Context since it is immutable
1 parent b474512 commit d79a782

File tree

2 files changed

+113
-34
lines changed

2 files changed

+113
-34
lines changed

opentelemetry/src/context.rs

Lines changed: 102 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -92,10 +92,15 @@ thread_local! {
9292
/// ```
9393
#[derive(Clone, Default)]
9494
pub struct Context {
95+
pub(super) inner: Option<Arc<InnerContext>>,
96+
}
97+
98+
#[derive(Clone, Default)]
99+
pub(super) struct InnerContext {
95100
#[cfg(feature = "trace")]
96101
pub(crate) span: Option<Arc<SynchronizedSpan>>,
97102
entries: Option<Arc<EntryMap>>,
98-
suppress_telemetry: bool,
103+
context_flags: ContextFlags,
99104
}
100105

101106
type EntryMap = HashMap<TypeId, Arc<dyn Any + Sync + Send>, BuildHasherDefault<IdHasher>>;
@@ -196,7 +201,9 @@ impl Context {
196201
/// assert_eq!(cx.get::<MyUser>(), None);
197202
/// ```
198203
pub fn get<T: 'static>(&self) -> Option<&T> {
199-
self.entries
204+
self.inner
205+
.as_ref()?
206+
.entries
200207
.as_ref()?
201208
.get(&TypeId::of::<T>())?
202209
.downcast_ref()
@@ -230,20 +237,28 @@ impl Context {
230237
/// assert_eq!(cx_with_a_and_b.get::<ValueB>(), Some(&ValueB(42)));
231238
/// ```
232239
pub fn with_value<T: 'static + Send + Sync>(&self, value: T) -> Self {
233-
let entries = if let Some(current_entries) = &self.entries {
234-
let mut inner_entries = (**current_entries).clone();
235-
inner_entries.insert(TypeId::of::<T>(), Arc::new(value));
236-
Some(Arc::new(inner_entries))
240+
let (span, mut entries, context_flags) = if let Some(inner) = &self.inner {
241+
#[cfg(feature = "trace")]
242+
let span = inner.span.clone();
243+
#[cfg(not(feature = "trace"))]
244+
let span: Option<()> = None;
245+
246+
if let Some(entries) = &inner.entries {
247+
(span, (**entries).clone(), inner.context_flags.clone())
248+
} else {
249+
(span, EntryMap::default(), inner.context_flags.clone())
250+
}
237251
} else {
238-
let mut entries = EntryMap::default();
239-
entries.insert(TypeId::of::<T>(), Arc::new(value));
240-
Some(Arc::new(entries))
252+
(None, EntryMap::default(), ContextFlags::default())
241253
};
254+
entries.insert(TypeId::of::<T>(), Arc::new(value));
242255
Context {
243-
entries,
244-
#[cfg(feature = "trace")]
245-
span: self.span.clone(),
246-
suppress_telemetry: self.suppress_telemetry,
256+
inner: Some(Arc::new(InnerContext {
257+
#[cfg(feature = "trace")]
258+
span,
259+
entries: Some(Arc::new(entries)),
260+
context_flags,
261+
})),
247262
}
248263
}
249264

@@ -333,16 +348,49 @@ impl Context {
333348
/// Returns whether telemetry is suppressed in this context.
334349
#[inline]
335350
pub fn is_telemetry_suppressed(&self) -> bool {
336-
self.suppress_telemetry
351+
if let Some(inner) = &self.inner {
352+
inner.context_flags.suppress_telemetry()
353+
} else {
354+
false
355+
}
337356
}
338357

339358
/// Returns a new context with telemetry suppression enabled.
340359
pub fn with_telemetry_suppressed(&self) -> Self {
341-
Context {
342-
entries: self.entries.clone(),
360+
if self.is_telemetry_suppressed() {
361+
return self.clone();
362+
}
363+
let (span, entries, context_flags) = if let Some(inner) = &self.inner {
343364
#[cfg(feature = "trace")]
344-
span: self.span.clone(),
345-
suppress_telemetry: true,
365+
{
366+
(
367+
inner.span.clone(),
368+
inner.entries.clone(),
369+
inner.context_flags.with_telemetry_suppressed(),
370+
)
371+
}
372+
#[cfg(not(feature = "trace"))]
373+
{
374+
(
375+
Option::<()>::None,
376+
inner.entries.clone(),
377+
inner.context_flags.with_telemetry_suppressed(),
378+
)
379+
}
380+
} else {
381+
(
382+
None,
383+
None,
384+
ContextFlags::default().with_telemetry_suppressed(),
385+
)
386+
};
387+
Context {
388+
inner: Some(Arc::new(InnerContext {
389+
#[cfg(feature = "trace")]
390+
span,
391+
entries,
392+
context_flags,
393+
})),
346394
}
347395
}
348396

@@ -408,30 +456,38 @@ impl Context {
408456

409457
#[cfg(feature = "trace")]
410458
pub(crate) fn current_with_synchronized_span(value: SynchronizedSpan) -> Self {
411-
Self::map_current(|cx| Context {
412-
span: Some(Arc::new(value)),
413-
entries: cx.entries.clone(),
414-
suppress_telemetry: cx.suppress_telemetry,
415-
})
459+
Self::map_current(|cx| cx.with_synchronized_span(value))
416460
}
417461

418462
#[cfg(feature = "trace")]
419463
pub(crate) fn with_synchronized_span(&self, value: SynchronizedSpan) -> Self {
464+
let (entries, context_flags) = if let Some(inner) = &self.inner {
465+
(inner.entries.clone(), inner.context_flags.clone())
466+
} else {
467+
(None, ContextFlags::default())
468+
};
420469
Context {
421-
span: Some(Arc::new(value)),
422-
entries: self.entries.clone(),
423-
suppress_telemetry: self.suppress_telemetry,
470+
inner: Some(Arc::new(InnerContext {
471+
span: Some(Arc::new(value)),
472+
entries,
473+
context_flags,
474+
})),
424475
}
425476
}
426477
}
427478

428479
impl fmt::Debug for Context {
429480
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
430481
let mut dbg = f.debug_struct("Context");
431-
let mut entries = self.entries.as_ref().map_or(0, |e| e.len());
482+
let (mut entries, suppress_telemetry) = self.inner.as_ref().map_or((0, false), |inner| {
483+
(
484+
inner.entries.as_ref().map_or(0, |e| e.len()),
485+
inner.context_flags.suppress_telemetry(),
486+
)
487+
});
432488
#[cfg(feature = "trace")]
433489
{
434-
if let Some(span) = &self.span {
490+
if let Some(span) = self.inner.as_ref().and_then(|inner| inner.span.as_ref()) {
435491
dbg.field("span", &span.span_context());
436492
entries += 1;
437493
} else {
@@ -440,7 +496,7 @@ impl fmt::Debug for Context {
440496
}
441497

442498
dbg.field("entries count", &entries)
443-
.field("suppress_telemetry", &self.suppress_telemetry)
499+
.field("suppress_telemetry", &suppress_telemetry)
444500
.finish()
445501
}
446502
}
@@ -599,6 +655,23 @@ impl Default for ContextStack {
599655
}
600656
}
601657

658+
#[derive(Clone, Default)]
659+
struct ContextFlags(u32);
660+
661+
impl ContextFlags {
662+
const SUPPRESS_TELEMETRY: u32 = 0b0000_0001;
663+
664+
#[inline(always)]
665+
fn suppress_telemetry(&self) -> bool {
666+
self.0 & ContextFlags::SUPPRESS_TELEMETRY != 0
667+
}
668+
669+
#[inline(always)]
670+
fn with_telemetry_suppressed(&self) -> Self {
671+
ContextFlags(self.0 | ContextFlags::SUPPRESS_TELEMETRY)
672+
}
673+
}
674+
602675
#[cfg(test)]
603676
mod tests {
604677
use super::*;

opentelemetry/src/trace/context.rs

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -284,15 +284,21 @@ impl TraceContextExt for Context {
284284
}
285285

286286
fn span(&self) -> SpanRef<'_> {
287-
if let Some(span) = self.span.as_ref() {
288-
SpanRef(span)
289-
} else {
290-
SpanRef(&NOOP_SPAN)
287+
if let Some(inner) = self.inner.as_ref() {
288+
if let Some(span) = inner.span.as_ref() {
289+
return SpanRef(span);
290+
}
291291
}
292+
293+
SpanRef(&NOOP_SPAN)
292294
}
293295

294296
fn has_active_span(&self) -> bool {
295-
self.span.is_some()
297+
if let Some(inner) = self.inner.as_ref() {
298+
inner.span.is_some()
299+
} else {
300+
false
301+
}
296302
}
297303

298304
fn with_remote_span_context(&self, span_context: crate::trace::SpanContext) -> Self {

0 commit comments

Comments
 (0)