Skip to content

Commit 6e96f65

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

File tree

2 files changed

+125
-34
lines changed

2 files changed

+125
-34
lines changed

opentelemetry/src/context.rs

Lines changed: 114 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,36 @@ 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+
(
248+
span,
249+
(**entries).clone(),
250+
inner.context_flags.clone(),
251+
)
252+
} else {
253+
(
254+
span,
255+
EntryMap::default(),
256+
inner.context_flags.clone(),
257+
)
258+
}
237259
} else {
238-
let mut entries = EntryMap::default();
239-
entries.insert(TypeId::of::<T>(), Arc::new(value));
240-
Some(Arc::new(entries))
260+
(None, EntryMap::default(), ContextFlags::default())
241261
};
262+
entries.insert(TypeId::of::<T>(), Arc::new(value));
242263
Context {
243-
entries,
244-
#[cfg(feature = "trace")]
245-
span: self.span.clone(),
246-
suppress_telemetry: self.suppress_telemetry,
264+
inner: Some(Arc::new(InnerContext {
265+
#[cfg(feature = "trace")]
266+
span,
267+
entries: Some(Arc::new(entries)),
268+
context_flags,
269+
})),
247270
}
248271
}
249272

@@ -333,16 +356,49 @@ impl Context {
333356
/// Returns whether telemetry is suppressed in this context.
334357
#[inline]
335358
pub fn is_telemetry_suppressed(&self) -> bool {
336-
self.suppress_telemetry
359+
if let Some(inner) = &self.inner {
360+
inner.context_flags.suppress_telemetry()
361+
} else {
362+
false
363+
}
337364
}
338365

339366
/// Returns a new context with telemetry suppression enabled.
340367
pub fn with_telemetry_suppressed(&self) -> Self {
341-
Context {
342-
entries: self.entries.clone(),
368+
if self.is_telemetry_suppressed() {
369+
return self.clone();
370+
}
371+
let (span, entries, context_flags) = if let Some(inner) = &self.inner {
343372
#[cfg(feature = "trace")]
344-
span: self.span.clone(),
345-
suppress_telemetry: true,
373+
{
374+
(
375+
inner.span.clone(),
376+
inner.entries.clone(),
377+
inner.context_flags.with_telemetry_suppressed(),
378+
)
379+
}
380+
#[cfg(not(feature = "trace"))]
381+
{
382+
(
383+
Option::<()>::None,
384+
inner.entries.clone(),
385+
inner.context_flags.with_telemetry_suppressed(),
386+
)
387+
}
388+
} else {
389+
(
390+
None,
391+
None,
392+
ContextFlags::default().with_telemetry_suppressed(),
393+
)
394+
};
395+
Context {
396+
inner: Some(Arc::new(InnerContext {
397+
#[cfg(feature = "trace")]
398+
span,
399+
entries,
400+
context_flags,
401+
})),
346402
}
347403
}
348404

@@ -408,30 +464,42 @@ impl Context {
408464

409465
#[cfg(feature = "trace")]
410466
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-
})
467+
Self::map_current(|cx| cx.with_synchronized_span(value))
416468
}
417469

418470
#[cfg(feature = "trace")]
419471
pub(crate) fn with_synchronized_span(&self, value: SynchronizedSpan) -> Self {
472+
let (entries, context_flags) = if let Some(inner) = &self.inner {
473+
(inner.entries.clone(), inner.context_flags.clone())
474+
} else {
475+
(None, ContextFlags::default())
476+
};
420477
Context {
421-
span: Some(Arc::new(value)),
422-
entries: self.entries.clone(),
423-
suppress_telemetry: self.suppress_telemetry,
478+
inner: Some(Arc::new(InnerContext {
479+
span: Some(Arc::new(value)),
480+
entries,
481+
context_flags,
482+
})),
424483
}
425484
}
426485
}
427486

428487
impl fmt::Debug for Context {
429488
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
430489
let mut dbg = f.debug_struct("Context");
431-
let mut entries = self.entries.as_ref().map_or(0, |e| e.len());
490+
let (mut entries, suppress_telemetry) = self.inner.as_ref().map_or((0, false), |inner| {
491+
(
492+
inner.entries.as_ref().map_or(0, |e| e.len()),
493+
inner.context_flags.suppress_telemetry(),
494+
)
495+
});
432496
#[cfg(feature = "trace")]
433497
{
434-
if let Some(span) = &self.span {
498+
if let Some(span) = self
499+
.inner
500+
.as_ref()
501+
.map_or(None, |inner| inner.span.as_ref())
502+
{
435503
dbg.field("span", &span.span_context());
436504
entries += 1;
437505
} else {
@@ -440,7 +508,7 @@ impl fmt::Debug for Context {
440508
}
441509

442510
dbg.field("entries count", &entries)
443-
.field("suppress_telemetry", &self.suppress_telemetry)
511+
.field("suppress_telemetry", &suppress_telemetry)
444512
.finish()
445513
}
446514
}
@@ -599,6 +667,23 @@ impl Default for ContextStack {
599667
}
600668
}
601669

670+
#[derive(Clone, Default)]
671+
struct ContextFlags(u32);
672+
673+
impl ContextFlags {
674+
const SUPPRESS_TELEMETRY: u32 = 0b0000_0001;
675+
676+
#[inline(always)]
677+
fn suppress_telemetry(&self) -> bool {
678+
self.0 & ContextFlags::SUPPRESS_TELEMETRY != 0
679+
}
680+
681+
#[inline(always)]
682+
fn with_telemetry_suppressed(&self) -> Self {
683+
ContextFlags(self.0 | ContextFlags::SUPPRESS_TELEMETRY)
684+
}
685+
}
686+
602687
#[cfg(test)]
603688
mod tests {
604689
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+
return inner.span.is_some();
299+
} else {
300+
return false;
301+
}
296302
}
297303

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

0 commit comments

Comments
 (0)