Skip to content

Commit c6535e6

Browse files
committed
Avoid allocating and iterating links and activities if we don't have any
1 parent 9dc2e9c commit c6535e6

File tree

2 files changed

+41
-12
lines changed

2 files changed

+41
-12
lines changed

tracer/src/Datadog.Trace/Activity/Helpers/ActivityEnumerationHelper.cs

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55

66
#nullable enable
77

8+
using System;
89
using System.Collections.Generic;
910
using System.Threading;
1011
using Datadog.Trace.Activity.DuckTypes;
@@ -67,4 +68,22 @@ public static bool HasTagObjects<T>(this T activity5)
6768
public static bool HasTags<T>(this T activity5)
6869
where T : IActivity
6970
=> activity5.Tags is not KeyValuePair<string, string?>[] { Length: 0 };
71+
72+
/// <summary>
73+
/// Checks if the <see cref="IActivity5.Events"/> object is a zero-length array, to avoid unnecessary allocations
74+
/// from boxing the enumerator
75+
/// </summary>
76+
/// <returns>true if <see cref="IActivity5.Events"/> may contain values, false if it definitely doesn't</returns>
77+
public static bool HasEvents<T>(this T activity5)
78+
where T : IActivity5
79+
=> activity5.Events is not Array { Length: 0 };
80+
81+
/// <summary>
82+
/// Checks if the <see cref="IActivity5.Links"/> object is a zero-length array, to avoid unnecessary allocations
83+
/// from boxing the enumerator
84+
/// </summary>
85+
/// <returns>true if <see cref="IActivity5.Links"/> may contain values, false if it definitely doesn't</returns>
86+
public static bool HasLinks<T>(this T activity5)
87+
where T : IActivity5
88+
=> activity5.Links is not Array { Length: 0 };
7089
}

tracer/src/Datadog.Trace/Activity/OtlpHelpers.cs

Lines changed: 22 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -203,19 +203,24 @@ private static void AgentConvertSpan<TInner>(TInner activity, Span span)
203203
}
204204

205205
// extract any ActivityLinks and ActivityEvents
206-
ExtractActivityLinks<TInner>(span, activity5);
207-
ExtractActivityEvents<TInner>(span, activity5);
206+
if (activity5 is not null)
207+
{
208+
ExtractActivityLinks(span, activity5);
209+
ExtractActivityEvents(span, activity5);
210+
}
208211
}
209212

210-
private static void ExtractActivityLinks<TInner>(Span span, IActivity5? activity5)
211-
where TInner : IActivity
213+
private static void ExtractActivityLinks<TInner>(Span span, TInner activity5)
214+
where TInner : IActivity5
212215
{
213-
if (activity5 is null)
216+
// The underlying storage when there are no link is an empty array, so bail out in this scenario,
217+
// to avoid the allocations from boxing the enumerator
218+
if (!activity5.HasLinks())
214219
{
215220
return;
216221
}
217222

218-
foreach (var link in (activity5.Links))
223+
foreach (var link in activity5.Links)
219224
{
220225
if (!link.TryDuckCast<IActivityLink>(out var duckLink)
221226
|| duckLink.Context.TraceId.TraceId is null
@@ -264,7 +269,7 @@ private static void ExtractActivityLinks<TInner>(Span span, IActivity5? activity
264269
spanContext.LastParentId = traceState.LastParent;
265270
spanContext.PropagatedTags = traceTags;
266271

267-
List<KeyValuePair<string, string>> attributes = new();
272+
List<KeyValuePair<string, string>>? attributes = null;
268273
if (duckLink.Tags is not null)
269274
{
270275
foreach (var kvp in duckLink.Tags)
@@ -279,13 +284,15 @@ private static void ExtractActivityLinks<TInner>(Span span, IActivity5? activity
279284
{
280285
if (item?.ToString() is { } value)
281286
{
287+
attributes ??= new();
282288
attributes.Add(new($"{kvp.Key}.{index}", value));
283289
index++;
284290
}
285291
}
286292
}
287293
else if (kvp.Value?.ToString() is { } kvpValue)
288294
{
295+
attributes ??= new();
289296
attributes.Add(new(kvp.Key, kvpValue));
290297
}
291298
}
@@ -296,10 +303,12 @@ private static void ExtractActivityLinks<TInner>(Span span, IActivity5? activity
296303
}
297304
}
298305

299-
private static void ExtractActivityEvents<TInner>(Span span, IActivity5? activity5)
300-
where TInner : IActivity
306+
private static void ExtractActivityEvents<TInner>(Span span, TInner activity5)
307+
where TInner : IActivity5
301308
{
302-
if (activity5 is null)
309+
// The underlying storage when there are no link is an empty array, so bail out in this scenario,
310+
// to avoid the allocations from boxing the enumerator
311+
if (!activity5.HasEvents())
303312
{
304313
return;
305314
}
@@ -311,11 +320,12 @@ private static void ExtractActivityEvents<TInner>(Span span, IActivity5? activit
311320
continue;
312321
}
313322

314-
var eventAttributes = new List<KeyValuePair<string, object>>();
323+
List<KeyValuePair<string, object>>? eventAttributes = null;
315324
foreach (var kvp in duckEvent.Tags)
316325
{
317326
if (!string.IsNullOrEmpty(kvp.Key) && kvp.Value is not null)
318327
{
328+
eventAttributes ??= new();
319329
eventAttributes.Add(new(kvp.Key, kvp.Value));
320330
}
321331
}
@@ -526,7 +536,7 @@ private static void ExtractExceptionAttributes<TInner>(TInner activity, Span spa
526536
{
527537
// OpenTelemetry stores the exception attributes in Activity.Events
528538
// Activity.Events was only added in .NET 5+, which maps to our IActivity5 & IActivity6
529-
if (activity is not IActivity5 activity5)
539+
if (activity is not IActivity5 activity5 || !activity5.HasEvents())
530540
{
531541
return;
532542
}

0 commit comments

Comments
 (0)