diff --git a/benchmarks/build.gradle b/benchmarks/build.gradle new file mode 100644 index 00000000..2f878a0a --- /dev/null +++ b/benchmarks/build.gradle @@ -0,0 +1,21 @@ +dependencies { + jmh project(':micrometer-tracing-bridge-brave') + jmh project(':micrometer-tracing-bridge-otel') + jmh 'org.openjdk.jmh:jmh-core:latest.release' + jmh 'io.zipkin.brave:brave-tests' + + jmh 'ch.qos.logback:logback-classic' + + // Nebula doesn't like having jmhAnnotationProcessor without jmh so we just add it twice. + jmh 'org.openjdk.jmh:jmh-generator-annprocess:latest.release' + jmhAnnotationProcessor 'org.openjdk.jmh:jmh-generator-annprocess:latest.release' +} + +jmh { + jmhVersion = '1.36' + fork = 1 + warmupIterations = 1 + iterations = 1 + duplicateClassesStrategy = DuplicatesStrategy.EXCLUDE + zip64 = true +} diff --git a/benchmarks/src/jmh/.gitignore b/benchmarks/src/jmh/.gitignore new file mode 100644 index 00000000..86d4c2dd --- /dev/null +++ b/benchmarks/src/jmh/.gitignore @@ -0,0 +1 @@ +generated diff --git a/benchmarks/src/jmh/java/io/micrometer/benchmark/tracer/BraveTracerBenchmark.java b/benchmarks/src/jmh/java/io/micrometer/benchmark/tracer/BraveTracerBenchmark.java new file mode 100644 index 00000000..35182636 --- /dev/null +++ b/benchmarks/src/jmh/java/io/micrometer/benchmark/tracer/BraveTracerBenchmark.java @@ -0,0 +1,154 @@ +/* + * Copyright 2017 VMware, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.micrometer.benchmark.tracer; + +import brave.Span; +import brave.Tracing; +import brave.handler.SpanHandler; +import brave.propagation.ThreadLocalCurrentTraceContext; +import brave.propagation.TraceContextOrSamplingFlags; +import brave.sampler.Sampler; +import io.micrometer.tracing.Tracer; +import io.micrometer.tracing.brave.bridge.BraveBaggageManager; +import io.micrometer.tracing.brave.bridge.BraveCurrentTraceContext; +import io.micrometer.tracing.brave.bridge.BraveTracer; +import org.openjdk.jmh.annotations.*; +import org.openjdk.jmh.infra.Blackhole; + +@BenchmarkMode(Mode.Throughput) +public class BraveTracerBenchmark implements MicrometerTracingBenchmarks { + + @State(Scope.Benchmark) + public static class MicrometerTracingState { + + @Param({ "5" }) + public int childSpanCount; + + ThreadLocalCurrentTraceContext braveCurrentTraceContext; + + BraveCurrentTraceContext bridgeContext; + + Tracing tracing; + + brave.Tracer braveTracer; + + Tracer tracer; + + @Setup + public void setup() { + this.braveCurrentTraceContext = ThreadLocalCurrentTraceContext.newBuilder().build(); + this.bridgeContext = new BraveCurrentTraceContext(this.braveCurrentTraceContext); + this.tracing = Tracing.newBuilder() + .currentTraceContext(this.braveCurrentTraceContext) + .sampler(Sampler.NEVER_SAMPLE) + .addSpanHandler(SpanHandler.NOOP) + .build(); + this.tracing.setNoop(true); + this.braveTracer = this.tracing.tracer(); + this.tracer = new BraveTracer(this.braveTracer, this.bridgeContext, new BraveBaggageManager()); + } + + @TearDown + public void close() { + this.tracing.close(); + } + + } + + @Benchmark + public void micrometerTracing(MicrometerTracingState state, Blackhole blackhole) { + micrometerTracing(state.tracer, state.childSpanCount, blackhole); + } + + @Benchmark + public io.micrometer.tracing.Span micrometerTracingNewSpan(MicrometerTracingState state) { + return micrometerTracingNewSpan(state.tracer); + } + + @Benchmark + public void micrometerTracingWithScope(MicrometerTracingState state, Blackhole blackhole) { + micrometerTracingWithScope(state.tracer, state.childSpanCount, blackhole); + } + + @State(Scope.Benchmark) + public static class BraveState { + + @Param({ "5" }) + public int childSpanCount; + + ThreadLocalCurrentTraceContext braveCurrentTraceContext; + + Tracing tracing; + + brave.Tracer tracer; + + @Setup + public void setup() { + this.braveCurrentTraceContext = ThreadLocalCurrentTraceContext.newBuilder().build(); + this.tracing = Tracing.newBuilder() + .currentTraceContext(this.braveCurrentTraceContext) + .sampler(Sampler.NEVER_SAMPLE) + .addSpanHandler(SpanHandler.NOOP) + .build(); + this.tracing.setNoop(true); + this.tracer = this.tracing.tracer(); + } + + @TearDown + public void close() { + this.tracing.close(); + } + + } + + @Benchmark + public void braveTracing(BraveState state, Blackhole blackhole) { + brave.Span parentSpan = state.tracer.nextSpan().name("parent-span").start(); + TraceContextOrSamplingFlags traceContext = TraceContextOrSamplingFlags.create(parentSpan.context()); + for (int i = 0; i < state.childSpanCount; i++) { + brave.Span span = state.tracer.nextSpan(traceContext).name("new-span" + i); + span.start().tag("key", "value").annotate("event").finish(); + blackhole.consume(span); + } + parentSpan.finish(); + blackhole.consume(parentSpan); + } + + @Benchmark + public Span braveTracingNewSpan(BraveState state, Blackhole blackhole) { + brave.Span span = state.tracer.nextSpan().name("child-span").start(); + span.finish(); + return span; + } + + @Benchmark + public void braveTracingWithScope(BraveState state, Blackhole blackhole) { + brave.Span parentSpan = state.tracer.nextSpan().name("parent-span").start(); + try (brave.Tracer.SpanInScope spanInScope = state.tracer.withSpanInScope(parentSpan)) { + for (int i = 0; i < state.childSpanCount; i++) { + brave.Span childSpan = state.tracer.nextSpan().name("new-span" + i); + try (brave.Tracer.SpanInScope spanInScope2 = state.tracer.withSpanInScope(childSpan.start())) { + childSpan.tag("key", "value").annotate("event"); + } + childSpan.finish(); + blackhole.consume(childSpan); + } + } + parentSpan.finish(); + blackhole.consume(parentSpan); + } + +} diff --git a/benchmarks/src/jmh/java/io/micrometer/benchmark/tracer/MicrometerTracingBenchmarks.java b/benchmarks/src/jmh/java/io/micrometer/benchmark/tracer/MicrometerTracingBenchmarks.java new file mode 100644 index 00000000..9e812731 --- /dev/null +++ b/benchmarks/src/jmh/java/io/micrometer/benchmark/tracer/MicrometerTracingBenchmarks.java @@ -0,0 +1,56 @@ +/* + * Copyright 2017 VMware, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.micrometer.benchmark.tracer; + +import io.micrometer.tracing.Span; +import io.micrometer.tracing.Tracer; +import org.openjdk.jmh.infra.Blackhole; + +interface MicrometerTracingBenchmarks { + + default void micrometerTracing(Tracer tracer, int childSpanCount, Blackhole blackhole) { + Span parentSpan = tracer.nextSpan().name("parent-span").start(); + for (int i = 0; i < childSpanCount; i++) { + Span span = tracer.nextSpan(parentSpan).name("new-span" + i); + span.start().tag("key", "value").event("event").end(); + blackhole.consume(span); + } + parentSpan.end(); + blackhole.consume(parentSpan); + } + + default Span micrometerTracingNewSpan(Tracer tracer) { + Span span = tracer.nextSpan().name("child-span").start(); + span.end(); + return span; + } + + default void micrometerTracingWithScope(Tracer tracer, int childSpanCount, Blackhole blackhole) { + Span parentSpan = tracer.nextSpan().name("parent-span").start(); + try (Tracer.SpanInScope ws = tracer.withSpan(parentSpan)) { + for (int i = 0; i < childSpanCount; i++) { + Span childSpan = tracer.nextSpan().name("new-span" + i).start(); + try (Tracer.SpanInScope ws2 = tracer.withSpan(childSpan)) { + childSpan.tag("key", "value").event("event"); + } + childSpan.end(); + } + } + parentSpan.end(); + blackhole.consume(parentSpan); + } + +} diff --git a/benchmarks/src/jmh/java/io/micrometer/benchmark/tracer/OtelTracerBenchmark.java b/benchmarks/src/jmh/java/io/micrometer/benchmark/tracer/OtelTracerBenchmark.java new file mode 100644 index 00000000..5556bed0 --- /dev/null +++ b/benchmarks/src/jmh/java/io/micrometer/benchmark/tracer/OtelTracerBenchmark.java @@ -0,0 +1,163 @@ +/* + * Copyright 2017 VMware, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.micrometer.benchmark.tracer; + +import io.micrometer.tracing.Span; +import io.micrometer.tracing.otel.bridge.OtelBaggageManager; +import io.micrometer.tracing.otel.bridge.OtelCurrentTraceContext; +import io.micrometer.tracing.otel.bridge.OtelTracer; +import io.opentelemetry.context.Context; +import io.opentelemetry.sdk.OpenTelemetrySdk; +import io.opentelemetry.sdk.trace.SdkTracerProvider; +import io.opentelemetry.sdk.trace.samplers.Sampler; +import org.openjdk.jmh.annotations.*; +import org.openjdk.jmh.infra.Blackhole; +import org.openjdk.jmh.runner.Runner; +import org.openjdk.jmh.runner.RunnerException; +import org.openjdk.jmh.runner.options.Options; +import org.openjdk.jmh.runner.options.OptionsBuilder; + +import java.util.Collections; + +@BenchmarkMode(Mode.Throughput) +public class OtelTracerBenchmark implements MicrometerTracingBenchmarks { + + public static void main(String[] args) throws RunnerException { + Options opt = new OptionsBuilder().include(OtelTracerBenchmark.class.getSimpleName()) + .warmupIterations(5) + .measurementIterations(10) + .mode(Mode.SampleTime) + .forks(1) + .build(); + + new Runner(opt).run(); + } + + @State(Scope.Benchmark) + public static class MicrometerTracingState { + + @Param({ "5" }) + public int childSpanCount; + + SdkTracerProvider sdkTracerProvider; + + OpenTelemetrySdk openTelemetrySdk; + + io.opentelemetry.api.trace.Tracer otelTracer; + + OtelCurrentTraceContext otelCurrentTraceContext; + + OtelTracer tracer; + + Span createdSpan; + + Span startedSpan; + + @Setup + public void setup() { + this.sdkTracerProvider = SdkTracerProvider.builder().setSampler(Sampler.alwaysOff()).build(); + this.openTelemetrySdk = OpenTelemetrySdk.builder().setTracerProvider(sdkTracerProvider).build(); + this.otelTracer = openTelemetrySdk.getTracerProvider().get("io.micrometer.micrometer-tracing"); + this.otelCurrentTraceContext = new OtelCurrentTraceContext(); + this.tracer = new OtelTracer(otelTracer, otelCurrentTraceContext, event -> { + }, new OtelBaggageManager(otelCurrentTraceContext, Collections.emptyList(), Collections.emptyList())); + this.createdSpan = this.tracer.nextSpan().name("created-span"); + this.startedSpan = this.tracer.nextSpan().name("started-span").start(); + } + + } + + @Benchmark + public void micrometerTracing(MicrometerTracingState state, Blackhole blackhole) { + micrometerTracing(state.tracer, state.childSpanCount, blackhole); + } + + @Benchmark + public Span micrometerTracingNewSpan(MicrometerTracingState state) { + return micrometerTracingNewSpan(state.tracer); + } + + @Benchmark + public void micrometerTracingWithScope(MicrometerTracingState state, Blackhole blackhole) { + micrometerTracingWithScope(state.tracer, state.childSpanCount, blackhole); + } + + @State(Scope.Benchmark) + public static class OtelState { + + @Param({ "5" }) + public int childSpanCount; + + SdkTracerProvider sdkTracerProvider; + + OpenTelemetrySdk openTelemetrySdk; + + io.opentelemetry.api.trace.Tracer tracer; + + io.opentelemetry.api.trace.Span startedSpan; + + Context parentContext; + + @Setup + public void setup() { + this.sdkTracerProvider = SdkTracerProvider.builder().setSampler(Sampler.alwaysOff()).build(); + this.openTelemetrySdk = OpenTelemetrySdk.builder().setTracerProvider(sdkTracerProvider).build(); + this.tracer = openTelemetrySdk.getTracerProvider().get("io.micrometer.micrometer-tracing"); + this.startedSpan = this.tracer.spanBuilder("started-span").startSpan(); + this.parentContext = Context.root().with(this.startedSpan); + } + + } + + @Benchmark + public void otelTracing(OtelState state, Blackhole blackhole) { + io.opentelemetry.api.trace.Span parentSpan = state.tracer.spanBuilder("parent-span").startSpan(); + for (int i = 0; i < state.childSpanCount; i++) { + io.opentelemetry.api.trace.Span span = state.tracer.spanBuilder("new-span" + i) + .setParent(state.parentContext) + .startSpan(); + span.setAttribute("key", "value").addEvent("event").end(); + blackhole.consume(span); + } + parentSpan.end(); + blackhole.consume(parentSpan); + } + + @Benchmark + public io.opentelemetry.api.trace.Span otelTracingNewSpan(OtelState state) { + io.opentelemetry.api.trace.Span parentSpan = state.tracer.spanBuilder("child-span").startSpan(); + parentSpan.end(); + return parentSpan; + } + + @Benchmark + public void otelTracingWithScope(OtelState state, Blackhole blackhole) { + io.opentelemetry.api.trace.Span parentSpan = state.tracer.spanBuilder("parent-span").startSpan(); + try (io.opentelemetry.context.Scope scope = parentSpan.makeCurrent()) { + for (int i = 0; i < state.childSpanCount; i++) { + io.opentelemetry.api.trace.Span span = state.tracer.spanBuilder("new-span" + i).startSpan(); + try (io.opentelemetry.context.Scope scope2 = span.makeCurrent()) { + span.setAttribute("key", "value").addEvent("event"); + } + span.end(); + blackhole.consume(span); + } + } + parentSpan.end(); + blackhole.consume(parentSpan); + } + +} diff --git a/build.gradle b/build.gradle index 78a079b0..7e1de227 100644 --- a/build.gradle +++ b/build.gradle @@ -16,6 +16,7 @@ buildscript { classpath 'io.spring.nohttp:nohttp-gradle:0.0.11' classpath 'io.github.gradle-nexus:publish-plugin:1.3.0' classpath 'io.spring.javaformat:spring-javaformat-gradle-plugin:0.0.39' + classpath 'me.champeau.jmh:jmh-gradle-plugin:0.7.1' constraints { classpath('org.ow2.asm:asm:7.3.1') { @@ -46,11 +47,16 @@ subprojects { apply plugin: 'signing' if (project.name != 'micrometer-tracing-bom') { - apply plugin: 'java-library' + if (project.name.contains('benchmarks')) { + apply plugin: 'java' + } else { + apply plugin: 'java-library' + } apply plugin: 'com.github.hierynomus.license' apply plugin: 'checkstyle' apply plugin: 'io.spring.nohttp' apply plugin: 'io.spring.javaformat' + apply plugin: 'me.champeau.jmh' java { // It is more idiomatic to define different features for different sets of optional @@ -70,6 +76,14 @@ subprojects { // JSR-305 only used for non-required meta-annotations optionalApi 'com.google.code.findbugs:jsr305:latest.release' checkstyle 'io.spring.javaformat:spring-javaformat-checkstyle:latest.release' + jmh 'org.openjdk.jmh:jmh-core:1.36' + jmh 'org.openjdk.jmh:jmh-generator-annprocess:1.36' + jmhAnnotationProcessor 'org.openjdk.jmh:jmh-generator-annprocess:1.36' + jmh 'net.sf.jopt-simple:jopt-simple' + } + + jmh { + duplicateClassesStrategy = DuplicatesStrategy.EXCLUDE } tasks { diff --git a/buildscript-gradle.lockfile b/buildscript-gradle.lockfile index f38879b0..9fe233d7 100644 --- a/buildscript-gradle.lockfile +++ b/buildscript-gradle.lockfile @@ -51,13 +51,16 @@ io.spring.nohttp:nohttp-checkstyle:0.0.11=classpath io.spring.nohttp:nohttp-gradle:0.0.11=classpath io.spring.nohttp:nohttp:0.0.11=classpath joda-time:joda-time:2.10=classpath +me.champeau.jmh:jmh-gradle-plugin:0.7.1=classpath net.java.dev.jna:jna-platform:5.7.0=classpath net.java.dev.jna:jna:5.7.0=classpath net.java.dev.jna:platform:3.4.0=classpath +net.sf.jopt-simple:jopt-simple:5.0.4=classpath net.sf.saxon:Saxon-HE:9.9.1-7=classpath org.ajoberstar.grgit:grgit-core:4.0.2=classpath org.antlr:antlr-runtime:3.4=classpath org.antlr:antlr4-runtime:4.8-1=classpath +org.apache.commons:commons-math3:3.2=classpath org.apache.commons:commons-pool2:2.2=classpath org.apache.maven:maven-settings-builder:3.0.4=classpath org.apache.maven:maven-settings:3.0.4=classpath @@ -71,6 +74,7 @@ org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.7.20=classpath org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.7.20=classpath org.jetbrains.kotlin:kotlin-stdlib:1.7.20=classpath org.jetbrains:annotations:13.0=classpath +org.openjdk.jmh:jmh-core:1.36=classpath org.slf4j:slf4j-api:1.7.30=classpath org.sonatype.plexus:plexus-cipher:1.4=classpath org.sonatype.plexus:plexus-sec-dispatcher:1.3=classpath diff --git a/settings.gradle b/settings.gradle index 917b871b..35927bbe 100644 --- a/settings.gradle +++ b/settings.gradle @@ -21,7 +21,7 @@ gradleEnterprise { server = 'https://ge.micrometer.io' } -include 'micrometer-tracing', 'micrometer-tracing-bom' +include 'micrometer-tracing', 'micrometer-tracing-bom', 'benchmarks' ['brave', 'otel'].each { bridge -> include "micrometer-tracing-bridge-$bridge"