Skip to content
Closed
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
12 changes: 12 additions & 0 deletions dd-trace-core/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,18 @@ jmh {
if (project.hasProperty('jmh.profilers')) {
profilers = project.property('jmh.profilers').tokenize(',')
}
if (project.hasProperty('jmh.jvmArgs')) {
jvmArgsAppend = project.property('jmh.jvmArgs').tokenize(' ')
}
if (project.hasProperty('jmh.fork')) {
fork = project.property('jmh.fork') as Integer
}
if (project.hasProperty('jmh.warmupIterations')) {
warmupIterations = project.property('jmh.warmupIterations') as Integer
}
if (project.hasProperty('jmh.iterations')) {
iterations = project.property('jmh.iterations') as Integer
}
if (project.hasProperty('testJvm')) {
def testJvmSpec = new TestJvmSpec(project)
jvm = testJvmSpec.javaTestLauncher.map { it.executablePath.asFile.absolutePath }
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package datadog.trace.core;

import datadog.trace.api.DDTags;
import datadog.trace.bootstrap.instrumentation.api.Tags;
import datadog.trace.common.writer.Writer;
import java.util.List;
Expand Down Expand Up @@ -39,9 +40,52 @@ public class DDSpanContextSetTagBenchmark {
private static final CoreTracer TRACER =
CoreTracer.builder().writer(new NoopWriter()).strictTraceWrites(false).build();

// A realistic mix set on one span: ~1/3 intercepted (resource/status/kind/error/service), ~2/3
// ordinary app/integration tags. Cycling these through a single setTag call site keeps the
// handlerId/handleIntercept dispatch polymorphic and interleaves the store -- closer to
// production
// than the single-arm benchmarks, and it should keep C2 out of the degenerate single-mode that
// the
// notIntercepted-only loop locks into. setTag(String, Object) is the manual-instrumentation path.
private static final String[] MIXED_TAGS = {
DDTags.RESOURCE_NAME,
"http.useragent",
"db.instance",
Tags.HTTP_STATUS,
"component",
"thread.name",
Tags.SPAN_KIND,
"messaging.system",
"network.peer.address",
Tags.ERROR,
"http.route",
DDTags.SERVICE_NAME,
"rpc.method",
"app.queue.depth",
"http.request.content_length"
};
private static final Object[] MIXED_VALUES = {
"GET /api/users",
"Mozilla/5.0",
"orders",
200,
"netty",
"worker-1",
"server",
"kafka",
"10.0.0.1",
Boolean.FALSE,
"/api/users/{id}",
"my-service",
"GetUser",
42,
1024
};

@State(Scope.Thread)
public static class SpanState {
DDSpan span;
int mixIdx;

@Setup
public void setup() {
Expand All @@ -60,6 +104,14 @@ public Object notIntercepted(SpanState s) {
return s.span.setTag("app.queue.depth", 200);
}

/** Realistic mix of intercepted + non-intercepted tags cycled through one setTag call site. */
@Benchmark
public Object mixed(SpanState s) {
int i = s.mixIdx;
s.mixIdx = (i + 1 == MIXED_TAGS.length) ? 0 : i + 1;
return s.span.setTag(MIXED_TAGS[i], MIXED_VALUES[i]);
}

static final class NoopWriter implements Writer {
@Override
public void write(List<DDSpan> trace) {}
Expand Down
92 changes: 57 additions & 35 deletions dd-trace-core/src/main/java/datadog/trace/core/DDSpanContext.java
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@
import datadog.trace.bootstrap.instrumentation.api.UTF8BytesString;
import datadog.trace.core.propagation.PropagationTags;
import datadog.trace.core.taginterceptor.TagInterceptor;
import datadog.trace.core.taginterceptor.TagInterceptor.TagHandler;
import datadog.trace.core.tagprocessor.TagsPostProcessorFactory;
import datadog.trace.util.TagsHelper;
import java.io.Closeable;
Expand Down Expand Up @@ -881,7 +882,13 @@ public void setTag(final String tag, final Object value) {
}
if (null == value) {
removeTag(tag);
} else if (!tagInterceptor.interceptTag(this, tag, value)) {
return;
}
// Dispatch inline (not via interceptTag) so this overload owns its handle() call site: each
// caller that inlines setTag gets its own type profile, letting a tag-stable caller
// devirtualize and inline its one handler.
TagHandler handler = tagInterceptor.handler(tag);
if (handler == null || !handler.handle(this, tag, value)) {
synchronized (unsafeTags) {
unsafeTags.set(tag, value);
}
Expand All @@ -894,7 +901,10 @@ public void setTag(final String tag, final String value) {
}
if (null == value) {
removeTag(tag);
} else if (!tagInterceptor.interceptTag(this, tag, value)) {
return;
}
TagHandler handler = tagInterceptor.handler(tag);
if (handler == null || !handler.handle(this, tag, value)) {
synchronized (unsafeTags) {
unsafeTags.set(tag, value);
}
Expand All @@ -906,37 +916,29 @@ public void setTag(TagMap.EntryReader entry) {
return;
}

// resolve the handler once; id 0 short-circuits without a second lookup
int handlerId = tagInterceptor.handlerId(entry.tag());
boolean intercepted =
handlerId != 0
&& tagInterceptor.handleIntercept(this, handlerId, entry.tag(), entry.objectValue());
// resolve the handler once; a miss (null) short-circuits without a second lookup
TagHandler handler = tagInterceptor.handler(entry.tag());
boolean intercepted = handler != null && handler.handle(this, entry.tag(), entry.objectValue());
if (!intercepted) {
synchronized (unsafeTags) {
unsafeTags.set(entry);
}
}
}

/*
* Used when handlerId determined the tag is intercepted (id != 0): the primitive must be boxed to
* pass to the interceptor. The box is reused -- the optimized TagMap caches it on store.
*/
private void setBox(int handlerId, String tag, Object box) {
if (!tagInterceptor.handleIntercept(this, handlerId, tag, box)) {
synchronized (unsafeTags) {
unsafeTags.set(tag, box);
}
}
}

public void setTag(final String tag, final boolean value) {
if (null == tag) {
return;
}
int handlerId = tagInterceptor.handlerId(tag);
if (handlerId != 0) {
this.setBox(handlerId, tag, value);
TagHandler handler = tagInterceptor.handler(tag);
if (handler != null) {
// intercepted: box once (the optimized TagMap caches it on store) and dispatch
Object box = value;
if (!handler.handle(this, tag, box)) {
synchronized (unsafeTags) {
unsafeTags.set(tag, box);
}
}
} else {
synchronized (unsafeTags) {
unsafeTags.set(tag, value);
Expand All @@ -948,9 +950,14 @@ public void setTag(final String tag, final int value) {
if (null == tag) {
return;
}
int handlerId = tagInterceptor.handlerId(tag);
if (handlerId != 0) {
this.setBox(handlerId, tag, value);
TagHandler handler = tagInterceptor.handler(tag);
if (handler != null) {
Object box = value;
if (!handler.handle(this, tag, box)) {
synchronized (unsafeTags) {
unsafeTags.set(tag, box);
}
}
} else {
synchronized (unsafeTags) {
unsafeTags.set(tag, value);
Expand All @@ -962,10 +969,15 @@ public void setTag(final String tag, final long value) {
if (null == tag) {
return;
}
// resolve once; handlerId 0 (not intercepted) stores the primitive without boxing
int handlerId = tagInterceptor.handlerId(tag);
if (handlerId != 0) {
this.setBox(handlerId, tag, value);
// resolve once; a miss (null) stores the primitive without boxing
TagHandler handler = tagInterceptor.handler(tag);
if (handler != null) {
Object box = value;
if (!handler.handle(this, tag, box)) {
synchronized (unsafeTags) {
unsafeTags.set(tag, box);
}
}
} else {
synchronized (unsafeTags) {
unsafeTags.set(tag, value);
Expand All @@ -977,9 +989,14 @@ public void setTag(final String tag, final float value) {
if (null == tag) {
return;
}
int handlerId = tagInterceptor.handlerId(tag);
if (handlerId != 0) {
this.setBox(handlerId, tag, value);
TagHandler handler = tagInterceptor.handler(tag);
if (handler != null) {
Object box = value;
if (!handler.handle(this, tag, box)) {
synchronized (unsafeTags) {
unsafeTags.set(tag, box);
}
}
} else {
synchronized (unsafeTags) {
unsafeTags.set(tag, value);
Expand All @@ -991,9 +1008,14 @@ public void setTag(final String tag, final double value) {
if (null == tag) {
return;
}
int handlerId = tagInterceptor.handlerId(tag);
if (handlerId != 0) {
this.setBox(handlerId, tag, value);
TagHandler handler = tagInterceptor.handler(tag);
if (handler != null) {
Object box = value;
if (!handler.handle(this, tag, box)) {
synchronized (unsafeTags) {
unsafeTags.set(tag, box);
}
}
} else {
synchronized (unsafeTags) {
unsafeTags.set(tag, value);
Expand Down
Loading