diff --git a/benchmark/benchmark.gradle b/benchmark/benchmark.gradle index bf7b2db898..f0c8a71dc8 100644 --- a/benchmark/benchmark.gradle +++ b/benchmark/benchmark.gradle @@ -7,6 +7,11 @@ apply from: "${rootDir}/gradle/java.gradle" dependencies { jmh group: 'io.opentelemetry', name: 'opentelemetry-api', version: '0.3.0' jmh deps.bytebuddyagent + + jmh 'javax.servlet:javax.servlet-api:4.0.1' + jmh 'com.google.http-client:google-http-client:1.19.0' + jmh 'org.eclipse.jetty:jetty-server:9.4.1.v20170120' + jmh 'org.eclipse.jetty:jetty-servlet:9.4.1.v20170120' } jmh { @@ -25,6 +30,7 @@ jmh { // profilers = ['stack:lines=5;detailLine=true;period=5;excludePackages=true'] // Use profilers to collect additional data. Supported profilers: [cl, comp, gc, stack, perf, perfnorm, perfasm, xperf, xperfasm, hs_cl, hs_comp, hs_gc, hs_rt, hs_thr] + profilers = ['io.opentelemetry.benchmark.UsedMemoryProfiler', 'gc'] // humanOutputFile = project.file("${project.buildDir}/reports/jmh/human.txt") // human-readable output file // operationsPerInvocation = 10 // Operations per invocation. diff --git a/benchmark/src/jmh/java/io/opentelemetry/benchmark/HttpBenchmark.java b/benchmark/src/jmh/java/io/opentelemetry/benchmark/HttpBenchmark.java new file mode 100644 index 0000000000..c1070418eb --- /dev/null +++ b/benchmark/src/jmh/java/io/opentelemetry/benchmark/HttpBenchmark.java @@ -0,0 +1,74 @@ +/* + * Copyright The OpenTelemetry Authors + * + * 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 + * + * http://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.opentelemetry.benchmark; + +import io.opentelemetry.benchmark.classes.HttpClass; +import java.io.IOException; +import org.eclipse.jetty.server.Server; +import org.eclipse.jetty.util.component.AbstractLifeCycle; +import org.openjdk.jmh.annotations.Benchmark; +import org.openjdk.jmh.annotations.Fork; +import org.openjdk.jmh.annotations.Level; +import org.openjdk.jmh.annotations.Scope; +import org.openjdk.jmh.annotations.Setup; +import org.openjdk.jmh.annotations.State; +import org.openjdk.jmh.annotations.TearDown; + +public class HttpBenchmark { + + @State(Scope.Benchmark) + public static class BenchmarkState { + @Setup(Level.Trial) + public void doSetup() { + try { + jettyServer = new HttpClass().buildJettyServer(); + jettyServer.start(); + // Make sure it's actually running + while (!AbstractLifeCycle.STARTED.equals(jettyServer.getState())) { + Thread.sleep(500); + } + } catch (final Exception e) { + throw new RuntimeException(e); + } + } + + @TearDown(Level.Trial) + public void doTearDown() { + try { + jettyServer.stop(); + } catch (final Exception e) { + e.printStackTrace(); + } finally { + jettyServer.destroy(); + } + } + + HttpClass http = new HttpClass(); + Server jettyServer; + } + + @Benchmark + public void testMakingRequest(final BenchmarkState state) throws IOException { + state.http.executeRequest(); + } + + @Fork( + jvmArgsAppend = { + "-javaagent:/path/to/opentelemetry-auto-instr-java/java-agent/build/libs/opentelemetry-auto.jar", + "-Dota.exporter.jar=/path/to/opentelemetry-auto-instr-java/auto-exporters/logging/build/libs/opentelemetry-auto-exporters-logging-or-other-exporter.jar" + }) + public static class WithAgent extends ClassRetransformingBenchmark {} +} diff --git a/benchmark/src/jmh/java/io/opentelemetry/benchmark/UsedMemoryProfiler.java b/benchmark/src/jmh/java/io/opentelemetry/benchmark/UsedMemoryProfiler.java new file mode 100644 index 0000000000..5afa3584c7 --- /dev/null +++ b/benchmark/src/jmh/java/io/opentelemetry/benchmark/UsedMemoryProfiler.java @@ -0,0 +1,66 @@ +/* + * Copyright The OpenTelemetry Authors + * + * 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 + * + * http://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.opentelemetry.benchmark; + +import java.util.ArrayList; +import java.util.Collection; +import org.openjdk.jmh.infra.BenchmarkParams; +import org.openjdk.jmh.infra.IterationParams; +import org.openjdk.jmh.profile.InternalProfiler; +import org.openjdk.jmh.results.AggregationPolicy; +import org.openjdk.jmh.results.IterationResult; +import org.openjdk.jmh.results.Result; +import org.openjdk.jmh.results.ScalarResult; + +public class UsedMemoryProfiler implements InternalProfiler { + private long totalHeapBefore; + private long usedHeapBefore; + + @Override + public String getDescription() { + return "Used memory heap profiler"; + } + + @Override + public void beforeIteration( + final BenchmarkParams benchmarkParams, final IterationParams iterationParams) { + System.gc(); + System.runFinalization(); + + totalHeapBefore = Runtime.getRuntime().totalMemory(); + usedHeapBefore = Runtime.getRuntime().totalMemory() - Runtime.getRuntime().freeMemory(); + } + + @Override + public Collection afterIteration( + final BenchmarkParams benchmarkParams, + final IterationParams iterationParams, + final IterationResult result) { + + final long totalHeap = Runtime.getRuntime().totalMemory(); + final long usedHeap = Runtime.getRuntime().totalMemory() - Runtime.getRuntime().freeMemory(); + + final Collection results = new ArrayList<>(); + results.add( + new ScalarResult("heap.total.before", totalHeapBefore, "bytes", AggregationPolicy.AVG)); + results.add( + new ScalarResult("heap.used.before", usedHeapBefore, "bytes", AggregationPolicy.AVG)); + results.add(new ScalarResult("heap.total.after", totalHeap, "bytes", AggregationPolicy.AVG)); + results.add(new ScalarResult("heap.used.after", usedHeap, "bytes", AggregationPolicy.AVG)); + + return results; + } +} diff --git a/benchmark/src/jmh/java/io/opentelemetry/benchmark/classes/HttpClass.java b/benchmark/src/jmh/java/io/opentelemetry/benchmark/classes/HttpClass.java new file mode 100644 index 0000000000..37d4cdd49d --- /dev/null +++ b/benchmark/src/jmh/java/io/opentelemetry/benchmark/classes/HttpClass.java @@ -0,0 +1,72 @@ +/* + * Copyright The OpenTelemetry Authors + * + * 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 + * + * http://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.opentelemetry.benchmark.classes; + +import com.google.api.client.http.GenericUrl; +import com.google.api.client.http.HttpRequest; +import com.google.api.client.http.HttpRequestFactory; +import com.google.api.client.http.javanet.NetHttpTransport; +import java.io.IOException; +import java.net.InetSocketAddress; +import javax.servlet.ServletException; +import javax.servlet.annotation.WebServlet; +import javax.servlet.http.HttpServlet; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import org.eclipse.jetty.server.Server; +import org.eclipse.jetty.servlet.ServletContextHandler; + +public class HttpClass { + private String contextPath = "/path"; + private Integer port = 18888; + + public Server buildJettyServer() { + System.setProperty("org.eclipse.jetty.util.log.class", "org.eclipse.jetty.util.log.StdErrLog"); + System.setProperty("org.eclipse.jetty.LEVEL", "WARN"); + + Server jettyServer = new Server(new InetSocketAddress("localhost", port)); + ServletContextHandler servletContext = new ServletContextHandler(); + + servletContext.addServlet(HttpClassServlet.class, contextPath); + jettyServer.setHandler(servletContext); + return jettyServer; + } + + @WebServlet + public static class HttpClassServlet extends HttpServlet { + @Override + protected void doGet(final HttpServletRequest req, final HttpServletResponse resp) + throws ServletException, IOException { + try { + Thread.sleep(10); + } catch (Exception e) { + } + resp.setContentType("application/json"); + resp.setStatus(HttpServletResponse.SC_OK); + resp.getWriter().println("{ \"status\": \"ok\"}"); + } + } + + private HttpRequestFactory requestFactory = new NetHttpTransport().createRequestFactory(); + + public void executeRequest() throws IOException { + String url = "http://localhost:" + port + contextPath; + + HttpRequest request = requestFactory.buildGetRequest(new GenericUrl(url)); + request.setThrowExceptionOnExecuteError(false); + request.execute(); + } +}