Skip to content
Draft
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
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,6 @@ class TypeConverterTest extends InstrumentationSpecification {
[:],
false,
"fakeType",
0,
trace,
null,
null,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,6 @@ class TypeConverterTest extends InstrumentationSpecification {
[:],
false,
"fakeType",
0,
trace,
null,
null,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,6 @@ class TypeConverterTest extends InstrumentationSpecification {
[:],
false,
"fakeType",
0,
trace,
null,
null,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -116,7 +116,6 @@ public void init(Blackhole blackhole) {
Collections.<String, String>emptyMap(),
false,
UTF8BytesString.create("type"),
0,
traceCollector,
null,
null,
Expand Down
116 changes: 116 additions & 0 deletions dd-trace-core/src/jmh/java/datadog/trace/core/GetTagBenchmark.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
package datadog.trace.core;

import static java.util.concurrent.TimeUnit.NANOSECONDS;

import datadog.trace.api.KnownTags;
import org.openjdk.jmh.annotations.Benchmark;
import org.openjdk.jmh.annotations.BenchmarkMode;
import org.openjdk.jmh.annotations.Fork;
import org.openjdk.jmh.annotations.Measurement;
import org.openjdk.jmh.annotations.Mode;
import org.openjdk.jmh.annotations.OutputTimeUnit;
import org.openjdk.jmh.annotations.Scope;
import org.openjdk.jmh.annotations.Setup;
import org.openjdk.jmh.annotations.State;
import org.openjdk.jmh.annotations.TearDown;
import org.openjdk.jmh.annotations.Warmup;

/**
* Measures reading tags back from a live span, by how the tag is named: a known tag's name, a
* custom name, an OpenTelemetry name ({@code server.address}, which the core treats as a custom
* name), and misses. {@code getTag} takes the span's tag lock; {@code unsafeGetTag} reads without
* it, as the tracer's internal readers do.
*/
@State(Scope.Thread)
@Warmup(iterations = 3, time = 1)
@Measurement(iterations = 5, time = 1)
@BenchmarkMode(Mode.AverageTime)
@OutputTimeUnit(NANOSECONDS)
@Fork(value = 3)
public class GetTagBenchmark {
static final CoreTracer TRACER = CoreTracer.builder().build();

private DDSpan span;

/**
* The known tags the span holds, read in turn by the {@code *_cycle} arms so a read's cost is
* averaged over the span's tags rather than decided by whether one particular tag collides in its
* bucket. A power-of-two length lets the index wrap with a mask.
*/
private static final String[] KNOWN_HELD = {
"span.kind",
KnownTags.HTTP_ROUTE_NAME,
KnownTags.PEER_HOSTNAME_NAME,
KnownTags.HTTP_STATUS_CODE_NAME
};

private int next;

@Setup
public void setup() {
span = (DDSpan) TRACER.startSpan("benchmark", "get.tag");
span.setTag("span.kind", "client");
span.setTag(KnownTags.HTTP_ROUTE_NAME, "/users/{id}");
span.setTag(KnownTags.PEER_HOSTNAME_NAME, "db.example");
span.setTag(KnownTags.HTTP_STATUS_CODE_NAME, 200);
span.setTag("my.custom.tag", "custom");
span.setTag("my.other.tag", 42);
}

@TearDown
public void tearDown() {
span.finish();
}

@Benchmark
public Object knownTag_getTag() {
return span.getTag(KnownTags.HTTP_ROUTE_NAME);
}

@Benchmark
public Object customTag_getTag() {
return span.getTag("my.custom.tag");
}

@Benchmark
public Object aliasName_getTag() {
return span.getTag("server.address");
}

@Benchmark
public Object missingTag_getTag() {
return span.getTag("not.there");
}

/** A known tag the span doesn't hold: a clear slot bit can answer without a probe. */
@Benchmark
public Object missingKnownTag_getTag() {
return span.getTag(KnownTags.HTTP_METHOD_NAME);
}

@Benchmark
public Object missingKnownTag_unsafeGetTag() {
return span.unsafeGetTag(KnownTags.HTTP_METHOD_NAME);
}

@Benchmark
public Object knownTag_unsafeGetTag() {
return span.unsafeGetTag(KnownTags.HTTP_ROUTE_NAME);
}

/** Every known tag the span holds, in turn; see {@link #KNOWN_HELD}. */
@Benchmark
public Object knownTags_cycle_unsafeGetTag() {
return span.unsafeGetTag(KNOWN_HELD[next++ & (KNOWN_HELD.length - 1)]);
}

@Benchmark
public Object knownTags_cycle_getTag() {
return span.getTag(KNOWN_HELD[next++ & (KNOWN_HELD.length - 1)]);
}

@Benchmark
public Object customTag_unsafeGetTag() {
return span.unsafeGetTag("my.custom.tag");
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,6 @@ public void init(TraceCounters counters, Blackhole blackhole) {
Collections.<String, String>emptyMap(),
false,
"type",
0,
traceCollector,
null,
null,
Expand All @@ -80,7 +79,6 @@ public void init(TraceCounters counters, Blackhole blackhole) {
Collections.<String, String>emptyMap(),
false,
"type",
0,
traceCollector,
null,
null,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -135,7 +135,6 @@ private DDSpan createSpanWithOrigin(int iter, final String origin) {
Collections.<String, String>emptyMap(),
false,
"type",
0,
traceCollector,
null,
null,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -122,7 +122,6 @@ public void setUp(Blackhole blackhole) {
Collections.<String, String>emptyMap(),
false,
"type",
0,
tracer.createTraceCollector(traceId),
null,
null,
Expand Down
66 changes: 56 additions & 10 deletions dd-trace-core/src/main/java/datadog/trace/core/CoreTracer.java
Original file line number Diff line number Diff line change
Expand Up @@ -207,10 +207,43 @@ private static final class LocalRootSpanTags {
final TagMap tags;
final boolean needsIntercept;

/** The last {@link #traceLevelParent}, with the merged tracer tags it was built over. */
private volatile TraceLevelTags traceLevel;

LocalRootSpanTags(final TagMap tags, final boolean needsIntercept) {
this.tags = tags;
this.needsIntercept = needsIntercept;
}

/**
* All of a root span's trace-level tags in one frozen map -- these tags over {@code
* mergedTracerTags} -- for the root span to read through instead of copying them in. The tag
* registry colors trace-level tags separately, so they reuse span-level slot bits; keeping them
* out of the span's own store keeps its slot bits exact. Built once per config snapshot; a racy
* rebuild is harmless, since it builds the same map.
*/
TagMap traceLevelParent(final TagMap mergedTracerTags) {
TraceLevelTags cached = this.traceLevel;
if (cached != null && cached.mergedTracerTags == mergedTracerTags) {
return cached.parent;
}
TagMap parent = TagMap.create();
parent.putAll(mergedTracerTags);
parent.putAll(this.tags);
parent.freeze();
this.traceLevel = new TraceLevelTags(mergedTracerTags, parent);
return parent;
}
}

private static final class TraceLevelTags {
final TagMap mergedTracerTags;
final TagMap parent;

TraceLevelTags(final TagMap mergedTracerTags, final TagMap parent) {
this.mergedTracerTags = mergedTracerTags;
this.parent = parent;
}
}

private volatile LocalRootSpanTags localRootSpanTags;
Expand Down Expand Up @@ -2130,6 +2163,7 @@ protected static final DDSpanContext buildSpanContext(
final CharSequence origin;
final TagMap coreTags;
final boolean coreTagsNeedsIntercept;
final LocalRootSpanTags localRootSpanTags;
final TagMap rootSpanTags;
final boolean rootSpanTagsNeedsIntercept;
final DDSpanContext context;
Expand Down Expand Up @@ -2159,6 +2193,7 @@ protected static final DDSpanContext buildSpanContext(
origin = null;
coreTags = null;
coreTagsNeedsIntercept = false;
localRootSpanTags = null;
rootSpanTags = null;
rootSpanTagsNeedsIntercept = false;
parentServiceName = ddsc.getServiceName();
Expand Down Expand Up @@ -2233,7 +2268,7 @@ protected static final DDSpanContext buildSpanContext(
ciVisibilityContextData = null;
}

LocalRootSpanTags localRootSpanTags = tracer.localRootSpanTags;
localRootSpanTags = tracer.localRootSpanTags;
rootSpanTags = localRootSpanTags.tags;
rootSpanTagsNeedsIntercept = localRootSpanTags.needsIntercept;

Expand Down Expand Up @@ -2312,12 +2347,22 @@ protected static final DDSpanContext buildSpanContext(
final TagMap mergedTracerTags = traceConfig.mergedTracerTags;
boolean mergedTracerTagsNeedsIntercept = traceConfig.mergedTracerTagsNeedsIntercept;

final int tagsSize =
mergedTracerTags.size()
+ (null == tagLedger ? 0 : tagLedger.estimateSize())
+ (null == coreTags ? 0 : coreTags.size())
+ (null == rootSpanTags ? 0 : rootSpanTags.size())
+ (null == contextualTags ? 0 : contextualTags.size());
// With builder tags taking precedence, a root span reads its trace-level tags (root-span tags
// over the merged tracer tags) through one shared frozen parent instead of copying the
// root-span tags in. They become the precedence floor: the flag's order already puts them
// below builder and contextual tags, and now inbound header tags and prototype defaults also
// win over them. Tags needing interception are still copied in.
final boolean rootSpanTagsReadThrough =
localRootSpanTags != null
&& tracer.builderTagsPrecedence
&& !rootSpanTagsNeedsIntercept
&& !mergedTracerTagsNeedsIntercept;
final TagMap tagParent;
if (rootSpanTagsReadThrough) {
tagParent = localRootSpanTags.traceLevelParent(mergedTracerTags);
} else {
tagParent = mergedTracerTagsNeedsIntercept ? null : mergedTracerTags;
}

if (builderRequestContextDataAppSec != null) {
requestContextDataAppSec = builderRequestContextDataAppSec;
Expand Down Expand Up @@ -2346,7 +2391,6 @@ protected static final DDSpanContext buildSpanContext(
w3cBaggage,
errorFlag,
spanType,
tagsSize,
parentTraceCollector,
requestContextDataAppSec,
requestContextDataIast,
Expand All @@ -2357,7 +2401,7 @@ protected static final DDSpanContext buildSpanContext(
tracer.profilingContextIntegration,
tracer.injectBaggageAsTags,
tracer.injectLinksAsTags,
mergedTracerTagsNeedsIntercept ? null : mergedTracerTags);
tagParent);

// By setting the tags on the context we apply decorators to any tags that have been set via
// the builder. The `mergedTracerTags` are always applied first (the precedence floor:
Expand All @@ -2382,7 +2426,9 @@ protected static final DDSpanContext buildSpanContext(
}
if (tracer.builderTagsPrecedence) {
context.setAllTags(coreTags, coreTagsNeedsIntercept);
context.setAllTags(rootSpanTags, rootSpanTagsNeedsIntercept);
if (!rootSpanTagsReadThrough) {
context.setAllTags(rootSpanTags, rootSpanTagsNeedsIntercept);
}
context.setAllTags(contextualTags);
context.setAllTags(tagLedger);
} else {
Expand Down
Loading
Loading