From 13a06eed85f9e7703d20f6d868d7daeb1d82daa4 Mon Sep 17 00:00:00 2001 From: Jens Schulze Date: Thu, 10 Oct 2024 13:42:01 +0200 Subject: [PATCH] add benchmark for okhttp unzip --- build.gradle | 2 +- .../commercetools-okhttp-client3/build.gradle | 12 +- .../http/okhttp3/UnzipBenchmark.java | 120 ++++++++++++++++++ .../http/okhttp3/CtOkHttp3Client.java | 2 +- .../commercetools-okhttp-client4/build.gradle | 10 ++ .../http/okhttp4/UnzipBenchmark.java | 120 ++++++++++++++++++ .../http/okhttp4/CtOkHttp4Client.java | 6 +- .../http/okhttp4/SupplierTest.java | 49 +++++++ 8 files changed, 315 insertions(+), 6 deletions(-) create mode 100644 commercetools/commercetools-okhttp-client3/src/jmh/java/com/commercetools/http/okhttp3/UnzipBenchmark.java create mode 100644 commercetools/commercetools-okhttp-client4/src/jmh/java/com/commercetools/http/okhttp4/UnzipBenchmark.java diff --git a/build.gradle b/build.gradle index 5e3bca76272..32a90550b9d 100644 --- a/build.gradle +++ b/build.gradle @@ -17,7 +17,7 @@ plugins { id 'io.github.gradle-nexus.publish-plugin' version '1.3.0' id 'com.github.jk1.dependency-license-report' version '2.0' - id "me.champeau.jmh" version "0.7.2" + id "me.champeau.jmh" version "0.6.8" id "jacoco" } diff --git a/commercetools/commercetools-okhttp-client3/build.gradle b/commercetools/commercetools-okhttp-client3/build.gradle index b7605566d7b..c08ac3cb6c7 100644 --- a/commercetools/commercetools-okhttp-client3/build.gradle +++ b/commercetools/commercetools-okhttp-client3/build.gradle @@ -1,8 +1,18 @@ +apply plugin: "me.champeau.jmh" + +jmh { + iterations = 5 + benchmarkMode = ['thrpt'] + threads = 25 + fork = 3 + timeOnIteration = '1s' + profilers = ['gc'] +} dependencies { api project(":rmf:rmf-java-base") - api "com.squareup.okio:okio:3.9.0" + implementation "com.squareup.okio:okio:3.9.0" api "com.squareup.okhttp3:okhttp:3.14.9" version { strictly "[3.0,4.0[" prefer "3.14.9" diff --git a/commercetools/commercetools-okhttp-client3/src/jmh/java/com/commercetools/http/okhttp3/UnzipBenchmark.java b/commercetools/commercetools-okhttp-client3/src/jmh/java/com/commercetools/http/okhttp3/UnzipBenchmark.java new file mode 100644 index 00000000000..38ce5e74e08 --- /dev/null +++ b/commercetools/commercetools-okhttp-client3/src/jmh/java/com/commercetools/http/okhttp3/UnzipBenchmark.java @@ -0,0 +1,120 @@ + +package com.commercetools.http.okhttp3; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.nio.charset.StandardCharsets; +import java.util.zip.GZIPOutputStream; + +import io.vrap.rmf.base.client.ApiHttpResponse; + +import org.assertj.core.api.Assertions; +import org.openjdk.jmh.annotations.*; + +import okhttp3.*; +import okio.Okio; + +public class UnzipBenchmark { + + @State(Scope.Benchmark) + public static class InterceptorState { + private CtOkHttp3Client.UnzippingInterceptor interceptor; + + @Setup(Level.Trial) + public void init() { + interceptor = new CtOkHttp3Client.UnzippingInterceptor(); + printUsedMemory(); + + } + + @TearDown(Level.Trial) + public void tearDown() { + printUsedMemory(); + } + } + @Benchmark + public void unzip(InterceptorState state) throws IOException { + + ByteArrayOutputStream os = new ByteArrayOutputStream(); + GZIPOutputStream gzipOs = new GZIPOutputStream(os); + byte[] buffer = "Sample Text".getBytes(); + gzipOs.write(buffer, 0, buffer.length); + gzipOs.close(); + ByteArrayInputStream inputStream = new ByteArrayInputStream(os.toByteArray()); + + Response gzipped = new Response.Builder().request(new Request.Builder().url("http://localhost").build()) + .protocol(Protocol.HTTP_1_1) + .addHeader("content-encoding", "gzip") + .addHeader("content-type", "application/json") + .code(200) + .message("Ok") + .body(ResponseBody.create(MediaType.parse("application/json"), -1L, + Okio.buffer(Okio.source(inputStream)))) + .build(); + Assertions.assertThat(gzipped.body().source().isOpen()).isTrue(); + + Response unzipped = state.interceptor.unzip(gzipped); + + Assertions.assertThat(gzipped.body().source().isOpen()).isTrue(); + Assertions.assertThat(unzipped.body().source().isOpen()).isTrue(); + Assertions.assertThat(inputStream.available()).isEqualTo(31); + + ApiHttpResponse response = CtOkHttp3Client.toResponse(unzipped); + + Assertions.assertThat(gzipped.body().source().isOpen()).isFalse(); + Assertions.assertThat(unzipped.body().source().isOpen()).isFalse(); + Assertions.assertThat(inputStream.available()).isEqualTo(0); + + String text = new String(response.getBody(), StandardCharsets.UTF_8); + Assertions.assertThat(text).isEqualTo("Sample Text"); + } + + public static void printUsedMemory() { + long _usedMem; + long _total; + long _total2; + long _count = -1; + System.out.flush(); + // loop to get a stable reading, since memory may be resized between the method calls + do { + _count++; + _total = Runtime.getRuntime().totalMemory(); + try { + Thread.sleep(12); + } + catch (Exception ignore) { + } + long _free = Runtime.getRuntime().freeMemory(); + _total2 = Runtime.getRuntime().totalMemory(); + _usedMem = _total - _free; + } while (_total != _total2); + System.out.println("before GC: used=" + _usedMem + ", loopCount=" + _count + ", total=" + _total); + try { + Runtime.getRuntime().gc(); + Thread.sleep(55); + Runtime.getRuntime().gc(); + Thread.sleep(55); + Runtime.getRuntime().gc(); + Thread.sleep(55); + Runtime.getRuntime().gc(); + Thread.sleep(55); + } + catch (Exception ignore) { + } + // loop to get a stable reading, since memory may be resized between the method calls + do { + _count++; + _total = Runtime.getRuntime().totalMemory(); + try { + Thread.sleep(12); + } + catch (Exception ignore) { + } + long _free = Runtime.getRuntime().freeMemory(); + _total2 = Runtime.getRuntime().totalMemory(); + _usedMem = _total - _free; + } while (_total != _total2); + System.out.println("after GC: used=" + _usedMem + ", loopCount=" + _count + ", total=" + _total); + } +} diff --git a/commercetools/commercetools-okhttp-client3/src/main/java/com/commercetools/http/okhttp3/CtOkHttp3Client.java b/commercetools/commercetools-okhttp-client3/src/main/java/com/commercetools/http/okhttp3/CtOkHttp3Client.java index f5428bdfe4b..765031ea614 100644 --- a/commercetools/commercetools-okhttp-client3/src/main/java/com/commercetools/http/okhttp3/CtOkHttp3Client.java +++ b/commercetools/commercetools-okhttp-client3/src/main/java/com/commercetools/http/okhttp3/CtOkHttp3Client.java @@ -120,7 +120,7 @@ static ApiHttpResponse toResponse(final okhttp3.Response response) { .map(Utils.wrapToCompletionException(okhttp3.ResponseBody::bytes)) .orElse(null), response.message()); - if (apiHttpResponse.getBody() != null) { + if (response.body() != null) { response.body().close(); } return apiHttpResponse; diff --git a/commercetools/commercetools-okhttp-client4/build.gradle b/commercetools/commercetools-okhttp-client4/build.gradle index fd77de638b5..91851dee307 100644 --- a/commercetools/commercetools-okhttp-client4/build.gradle +++ b/commercetools/commercetools-okhttp-client4/build.gradle @@ -1,3 +1,13 @@ +apply plugin: "me.champeau.jmh" + +jmh { + iterations = 5 + benchmarkMode = ['thrpt'] + threads = 25 + fork = 3 + timeOnIteration = '1s' + profilers = ['gc'] +} dependencies { api project(":rmf:rmf-java-base") diff --git a/commercetools/commercetools-okhttp-client4/src/jmh/java/com/commercetools/http/okhttp4/UnzipBenchmark.java b/commercetools/commercetools-okhttp-client4/src/jmh/java/com/commercetools/http/okhttp4/UnzipBenchmark.java new file mode 100644 index 00000000000..1ab6865683a --- /dev/null +++ b/commercetools/commercetools-okhttp-client4/src/jmh/java/com/commercetools/http/okhttp4/UnzipBenchmark.java @@ -0,0 +1,120 @@ + +package com.commercetools.http.okhttp4; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.nio.charset.StandardCharsets; +import java.util.zip.GZIPOutputStream; + +import io.vrap.rmf.base.client.ApiHttpResponse; + +import org.assertj.core.api.Assertions; +import org.openjdk.jmh.annotations.*; + +import okhttp3.*; +import okio.Okio; + +public class UnzipBenchmark { + + @State(Scope.Benchmark) + public static class InterceptorState { + private CtOkHttp4Client.UnzippingInterceptor interceptor; + + @Setup(Level.Trial) + public void init() { + interceptor = new CtOkHttp4Client.UnzippingInterceptor(); + printUsedMemory(); + + } + + @TearDown(Level.Trial) + public void tearDown() { + printUsedMemory(); + } + } + @Benchmark + public void unzip(InterceptorState state) throws IOException { + + ByteArrayOutputStream os = new ByteArrayOutputStream(); + GZIPOutputStream gzipOs = new GZIPOutputStream(os); + byte[] buffer = "Sample Text".getBytes(); + gzipOs.write(buffer, 0, buffer.length); + gzipOs.close(); + ByteArrayInputStream inputStream = new ByteArrayInputStream(os.toByteArray()); + + Response gzipped = new Response.Builder().request(new Request.Builder().url("http://localhost").build()) + .protocol(Protocol.HTTP_1_1) + .addHeader("content-encoding", "gzip") + .addHeader("content-type", "application/json") + .code(200) + .message("Ok") + .body(ResponseBody.create(MediaType.parse("application/json"), -1L, + Okio.buffer(Okio.source(inputStream)))) + .build(); + Assertions.assertThat(gzipped.body().source().isOpen()).isTrue(); + + Response unzipped = state.interceptor.unzip(gzipped); + + Assertions.assertThat(gzipped.body().source().isOpen()).isTrue(); + Assertions.assertThat(unzipped.body().source().isOpen()).isTrue(); + Assertions.assertThat(inputStream.available()).isEqualTo(31); + + ApiHttpResponse response = CtOkHttp4Client.toResponse(unzipped); + + Assertions.assertThat(gzipped.body().source().isOpen()).isFalse(); + Assertions.assertThat(unzipped.body().source().isOpen()).isFalse(); + Assertions.assertThat(inputStream.available()).isEqualTo(0); + + String text = new String(response.getBody(), StandardCharsets.UTF_8); + Assertions.assertThat(text).isEqualTo("Sample Text"); + } + + public static void printUsedMemory() { + long _usedMem; + long _total; + long _total2; + long _count = -1; + System.out.flush(); + // loop to get a stable reading, since memory may be resized between the method calls + do { + _count++; + _total = Runtime.getRuntime().totalMemory(); + try { + Thread.sleep(12); + } + catch (Exception ignore) { + } + long _free = Runtime.getRuntime().freeMemory(); + _total2 = Runtime.getRuntime().totalMemory(); + _usedMem = _total - _free; + } while (_total != _total2); + System.out.println("before GC: used=" + _usedMem + ", loopCount=" + _count + ", total=" + _total); + try { + Runtime.getRuntime().gc(); + Thread.sleep(55); + Runtime.getRuntime().gc(); + Thread.sleep(55); + Runtime.getRuntime().gc(); + Thread.sleep(55); + Runtime.getRuntime().gc(); + Thread.sleep(55); + } + catch (Exception ignore) { + } + // loop to get a stable reading, since memory may be resized between the method calls + do { + _count++; + _total = Runtime.getRuntime().totalMemory(); + try { + Thread.sleep(12); + } + catch (Exception ignore) { + } + long _free = Runtime.getRuntime().freeMemory(); + _total2 = Runtime.getRuntime().totalMemory(); + _usedMem = _total - _free; + } while (_total != _total2); + System.out.println("after GC: used=" + _usedMem + ", loopCount=" + _count + ", total=" + _total); + } +} diff --git a/commercetools/commercetools-okhttp-client4/src/main/java/com/commercetools/http/okhttp4/CtOkHttp4Client.java b/commercetools/commercetools-okhttp-client4/src/main/java/com/commercetools/http/okhttp4/CtOkHttp4Client.java index a79889b200f..e8166fac7d9 100644 --- a/commercetools/commercetools-okhttp-client4/src/main/java/com/commercetools/http/okhttp4/CtOkHttp4Client.java +++ b/commercetools/commercetools-okhttp-client4/src/main/java/com/commercetools/http/okhttp4/CtOkHttp4Client.java @@ -107,7 +107,7 @@ public CompletableFuture> execute(final ApiHttpRequest r } - private static ApiHttpResponse toResponse(final okhttp3.Response response) { + static ApiHttpResponse toResponse(final okhttp3.Response response) { final ApiHttpHeaders apiHttpHeaders = new ApiHttpHeaders(response.headers() .toMultimap() .entrySet() @@ -120,7 +120,7 @@ private static ApiHttpResponse toResponse(final okhttp3.Response respons .map(Utils.wrapToCompletionException(okhttp3.ResponseBody::bytes)) .orElse(null), response.message()); - if (apiHttpResponse.getBody() != null) { + if (response.body() != null) { response.close(); } return apiHttpResponse; @@ -201,7 +201,7 @@ public okhttp3.Response intercept(Chain chain) throws IOException { return unzip(response); } - private okhttp3.Response unzip(final okhttp3.Response response) { + okhttp3.Response unzip(final okhttp3.Response response) { if (!"gzip".equalsIgnoreCase(response.header("Content-Encoding"))) { return response; } diff --git a/commercetools/commercetools-okhttp-client4/src/test/java/com/commercetools/http/okhttp4/SupplierTest.java b/commercetools/commercetools-okhttp-client4/src/test/java/com/commercetools/http/okhttp4/SupplierTest.java index 61251c78ee3..011f9112dd6 100644 --- a/commercetools/commercetools-okhttp-client4/src/test/java/com/commercetools/http/okhttp4/SupplierTest.java +++ b/commercetools/commercetools-okhttp-client4/src/test/java/com/commercetools/http/okhttp4/SupplierTest.java @@ -1,14 +1,63 @@ package com.commercetools.http.okhttp4; +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.nio.charset.StandardCharsets; +import java.util.zip.GZIPOutputStream; + +import io.vrap.rmf.base.client.ApiHttpResponse; import io.vrap.rmf.base.client.HttpClientSupplier; import org.assertj.core.api.Assertions; import org.junit.jupiter.api.Test; +import okhttp3.*; +import okio.Okio; + public class SupplierTest { @Test public void testCreate() { Assertions.assertThat(HttpClientSupplier.of().get()).isInstanceOf(CtOkHttp4Client.class); } + + @Test + public void testUnzip() throws IOException { + + ByteArrayOutputStream os = new ByteArrayOutputStream(); + GZIPOutputStream gzipOs = new GZIPOutputStream(os); + byte[] buffer = "Sample Text".getBytes(); + gzipOs.write(buffer, 0, buffer.length); + gzipOs.close(); + ByteArrayInputStream inputStream = new ByteArrayInputStream(os.toByteArray()); + + CtOkHttp4Client.UnzippingInterceptor interceptor = new CtOkHttp4Client.UnzippingInterceptor(); + + Response gzipped = new Response.Builder().request(new Request.Builder().url("http://localhost").build()) + .protocol(Protocol.HTTP_1_1) + .addHeader("content-encoding", "gzip") + .addHeader("content-type", "application/json") + .code(200) + .message("Ok") + .body(ResponseBody.create(MediaType.parse("application/json"), -1L, + Okio.buffer(Okio.source(inputStream)))) + .build(); + Assertions.assertThat(gzipped.body().source().isOpen()).isTrue(); + + Response unzipped = interceptor.unzip(gzipped); + + Assertions.assertThat(gzipped.body().source().isOpen()).isTrue(); + Assertions.assertThat(unzipped.body().source().isOpen()).isTrue(); + Assertions.assertThat(inputStream.available()).isEqualTo(31); + + ApiHttpResponse response = CtOkHttp4Client.toResponse(unzipped); + + Assertions.assertThat(gzipped.body().source().isOpen()).isFalse(); + Assertions.assertThat(unzipped.body().source().isOpen()).isFalse(); + Assertions.assertThat(inputStream.available()).isEqualTo(0); + + String text = new String(response.getBody(), StandardCharsets.UTF_8); + Assertions.assertThat(text).isEqualTo("Sample Text"); + } }