From 102670c86b9d0011b451eec7042755b46a110da7 Mon Sep 17 00:00:00 2001 From: Dongie Agnir Date: Mon, 1 Dec 2025 12:52:24 -0800 Subject: [PATCH 1/7] Add VirtualThread HTTP benchmarks Add virtual thread benchmarks for supported HTTP clients. Note that these "benchmarks" act more as verification that virtual threads don't get pinned with various HTTP clients rather than a direct measurement of performance gains or improvements as a result of using virtual threads over platform threads. This commit also refactors existing Apache4 and Apache5Benchmark slightly. --- test/http-client-benchmarks/pom.xml | 18 ++ .../AsyncVirtualThreadBenchmark.java | 207 ++++++++++++++++ .../benchmark/UnifiedBenchmarkRunner.java | 3 +- .../benchmark/VirtualThreadBenchmark.java | 220 ++++++++++++++++++ .../benchmark/apache4/Apache4Benchmark.java | 44 ++-- .../benchmark/apache5/Apache5Benchmark.java | 60 +++-- .../apache5/Apache5VirtualBenchmark.java | 215 ----------------- .../awssdk/benchmark/core/AsyncS3Wrapper.java | 64 +++++ .../awssdk/benchmark/core/ObjectSize.java | 33 +++ .../benchmark/core/S3BenchmarkHelper.java | 145 ++++++++++++ .../benchmark/core/S3BenchmarkImpl.java | 174 -------------- .../awssdk/benchmark/core/S3Wrapper.java | 36 +++ .../awssdk/benchmark/core/SyncS3Wrapper.java | 64 +++++ 13 files changed, 859 insertions(+), 424 deletions(-) create mode 100644 test/http-client-benchmarks/src/main/java/software/amazon/awssdk/benchmark/AsyncVirtualThreadBenchmark.java create mode 100644 test/http-client-benchmarks/src/main/java/software/amazon/awssdk/benchmark/VirtualThreadBenchmark.java delete mode 100644 test/http-client-benchmarks/src/main/java/software/amazon/awssdk/benchmark/apache5/Apache5VirtualBenchmark.java create mode 100644 test/http-client-benchmarks/src/main/java/software/amazon/awssdk/benchmark/core/AsyncS3Wrapper.java create mode 100644 test/http-client-benchmarks/src/main/java/software/amazon/awssdk/benchmark/core/ObjectSize.java create mode 100644 test/http-client-benchmarks/src/main/java/software/amazon/awssdk/benchmark/core/S3BenchmarkHelper.java delete mode 100644 test/http-client-benchmarks/src/main/java/software/amazon/awssdk/benchmark/core/S3BenchmarkImpl.java create mode 100644 test/http-client-benchmarks/src/main/java/software/amazon/awssdk/benchmark/core/S3Wrapper.java create mode 100644 test/http-client-benchmarks/src/main/java/software/amazon/awssdk/benchmark/core/SyncS3Wrapper.java diff --git a/test/http-client-benchmarks/pom.xml b/test/http-client-benchmarks/pom.xml index 7b2b8d3f4ecd..a23b6fcbebe4 100644 --- a/test/http-client-benchmarks/pom.xml +++ b/test/http-client-benchmarks/pom.xml @@ -74,6 +74,24 @@ ${awsjavasdk.version}-PREVIEW + + software.amazon.awssdk + url-connection-client + ${awsjavasdk.version} + + + + software.amazon.awssdk + aws-crt-client + ${awsjavasdk.version} + + + + software.amazon.awssdk + netty-nio-client + ${awsjavasdk.version} + + org.apache.logging.log4j log4j-api diff --git a/test/http-client-benchmarks/src/main/java/software/amazon/awssdk/benchmark/AsyncVirtualThreadBenchmark.java b/test/http-client-benchmarks/src/main/java/software/amazon/awssdk/benchmark/AsyncVirtualThreadBenchmark.java new file mode 100644 index 000000000000..04b8ca1ce731 --- /dev/null +++ b/test/http-client-benchmarks/src/main/java/software/amazon/awssdk/benchmark/AsyncVirtualThreadBenchmark.java @@ -0,0 +1,207 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file 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 software.amazon.awssdk.benchmark; + +import static software.amazon.awssdk.benchmark.apache5.utility.BenchmarkUtilities.isJava21OrHigher; + +import java.io.IOException; +import java.io.PrintStream; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.nio.file.StandardOpenOption; +import java.util.UUID; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.TimeUnit; +import org.openjdk.jmh.annotations.Benchmark; +import org.openjdk.jmh.annotations.Fork; +import org.openjdk.jmh.annotations.Level; +import org.openjdk.jmh.annotations.Param; +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.infra.Blackhole; +import software.amazon.awssdk.auth.credentials.DefaultCredentialsProvider; +import software.amazon.awssdk.benchmark.core.ObjectSize; +import software.amazon.awssdk.benchmark.core.S3BenchmarkHelper; +import software.amazon.awssdk.core.ResponseInputStream; +import software.amazon.awssdk.core.async.AsyncResponseTransformer; +import software.amazon.awssdk.http.SdkHttpConfigurationOption; +import software.amazon.awssdk.http.async.SdkAsyncHttpClient; +import software.amazon.awssdk.http.crt.AwsCrtAsyncHttpClient; +import software.amazon.awssdk.http.nio.netty.NettyNioAsyncHttpClient; +import software.amazon.awssdk.regions.Region; +import software.amazon.awssdk.services.s3.S3AsyncClient; +import software.amazon.awssdk.services.s3.model.GetObjectResponse; +import software.amazon.awssdk.services.s3.model.PutObjectResponse; +import software.amazon.awssdk.utils.AttributeMap; +import software.amazon.awssdk.utils.IoUtils; +import software.amazon.awssdk.utils.JavaSystemSetting; + +/** + * Async http client benchmark using virtual threads. This class requires Java 21+. + */ +@Fork(jvmArgsAppend = "-Djdk.tracePinnedThreads=full") +@State(Scope.Benchmark) +public class AsyncVirtualThreadBenchmark { + // We redirect standard out to a file for the -Djdk.tracePinnedThreads=full option. When virtual threads become pinned, + // the JDK will print out the stacktrace through standard out. However, because JMH runs benchmarks in a forked JVM + // (unless you specify -f 0, which is not recommended by JMH), that output is lost. Redirect standard out to a file so + // that any time a thread is pinned, the stack trace is written to the file instead,which can be inspected after the + // benchmark run. + static { + try { + Path tmp = Paths.get(AsyncVirtualThreadBenchmark.class.getSimpleName() + "-stdout-" + UUID.randomUUID() + ".log"); + PrintStream fileOut = new PrintStream(Files.newOutputStream(tmp, StandardOpenOption.APPEND, StandardOpenOption.CREATE)); + System.setOut(fileOut); + } catch (IOException e) { + throw new RuntimeException("Unable to create STDOUT file", e); + } + } + public enum Client { + Netty, + Crt + } + + @Param("50") + private int maxConnections; + + @Param("SMALL") + private ObjectSize objectSize; + + @Param({"Netty", "Crt"}) + private Client client; + + private S3AsyncClient s3AsyncClient; + private S3BenchmarkHelper benchmark; + private ExecutorService virtualThreadExecutor; + private String putKeyPrefix; + + @Setup(Level.Trial) + public void setup() { + if (!isJava21OrHigher()) { + throw new UnsupportedOperationException( + "Virtual threads require Java 21 or higher. Current version: " + JavaSystemSetting.JAVA_VERSION); + } + + SdkAsyncHttpClient.Builder httpClientBuilder = httpClientBuilder(); + + s3AsyncClient = S3AsyncClient.builder() + .region(Region.US_WEST_2) + .credentialsProvider(DefaultCredentialsProvider.create()) + .httpClient(configure(httpClientBuilder)) + .build(); + + String benchmarkName = AsyncVirtualThreadBenchmark.class.getSimpleName(); + + benchmark = new S3BenchmarkHelper(benchmarkName, s3AsyncClient); + benchmark.setup(); + + virtualThreadExecutor = createVirtualThreadExecutor(); + + putKeyPrefix = benchmarkName + "-"; + } + + private SdkAsyncHttpClient configure(SdkAsyncHttpClient.Builder builder) { + AttributeMap config = AttributeMap.builder() + .put(SdkHttpConfigurationOption.MAX_CONNECTIONS, maxConnections) + .build(); + + return builder.buildWithDefaults(config); + } + + private ExecutorService createVirtualThreadExecutor() { + try { + // Use reflection to call Executors.newVirtualThreadPerTaskExecutor() + Method method = Executors.class.getMethod("newVirtualThreadPerTaskExecutor"); + return (ExecutorService) method.invoke(null); + } catch (NoSuchMethodException e) { + throw new UnsupportedOperationException( + "Virtual threads are not available in this Java version. " + + "This benchmark requires Java 21 or higher.", e); + } catch (IllegalAccessException | InvocationTargetException e) { + throw new RuntimeException("Failed to create virtual thread executor", e); + } + } + + @Benchmark + public void getObject(Blackhole blackhole) { + safeExecute(() -> { + ResponseInputStream object = s3AsyncClient.getObject( + r -> r.bucket(benchmark.bucketName()).key(benchmark.objKey(objectSize)), + AsyncResponseTransformer.toBlockingInputStream()).join(); + blackhole.consume(object.response()); + IoUtils.drainInputStream(object); + }); + } + + @Benchmark + public void putObject(Blackhole blackhole) { + String jmhThreadName = Thread.currentThread().getName(); + safeExecute(() -> { + PutObjectResponse response = s3AsyncClient.putObject( + r -> r.bucket(benchmark.bucketName()).key(putKeyPrefix + jmhThreadName), + benchmark.asyncRequestBody(objectSize)).join(); + blackhole.consume(response); + }); + } + + @TearDown(Level.Trial) + public void tearDown() { + if (virtualThreadExecutor != null) { + virtualThreadExecutor.shutdown(); + try { + if (!virtualThreadExecutor.awaitTermination(30, TimeUnit.SECONDS)) { + virtualThreadExecutor.shutdownNow(); + } + } catch (InterruptedException e) { + virtualThreadExecutor.shutdownNow(); + } + } + + if (benchmark != null) { + benchmark.cleanup(); + } + + if (s3AsyncClient != null) { + s3AsyncClient.close(); + } + } + + private void safeExecute(Runnable runnable) { + try { + virtualThreadExecutor.submit(runnable).get(); + } catch (InterruptedException | ExecutionException e) { + throw new RuntimeException("Error during execution", e); + } + } + + private SdkAsyncHttpClient.Builder httpClientBuilder() { + switch (client) { + case Netty: + return NettyNioAsyncHttpClient.builder(); + case Crt: + return AwsCrtAsyncHttpClient.builder(); + default: + throw new IllegalArgumentException("Unknown HTTP client: " + client); + } + } +} diff --git a/test/http-client-benchmarks/src/main/java/software/amazon/awssdk/benchmark/UnifiedBenchmarkRunner.java b/test/http-client-benchmarks/src/main/java/software/amazon/awssdk/benchmark/UnifiedBenchmarkRunner.java index 8ee83735e204..f0f14a976db6 100644 --- a/test/http-client-benchmarks/src/main/java/software/amazon/awssdk/benchmark/UnifiedBenchmarkRunner.java +++ b/test/http-client-benchmarks/src/main/java/software/amazon/awssdk/benchmark/UnifiedBenchmarkRunner.java @@ -31,7 +31,6 @@ import org.openjdk.jmh.runner.options.OptionsBuilder; import software.amazon.awssdk.benchmark.apache4.Apache4Benchmark; import software.amazon.awssdk.benchmark.apache5.Apache5Benchmark; -import software.amazon.awssdk.benchmark.apache5.Apache5VirtualBenchmark; import software.amazon.awssdk.benchmark.core.BenchmarkResult; import software.amazon.awssdk.benchmark.metrics.CloudWatchMetricsPublisher; import software.amazon.awssdk.regions.Region; @@ -76,7 +75,7 @@ public static void main(String[] args) throws Exception { // Only run virtual threads benchmark if Java 21+ if (isJava21OrHigher()) { logger.info(() -> "Running Apache5 with virtual threads..."); - allResults.addAll(runBenchmark("Apache5-Virtual", Apache5VirtualBenchmark.class)); + allResults.addAll(runBenchmark("Apache5-Virtual", VirtualThreadBenchmark.class)); } else { logger.info(() -> "Skipping virtual threads benchmark - requires Java 21 or higher (current: " + JavaSystemSetting.JAVA_VERSION.getStringValueOrThrow() + ")"); diff --git a/test/http-client-benchmarks/src/main/java/software/amazon/awssdk/benchmark/VirtualThreadBenchmark.java b/test/http-client-benchmarks/src/main/java/software/amazon/awssdk/benchmark/VirtualThreadBenchmark.java new file mode 100644 index 000000000000..fac829213dd9 --- /dev/null +++ b/test/http-client-benchmarks/src/main/java/software/amazon/awssdk/benchmark/VirtualThreadBenchmark.java @@ -0,0 +1,220 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file 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 software.amazon.awssdk.benchmark; + +import static software.amazon.awssdk.benchmark.apache5.utility.BenchmarkUtilities.isJava21OrHigher; + +import java.io.IOException; +import java.io.PrintStream; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.nio.file.StandardOpenOption; +import java.util.UUID; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.TimeUnit; +import org.openjdk.jmh.annotations.Benchmark; +import org.openjdk.jmh.annotations.Fork; +import org.openjdk.jmh.annotations.Level; +import org.openjdk.jmh.annotations.Param; +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.infra.Blackhole; +import software.amazon.awssdk.auth.credentials.DefaultCredentialsProvider; +import software.amazon.awssdk.benchmark.core.ObjectSize; +import software.amazon.awssdk.benchmark.core.S3BenchmarkHelper; +import software.amazon.awssdk.core.ResponseInputStream; +import software.amazon.awssdk.core.sync.ResponseTransformer; +import software.amazon.awssdk.http.SdkHttpClient; +import software.amazon.awssdk.http.SdkHttpConfigurationOption; +import software.amazon.awssdk.http.apache.ApacheHttpClient; +import software.amazon.awssdk.http.apache5.Apache5HttpClient; +import software.amazon.awssdk.http.crt.AwsCrtHttpClient; +import software.amazon.awssdk.http.urlconnection.UrlConnectionHttpClient; +import software.amazon.awssdk.regions.Region; +import software.amazon.awssdk.services.s3.S3Client; +import software.amazon.awssdk.services.s3.model.GetObjectResponse; +import software.amazon.awssdk.services.s3.model.PutObjectResponse; +import software.amazon.awssdk.utils.AttributeMap; +import software.amazon.awssdk.utils.IoUtils; +import software.amazon.awssdk.utils.JavaSystemSetting; +import software.amazon.awssdk.utils.Logger; + +/** + * Http client benchmark using virtual threads. This class requires Java 21+. + */ +@Fork(jvmArgsAppend = "-Djdk.tracePinnedThreads=full") +@State(Scope.Benchmark) +public class VirtualThreadBenchmark { + static { + // We redirect standard out to a file for the -Djdk.tracePinnedThreads=full option. When virtual threads become pinned, + // the JDK will print out the stacktrace through standard out. However, because JMH runs benchmarks in a forked JVM + // (unless you specify -f 0, which is not recommended by JMH), that output is lost. Redirect standard out to a file so + // that any time a thread is pinned, the stack trace is written to the file instead,which can be inspected after the + // benchmark run. + try { + Path tmp = Paths.get(VirtualThreadBenchmark.class.getSimpleName() + "-stdout-" + UUID.randomUUID() + ".log"); + PrintStream fileOut = new PrintStream(Files.newOutputStream(tmp, StandardOpenOption.APPEND, StandardOpenOption.CREATE)); + System.setOut(fileOut); + } catch (IOException e) { + throw new RuntimeException("Unable to create STDOUT file", e); + } + } + public enum Client { + Apache5, + Apache4, + UrlConnection, + Crt + } + + @Param("50") + private int maxConnections; + + @Param("SMALL") + private ObjectSize objectSize; + + @Param({"Apache5", "UrlConnection", "Crt"}) + // Note: We know pinning happens with Apache4, so don't bother testing it by + // default + private Client client; + + private S3Client s3Client; + private S3BenchmarkHelper benchmark; + private ExecutorService virtualThreadExecutor; + private String putKeyPrefix; + + @Setup(Level.Trial) + public void setup() { + if (!isJava21OrHigher()) { + throw new UnsupportedOperationException( + "Virtual threads require Java 21 or higher. Current version: " + JavaSystemSetting.JAVA_VERSION); + } + + SdkHttpClient.Builder httpClientBuilder; + + switch (client) { + case Apache5: + httpClientBuilder = Apache5HttpClient.builder(); + break; + case Apache4: + httpClientBuilder = ApacheHttpClient.builder(); + break; + case UrlConnection: + httpClientBuilder = UrlConnectionHttpClient.builder(); + break; + case Crt: + httpClientBuilder = AwsCrtHttpClient.builder(); + break; + default: + throw new IllegalArgumentException("Unknown HTTP client: " + client); + } + + s3Client = S3Client.builder() + .region(Region.US_WEST_2) + .credentialsProvider(DefaultCredentialsProvider.create()) + .httpClient(configure(httpClientBuilder)) + .build(); + + String benchmarkName = VirtualThreadBenchmark.class.getSimpleName(); + + benchmark = new S3BenchmarkHelper(benchmarkName, s3Client); + benchmark.setup(); + + virtualThreadExecutor = createVirtualThreadExecutor(); + + putKeyPrefix = benchmarkName + "-"; + } + + private SdkHttpClient configure(SdkHttpClient.Builder builder) { + AttributeMap config = AttributeMap.builder() + .put(SdkHttpConfigurationOption.MAX_CONNECTIONS, maxConnections) + .build(); + + return builder.buildWithDefaults(config); + } + + private ExecutorService createVirtualThreadExecutor() { + try { + // Use reflection to call Executors.newVirtualThreadPerTaskExecutor() + Method method = Executors.class.getMethod("newVirtualThreadPerTaskExecutor"); + return (ExecutorService) method.invoke(null); + } catch (NoSuchMethodException e) { + throw new UnsupportedOperationException( + "Virtual threads are not available in this Java version. " + + "This benchmark requires Java 21 or higher.", e); + } catch (IllegalAccessException | InvocationTargetException e) { + throw new RuntimeException("Failed to create virtual thread executor", e); + } + } + + @Benchmark + public void getObject(Blackhole blackhole) { + safeExecute(() -> { + ResponseInputStream object = s3Client.getObject( + r -> r.bucket(benchmark.bucketName()).key(benchmark.objKey(objectSize)), + ResponseTransformer.toInputStream()); + blackhole.consume(object.response()); + IoUtils.drainInputStream(object); + }); + } + + @Benchmark + public void putObject(Blackhole blackhole) { + String jmhThreadName = Thread.currentThread().getName(); + safeExecute(() -> { + PutObjectResponse response = s3Client.putObject( + r -> r.bucket(benchmark.bucketName()).key(putKeyPrefix + jmhThreadName), + benchmark.requestBody(objectSize)); + blackhole.consume(response); + }); + } + + @TearDown(Level.Trial) + public void tearDown() { + if (virtualThreadExecutor != null) { + virtualThreadExecutor.shutdown(); + try { + if (!virtualThreadExecutor.awaitTermination(30, TimeUnit.SECONDS)) { + virtualThreadExecutor.shutdownNow(); + } + } catch (InterruptedException e) { + virtualThreadExecutor.shutdownNow(); + } + } + + if (benchmark != null) { + benchmark.cleanup(); + } + + if (s3Client != null) { + s3Client.close(); + } + } + + private void safeExecute(Runnable runnable) { + try { + virtualThreadExecutor.submit(runnable).get(); + } catch (InterruptedException | ExecutionException e) { + throw new RuntimeException("Error during execution", e); + } + } +} diff --git a/test/http-client-benchmarks/src/main/java/software/amazon/awssdk/benchmark/apache4/Apache4Benchmark.java b/test/http-client-benchmarks/src/main/java/software/amazon/awssdk/benchmark/apache4/Apache4Benchmark.java index 87dcb9ea84be..d026d2c2f8f1 100644 --- a/test/http-client-benchmarks/src/main/java/software/amazon/awssdk/benchmark/apache4/Apache4Benchmark.java +++ b/test/http-client-benchmarks/src/main/java/software/amazon/awssdk/benchmark/apache4/Apache4Benchmark.java @@ -37,11 +37,16 @@ import org.openjdk.jmh.annotations.Warmup; import org.openjdk.jmh.infra.Blackhole; import software.amazon.awssdk.benchmark.core.CoreBenchmark; -import software.amazon.awssdk.benchmark.core.S3BenchmarkImpl; +import software.amazon.awssdk.benchmark.core.ObjectSize; +import software.amazon.awssdk.benchmark.core.S3BenchmarkHelper; +import software.amazon.awssdk.core.ResponseInputStream; import software.amazon.awssdk.http.SdkHttpClient; import software.amazon.awssdk.http.apache.ApacheHttpClient; import software.amazon.awssdk.regions.Region; import software.amazon.awssdk.services.s3.S3Client; +import software.amazon.awssdk.services.s3.model.GetObjectResponse; +import software.amazon.awssdk.services.s3.model.PutObjectResponse; +import software.amazon.awssdk.utils.IoUtils; import software.amazon.awssdk.utils.Logger; @BenchmarkMode(Mode.Throughput) @@ -56,14 +61,14 @@ public class Apache4Benchmark implements CoreBenchmark { @Param({"50"}) private int maxConnections; - @Param({"5"}) - private int testDataInMB; + @Param("SMALL") + private ObjectSize objectSize; @Param({"10"}) private int threadCount; private S3Client s3Client; - private S3BenchmarkImpl benchmark; + private S3BenchmarkHelper benchmarkHelper; private ExecutorService platformThreadPool; @Setup(Level.Trial) @@ -84,9 +89,8 @@ public void setup() { .httpClient(httpClient) .build(); - // Initialize benchmark implementation - benchmark = new S3BenchmarkImpl(s3Client, new byte[testDataInMB * 1024 * 1024]); - benchmark.setup(); + benchmarkHelper = new S3BenchmarkHelper(Apache4Benchmark.class.getSimpleName(), s3Client); + benchmarkHelper.setup(); // Platform thread pool for multi-threaded tests platformThreadPool = Executors.newFixedThreadPool(threadCount, r -> { @@ -102,13 +106,13 @@ public void setup() { @Benchmark @Override public void simpleGet(Blackhole blackhole) throws Exception { - benchmark.executeGet("medium", blackhole); + executeGet(blackhole); } @Benchmark @Override public void simplePut(Blackhole blackhole) throws Exception { - benchmark.executePut("medium", blackhole); + executePut(blackhole); } @Benchmark @@ -123,6 +127,20 @@ public void multiThreadedPut(Blackhole blackhole) throws Exception { runMultiThreaded(platformThreadPool, threadCount, blackhole, false); } + private void executeGet(Blackhole blackhole) throws Exception { + ResponseInputStream object = s3Client.getObject( + r -> r.bucket(benchmarkHelper.bucketName()).key(benchmarkHelper.objKey(objectSize))); + blackhole.consume(object.response()); + IoUtils.drainInputStream(object); + } + + private void executePut(Blackhole blackhole) { + PutObjectResponse response = s3Client.putObject( + r -> r.bucket(benchmarkHelper.bucketName()).key("Apache4Benchmark-" + Thread.currentThread().getName()), + benchmarkHelper.requestBody(objectSize)); + blackhole.consume(response); + } + protected void runMultiThreaded(ExecutorService executor, int threads, Blackhole blackhole, boolean isGet) throws Exception { List> futures = new ArrayList<>(threads); @@ -131,9 +149,9 @@ protected void runMultiThreaded(ExecutorService executor, int threads, futures.add(executor.submit(() -> { try { if (isGet) { - benchmark.executeGet("medium", blackhole); + executeGet(blackhole); } else { - benchmark.executePut("medium", blackhole); + executePut(blackhole); } } catch (Exception e) { throw new RuntimeException("Operation failed", e); @@ -162,8 +180,8 @@ public void tearDown() { } } - if (benchmark != null) { - benchmark.cleanup(); + if (benchmarkHelper != null) { + benchmarkHelper.cleanup(); } if (s3Client != null) { diff --git a/test/http-client-benchmarks/src/main/java/software/amazon/awssdk/benchmark/apache5/Apache5Benchmark.java b/test/http-client-benchmarks/src/main/java/software/amazon/awssdk/benchmark/apache5/Apache5Benchmark.java index f80569e86d9f..331e9488beba 100644 --- a/test/http-client-benchmarks/src/main/java/software/amazon/awssdk/benchmark/apache5/Apache5Benchmark.java +++ b/test/http-client-benchmarks/src/main/java/software/amazon/awssdk/benchmark/apache5/Apache5Benchmark.java @@ -37,12 +37,18 @@ import org.openjdk.jmh.annotations.Warmup; import org.openjdk.jmh.infra.Blackhole; import software.amazon.awssdk.auth.credentials.DefaultCredentialsProvider; +import software.amazon.awssdk.benchmark.apache4.Apache4Benchmark; import software.amazon.awssdk.benchmark.core.CoreBenchmark; -import software.amazon.awssdk.benchmark.core.S3BenchmarkImpl; +import software.amazon.awssdk.benchmark.core.ObjectSize; +import software.amazon.awssdk.benchmark.core.S3BenchmarkHelper; +import software.amazon.awssdk.core.ResponseInputStream; import software.amazon.awssdk.http.SdkHttpClient; import software.amazon.awssdk.http.apache5.Apache5HttpClient; import software.amazon.awssdk.regions.Region; import software.amazon.awssdk.services.s3.S3Client; +import software.amazon.awssdk.services.s3.model.GetObjectResponse; +import software.amazon.awssdk.services.s3.model.PutObjectResponse; +import software.amazon.awssdk.utils.IoUtils; import software.amazon.awssdk.utils.Logger; @BenchmarkMode(Mode.Throughput) @@ -57,17 +63,14 @@ public class Apache5Benchmark implements CoreBenchmark { @Param({"50"}) private int maxConnections; - @Param({"5"}) - private int testDataInMB; + @Param("SMALL") + private ObjectSize objectSize; @Param({"10"}) private int threadCount; - @Param({"platform"}) - private String executorType; - private S3Client s3Client; - private S3BenchmarkImpl benchmark; + private S3BenchmarkHelper benchmarkHelper; private ExecutorService executorService; @Setup(Level.Trial) @@ -89,9 +92,8 @@ public void setup() { .httpClient(httpClient) .build(); - // Initialize benchmark implementation - benchmark = new S3BenchmarkImpl(s3Client, new byte[this.testDataInMB * 1024 * 1024]); - benchmark.setup(); + benchmarkHelper = new S3BenchmarkHelper(Apache4Benchmark.class.getSimpleName(), s3Client); + benchmarkHelper.setup(); // Always use platform threads executorService = Executors.newFixedThreadPool(threadCount, r -> { @@ -106,15 +108,19 @@ public void setup() { } @Benchmark - @Override - public void simpleGet(Blackhole blackhole) throws Exception { - benchmark.executeGet("medium", blackhole); + public void simpleGet(Blackhole blackhole) { + ResponseInputStream object = s3Client.getObject( + r -> r.bucket(benchmarkHelper.bucketName()).key(benchmarkHelper.objKey(objectSize))); + blackhole.consume(object.response()); + IoUtils.drainInputStream(object); } @Benchmark - @Override - public void simplePut(Blackhole blackhole) throws Exception { - benchmark.executePut("medium", blackhole); + public void simplePut(Blackhole blackhole) { + PutObjectResponse response = s3Client.putObject( + r -> r.bucket(benchmarkHelper.bucketName()).key("Apache4Benchmark-" + Thread.currentThread().getName()), + benchmarkHelper.requestBody(objectSize)); + blackhole.consume(response); } @Benchmark @@ -125,7 +131,7 @@ public void multiThreadedGet(Blackhole blackhole) throws Exception { for (int i = 0; i < threadCount; i++) { futures.add(executorService.submit(() -> { try { - benchmark.executeGet("medium", blackhole); + executeGet(blackhole); } catch (Exception e) { throw new RuntimeException("GET operation failed", e); } @@ -146,7 +152,7 @@ public void multiThreadedPut(Blackhole blackhole) throws Exception { for (int i = 0; i < threadCount; i++) { futures.add(executorService.submit(() -> { try { - benchmark.executePut("medium", blackhole); + executePut(blackhole); } catch (Exception e) { throw new RuntimeException("PUT operation failed", e); } @@ -159,6 +165,20 @@ public void multiThreadedPut(Blackhole blackhole) throws Exception { } } + private void executeGet(Blackhole blackhole) { + ResponseInputStream object = s3Client.getObject( + r -> r.bucket(benchmarkHelper.bucketName()).key(benchmarkHelper.objKey(objectSize))); + blackhole.consume(object.response()); + IoUtils.drainInputStream(object); + } + + private void executePut(Blackhole blackhole) { + PutObjectResponse response = s3Client.putObject( + r -> r.bucket(benchmarkHelper.bucketName()).key("Apache4Benchmark-" + Thread.currentThread().getName()), + benchmarkHelper.requestBody(objectSize)); + blackhole.consume(response); + } + @TearDown(Level.Trial) public void tearDown() { logger.info(() -> "Tearing down Apache5 benchmark"); @@ -174,8 +194,8 @@ public void tearDown() { } } - if (benchmark != null) { - benchmark.cleanup(); + if (benchmarkHelper != null) { + benchmarkHelper.cleanup(); } if (s3Client != null) { diff --git a/test/http-client-benchmarks/src/main/java/software/amazon/awssdk/benchmark/apache5/Apache5VirtualBenchmark.java b/test/http-client-benchmarks/src/main/java/software/amazon/awssdk/benchmark/apache5/Apache5VirtualBenchmark.java deleted file mode 100644 index e0c228df3ca4..000000000000 --- a/test/http-client-benchmarks/src/main/java/software/amazon/awssdk/benchmark/apache5/Apache5VirtualBenchmark.java +++ /dev/null @@ -1,215 +0,0 @@ -/* - * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at - * - * http://aws.amazon.com/apache2.0 - * - * or in the "license" file accompanying this file. This file 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 software.amazon.awssdk.benchmark.apache5; - -import static software.amazon.awssdk.benchmark.apache5.utility.BenchmarkUtilities.isJava21OrHigher; - -import java.lang.reflect.InvocationTargetException; -import java.lang.reflect.Method; -import java.time.Duration; -import java.util.ArrayList; -import java.util.List; -import java.util.concurrent.ExecutorService; -import java.util.concurrent.Executors; -import java.util.concurrent.Future; -import java.util.concurrent.TimeUnit; -import org.openjdk.jmh.annotations.Benchmark; -import org.openjdk.jmh.annotations.BenchmarkMode; -import org.openjdk.jmh.annotations.Fork; -import org.openjdk.jmh.annotations.Level; -import org.openjdk.jmh.annotations.Measurement; -import org.openjdk.jmh.annotations.Mode; -import org.openjdk.jmh.annotations.OutputTimeUnit; -import org.openjdk.jmh.annotations.Param; -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; -import org.openjdk.jmh.infra.Blackhole; -import software.amazon.awssdk.auth.credentials.DefaultCredentialsProvider; -import software.amazon.awssdk.benchmark.core.CoreBenchmark; -import software.amazon.awssdk.benchmark.core.S3BenchmarkImpl; -import software.amazon.awssdk.http.SdkHttpClient; -import software.amazon.awssdk.http.apache5.Apache5HttpClient; -import software.amazon.awssdk.regions.Region; -import software.amazon.awssdk.services.s3.S3Client; -import software.amazon.awssdk.utils.JavaSystemSetting; -import software.amazon.awssdk.utils.Logger; - -/** - * Apache5 benchmark using virtual threads. This class requires Java 21+. - */ -@BenchmarkMode(Mode.Throughput) -@OutputTimeUnit(TimeUnit.SECONDS) -@State(Scope.Benchmark) -@Fork(value = 1, jvmArgs = {"-Xms2G", "-Xmx2G"}) -@Warmup(iterations = 3, time = 15, timeUnit = TimeUnit.SECONDS) -@Measurement(iterations = 5, time = 10, timeUnit = TimeUnit.SECONDS) -public class Apache5VirtualBenchmark implements CoreBenchmark { - - private static final Logger logger = Logger.loggerFor(Apache5VirtualBenchmark.class); - - @Param({"50"}) - private int maxConnections; - - @Param({"5"}) - private int testDataInMB; - - @Param({"10"}) - private int threadCount; - - private S3Client s3Client; - private S3BenchmarkImpl benchmark; - private ExecutorService executorService; - - - - @Setup(Level.Trial) - public void setup() { - // Verify Java version - String version = JavaSystemSetting.JAVA_VERSION.getStringValueOrThrow(); - // Update logging call to use Supplier pattern - logger.info(() -> "Running on Java version: " + version); - - if (!isJava21OrHigher()) { - throw new UnsupportedOperationException( - "Virtual threads require Java 21 or higher. Current version: " + version); - } - - // Update logging call to use Supplier pattern - logger.info(() -> "Setting up Apache5 virtual threads benchmark with maxConnections=" + maxConnections); - - // Apache 5 HTTP client - SdkHttpClient httpClient = Apache5HttpClient.builder() - .connectionTimeout(Duration.ofSeconds(10)) - .socketTimeout(Duration.ofSeconds(30)) - .connectionAcquisitionTimeout(Duration.ofSeconds(10)) - .maxConnections(maxConnections) - .build(); - - // S3 client - s3Client = S3Client.builder() - .region(Region.US_WEST_2) - .credentialsProvider(DefaultCredentialsProvider.create()) - .httpClient(httpClient) - .build(); - - // Initialize benchmark implementation - benchmark = new S3BenchmarkImpl(s3Client, new byte[this.testDataInMB * 1024 * 1024]); - benchmark.setup(); - - // Create virtual thread executor - executorService = createVirtualThreadExecutor(); - // Update logging call to use Supplier pattern - logger.info(() -> "Using virtual thread executor"); - - // Update logging call to use Supplier pattern - logger.info(() -> "Apache5 virtual threads benchmark setup complete"); - } - - private ExecutorService createVirtualThreadExecutor() { - try { - // Use reflection to call Executors.newVirtualThreadPerTaskExecutor() - Method method = Executors.class.getMethod("newVirtualThreadPerTaskExecutor"); - return (ExecutorService) method.invoke(null); - } catch (NoSuchMethodException e) { - throw new UnsupportedOperationException( - "Virtual threads are not available in this Java version. " + - "This benchmark requires Java 21 or higher.", e); - } catch (IllegalAccessException | InvocationTargetException e) { - throw new RuntimeException("Failed to create virtual thread executor", e); - } - } - - @Benchmark - @Override - public void simpleGet(Blackhole blackhole) throws Exception { - benchmark.executeGet("medium", blackhole); - } - - @Benchmark - @Override - public void simplePut(Blackhole blackhole) throws Exception { - benchmark.executePut("medium", blackhole); - } - - @Benchmark - @Override - public void multiThreadedGet(Blackhole blackhole) throws Exception { - List> futures = new ArrayList<>(threadCount); - - for (int i = 0; i < threadCount; i++) { - futures.add(executorService.submit(() -> { - try { - benchmark.executeGet("medium", blackhole); - } catch (Exception e) { - throw new RuntimeException("GET operation failed", e); - } - })); - } - - // Wait for all operations to complete - for (Future future : futures) { - future.get(); - } - } - - @Benchmark - @Override - public void multiThreadedPut(Blackhole blackhole) throws Exception { - List> futures = new ArrayList<>(threadCount); - - for (int i = 0; i < threadCount; i++) { - futures.add(executorService.submit(() -> { - try { - benchmark.executePut("medium", blackhole); - } catch (Exception e) { - throw new RuntimeException("PUT operation failed", e); - } - })); - } - - // Wait for all operations to complete - for (Future future : futures) { - future.get(); - } - } - - @TearDown(Level.Trial) - public void tearDown() { - logger.info(() -> "Tearing down Apache5 virtual threads benchmark"); - - if (executorService != null) { - executorService.shutdown(); - try { - if (!executorService.awaitTermination(30, TimeUnit.SECONDS)) { - executorService.shutdownNow(); - } - } catch (InterruptedException e) { - executorService.shutdownNow(); - } - } - - if (benchmark != null) { - benchmark.cleanup(); - } - - if (s3Client != null) { - s3Client.close(); - } - } -} diff --git a/test/http-client-benchmarks/src/main/java/software/amazon/awssdk/benchmark/core/AsyncS3Wrapper.java b/test/http-client-benchmarks/src/main/java/software/amazon/awssdk/benchmark/core/AsyncS3Wrapper.java new file mode 100644 index 000000000000..71dd6c3c60eb --- /dev/null +++ b/test/http-client-benchmarks/src/main/java/software/amazon/awssdk/benchmark/core/AsyncS3Wrapper.java @@ -0,0 +1,64 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file 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 software.amazon.awssdk.benchmark.core; + +import java.nio.file.Path; +import software.amazon.awssdk.core.async.AsyncRequestBody; +import software.amazon.awssdk.services.s3.S3AsyncClient; +import software.amazon.awssdk.services.s3.model.CreateBucketRequest; +import software.amazon.awssdk.services.s3.model.DeleteBucketRequest; +import software.amazon.awssdk.services.s3.model.DeleteObjectRequest; +import software.amazon.awssdk.services.s3.model.ListObjectsV2Request; +import software.amazon.awssdk.services.s3.model.ListObjectsV2Response; +import software.amazon.awssdk.services.s3.model.PutObjectRequest; + +public class AsyncS3Wrapper implements S3Wrapper { + private final S3AsyncClient s3; + + public AsyncS3Wrapper(S3AsyncClient s3) { + this.s3 = s3; + } + + @Override + public void createBucket(CreateBucketRequest request) { + s3.createBucket(request).join(); + } + + @Override + public void putObject(PutObjectRequest request, Path file) { + s3.putObject(request, AsyncRequestBody.fromFile(file)).join(); + } + + @Override + public ListObjectsV2Response listObjectsV2(ListObjectsV2Request request) { + return s3.listObjectsV2(request).join(); + } + + @Override + public void deleteObject(DeleteObjectRequest request) { + s3.deleteObject(request).join(); + } + + @Override + public void deleteBucket(DeleteBucketRequest request) { + s3.deleteBucket(request).join(); + } + + @Override + public void waitUntilBucketExists(String bucketName) { + s3.waiter().waitUntilBucketExists(r -> r.bucket(bucketName)).join(); + } +} diff --git a/test/http-client-benchmarks/src/main/java/software/amazon/awssdk/benchmark/core/ObjectSize.java b/test/http-client-benchmarks/src/main/java/software/amazon/awssdk/benchmark/core/ObjectSize.java new file mode 100644 index 000000000000..6e38185b7f7b --- /dev/null +++ b/test/http-client-benchmarks/src/main/java/software/amazon/awssdk/benchmark/core/ObjectSize.java @@ -0,0 +1,33 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file 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 software.amazon.awssdk.benchmark.core; + +public enum ObjectSize { + + SMALL(1024 * 1024), + MEDIUM(8 * 1024 * 1024), + LARGE(64 * 1024 * 1024),; + + private final long sizeInBytes; + + private ObjectSize(long sizeInBytes) { + this.sizeInBytes = sizeInBytes; + } + + public long sizeInBytes() { + return sizeInBytes; + } +} diff --git a/test/http-client-benchmarks/src/main/java/software/amazon/awssdk/benchmark/core/S3BenchmarkHelper.java b/test/http-client-benchmarks/src/main/java/software/amazon/awssdk/benchmark/core/S3BenchmarkHelper.java new file mode 100644 index 000000000000..3515a49ed23c --- /dev/null +++ b/test/http-client-benchmarks/src/main/java/software/amazon/awssdk/benchmark/core/S3BenchmarkHelper.java @@ -0,0 +1,145 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file 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 software.amazon.awssdk.benchmark.core; + +import java.io.OutputStream; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.EnumMap; +import java.util.Locale; +import java.util.Random; +import software.amazon.awssdk.core.async.AsyncRequestBody; +import software.amazon.awssdk.core.sync.RequestBody; +import software.amazon.awssdk.services.s3.S3AsyncClient; +import software.amazon.awssdk.services.s3.S3Client; +import software.amazon.awssdk.services.s3.model.CreateBucketRequest; +import software.amazon.awssdk.services.s3.model.DeleteBucketRequest; +import software.amazon.awssdk.services.s3.model.DeleteObjectRequest; +import software.amazon.awssdk.services.s3.model.ListObjectsV2Request; +import software.amazon.awssdk.services.s3.model.ListObjectsV2Response; +import software.amazon.awssdk.services.s3.model.PutObjectRequest; +import software.amazon.awssdk.services.s3.model.S3Object; +import software.amazon.awssdk.utils.Logger; + +/** + * Shared S3 operations implementation used by all benchmark classes. + */ +public class S3BenchmarkHelper { + private static final Logger logger = Logger.loggerFor(S3BenchmarkHelper.class); + private static final String TEST_KEY_PREFIX = "benchmark-object-"; + + private final S3Wrapper s3Wrapper; + private final String name; + private String bucketName; + + private final EnumMap testFiles = new EnumMap<>(ObjectSize.class); + + public S3BenchmarkHelper(String name, S3Client s3Client) { + this.name = name; + this.s3Wrapper = new SyncS3Wrapper(s3Client); + } + + public S3BenchmarkHelper(String name, S3AsyncClient s3AsyncClient) { + this.name = name; + this.s3Wrapper = new AsyncS3Wrapper(s3AsyncClient); + } + + public void setup() { + try { + this.bucketName = name.toLowerCase(Locale.ENGLISH)+ "-bucket-" + System.currentTimeMillis(); + // Create bucket + s3Wrapper.createBucket(CreateBucketRequest.builder().bucket(bucketName).build()); + + // Wait for bucket to be ready + s3Wrapper.waitUntilBucketExists(bucketName); + + byte[] testData = new byte[1024 * 1024]; + new Random().nextBytes(testData); + + for (ObjectSize size : ObjectSize.values()) { + Path p = Files.createTempFile(name + "-test-file", null); + OutputStream os = Files.newOutputStream(p); + + long chunks = size.sizeInBytes() / testData.length; + for (long i = 0; i < chunks; i++) { + os.write(testData, 0, testData.length); + } + + os.close(); + testFiles.put(size, p); + + s3Wrapper.putObject(PutObjectRequest.builder().bucket(bucketName).key(objKey(size)).build(), p); + } + } catch (Exception e) { + logger.error(() -> "Setup failed: " + e.getMessage(), e); + throw new RuntimeException("Failed to setup S3 benchmark", e); + } + } + + public String bucketName() { + return bucketName; + } + + public String objKey(ObjectSize objectSize) { + return TEST_KEY_PREFIX + objectSize.name(); + } + + public RequestBody requestBody(ObjectSize objectSize) { + Path p = testFiles.get(objectSize); + if (p == null) { + throw new RuntimeException("Test file not found: " + objectSize.name()); + } + + return RequestBody.fromFile(p); + } + + public AsyncRequestBody asyncRequestBody(ObjectSize objectSize) { + Path p = testFiles.get(objectSize); + if (p == null) { + throw new RuntimeException("Test file not found: " + objectSize.name()); + } + return AsyncRequestBody.fromFile(p); + } + + public void cleanup() { + try { + // Delete all objects (handle pagination) + ListObjectsV2Request.Builder listRequestBuilder = ListObjectsV2Request.builder() + .bucket(bucketName); + String continuationToken = null; + do { + if (continuationToken != null) { + listRequestBuilder.continuationToken(continuationToken); + } + ListObjectsV2Response listResponse = s3Wrapper.listObjectsV2(listRequestBuilder.build()); + for (S3Object object : listResponse.contents()) { + s3Wrapper.deleteObject(DeleteObjectRequest.builder() + .bucket(bucketName) + .key(object.key()) + .build()); + } + continuationToken = listResponse.nextContinuationToken(); + } while (continuationToken != null); + // Delete bucket + s3Wrapper.deleteBucket(DeleteBucketRequest.builder() + .bucket(bucketName) + .build()); + logger.info(() -> "Cleaned up bucket: " + bucketName); + } catch (Exception e) { + logger.warn(() -> "Cleanup failed: " + e.getMessage(), e); + } + } +} diff --git a/test/http-client-benchmarks/src/main/java/software/amazon/awssdk/benchmark/core/S3BenchmarkImpl.java b/test/http-client-benchmarks/src/main/java/software/amazon/awssdk/benchmark/core/S3BenchmarkImpl.java deleted file mode 100644 index 9eed9e94ad60..000000000000 --- a/test/http-client-benchmarks/src/main/java/software/amazon/awssdk/benchmark/core/S3BenchmarkImpl.java +++ /dev/null @@ -1,174 +0,0 @@ -/* - * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at - * - * http://aws.amazon.com/apache2.0 - * - * or in the "license" file accompanying this file. This file 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 software.amazon.awssdk.benchmark.core; - -import java.io.ByteArrayOutputStream; -import java.io.IOException; -import java.io.InputStream; -import java.util.UUID; -import java.util.concurrent.ThreadLocalRandom; -import org.openjdk.jmh.infra.Blackhole; -import software.amazon.awssdk.core.ResponseInputStream; -import software.amazon.awssdk.core.sync.RequestBody; -import software.amazon.awssdk.services.s3.S3Client; -import software.amazon.awssdk.services.s3.model.CreateBucketRequest; -import software.amazon.awssdk.services.s3.model.DeleteBucketRequest; -import software.amazon.awssdk.services.s3.model.DeleteObjectRequest; -import software.amazon.awssdk.services.s3.model.GetObjectRequest; -import software.amazon.awssdk.services.s3.model.GetObjectResponse; -import software.amazon.awssdk.services.s3.model.HeadBucketRequest; -import software.amazon.awssdk.services.s3.model.ListObjectsV2Request; -import software.amazon.awssdk.services.s3.model.ListObjectsV2Response; -import software.amazon.awssdk.services.s3.model.PutObjectRequest; -import software.amazon.awssdk.services.s3.model.PutObjectResponse; -import software.amazon.awssdk.services.s3.model.S3Object; -import software.amazon.awssdk.utils.Logger; - -/** - * Shared S3 operations implementation used by all benchmark classes. - */ -public class S3BenchmarkImpl { - private static final Logger logger = Logger.loggerFor(S3BenchmarkImpl.class); - private static final String TEST_KEY_PREFIX = "benchmark-object-"; - private static final int OBJECT_COUNT = 100; - - private final S3Client s3Client; - private final String bucketName; - private final byte[] testData; - - public S3BenchmarkImpl(S3Client s3Client, byte[] testData) { - this.s3Client = s3Client; - this.bucketName = "benchmark-bucket-" + UUID.randomUUID().toString().substring(0, 8); - this.testData = testData; - ThreadLocalRandom.current().nextBytes(testData); - } - - public void setup() { - try { - // Create bucket - s3Client.createBucket(CreateBucketRequest.builder() - .bucket(bucketName) - .build()); - - logger.info(() -> "Created bucket: " + bucketName); - - // Wait for bucket to be ready - s3Client.waiter().waitUntilBucketExists(HeadBucketRequest.builder() - .bucket(bucketName) - .build()); - - // Upload test objects - for (int i = 0; i < OBJECT_COUNT; i++) { - String key = TEST_KEY_PREFIX + i; - s3Client.putObject( - PutObjectRequest.builder() - .bucket(bucketName) - .key(key) - .build(), - RequestBody.fromBytes(testData) - ); - } - - logger.info(() -> "Uploaded " + OBJECT_COUNT + " test objects"); - - } catch (Exception e) { - logger.error(() -> "Setup failed: " + e.getMessage(), e); - throw new RuntimeException("Failed to setup S3 benchmark", e); - } - } - - public void executeGet(String size, Blackhole blackhole) throws IOException { - // Random key to avoid caching effects - String key = TEST_KEY_PREFIX + ThreadLocalRandom.current().nextInt(OBJECT_COUNT); - - GetObjectRequest request = GetObjectRequest.builder() - .bucket(bucketName) - .key(key) - .build(); - - ResponseInputStream response = null; - try { - response = s3Client.getObject(request); - byte[] data = readAllBytes(response); - blackhole.consume(data); - blackhole.consume(response.response()); - } finally { - if (response != null) { - response.close(); - } - } - } - - public void executePut(String size, Blackhole blackhole) { - String key = "put-object-" + UUID.randomUUID(); - - PutObjectRequest request = PutObjectRequest.builder() - .bucket(bucketName) - .key(key) - .build(); - - PutObjectResponse response = s3Client.putObject(request, - RequestBody.fromBytes(testData)); - - blackhole.consume(response); - - // Clean up immediately to avoid accumulating objects - s3Client.deleteObject(DeleteObjectRequest.builder() - .bucket(bucketName) - .key(key) - .build()); - } - - public void cleanup() { - try { - // Delete all objects (handle pagination) - ListObjectsV2Request.Builder listRequestBuilder = ListObjectsV2Request.builder() - .bucket(bucketName); - String continuationToken = null; - do { - if (continuationToken != null) { - listRequestBuilder.continuationToken(continuationToken); - } - ListObjectsV2Response listResponse = s3Client.listObjectsV2(listRequestBuilder.build()); - for (S3Object object : listResponse.contents()) { - s3Client.deleteObject(DeleteObjectRequest.builder() - .bucket(bucketName) - .key(object.key()) - .build()); - } - continuationToken = listResponse.nextContinuationToken(); - } while (continuationToken != null); - // Delete bucket - s3Client.deleteBucket(DeleteBucketRequest.builder() - .bucket(bucketName) - .build()); - logger.info(() -> "Cleaned up bucket: " + bucketName); - - } catch (Exception e) { - logger.warn(() -> "Cleanup failed: " + e.getMessage(), e); - } - } - - private byte[] readAllBytes(InputStream inputStream) throws IOException { - ByteArrayOutputStream buffer = new ByteArrayOutputStream(); - byte[] data = new byte[8192]; - int nRead; - while ((nRead = inputStream.read(data, 0, data.length)) != -1) { - buffer.write(data, 0, nRead); - } - return buffer.toByteArray(); - } -} diff --git a/test/http-client-benchmarks/src/main/java/software/amazon/awssdk/benchmark/core/S3Wrapper.java b/test/http-client-benchmarks/src/main/java/software/amazon/awssdk/benchmark/core/S3Wrapper.java new file mode 100644 index 000000000000..eb2b26d27420 --- /dev/null +++ b/test/http-client-benchmarks/src/main/java/software/amazon/awssdk/benchmark/core/S3Wrapper.java @@ -0,0 +1,36 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file 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 software.amazon.awssdk.benchmark.core; + +import java.nio.file.Path; +import software.amazon.awssdk.services.s3.model.CreateBucketRequest; +import software.amazon.awssdk.services.s3.model.DeleteBucketRequest; +import software.amazon.awssdk.services.s3.model.DeleteObjectRequest; +import software.amazon.awssdk.services.s3.model.ListObjectsV2Request; +import software.amazon.awssdk.services.s3.model.ListObjectsV2Response; +import software.amazon.awssdk.services.s3.model.PutObjectRequest; + +/** + * Simple wrapper for S3 APIs so we can implement it using sync or async S3 clients. + */ +public interface S3Wrapper { + void createBucket(CreateBucketRequest request); + void putObject(PutObjectRequest request, Path file); + ListObjectsV2Response listObjectsV2(ListObjectsV2Request request); + void deleteObject(DeleteObjectRequest request); + void deleteBucket(DeleteBucketRequest request); + void waitUntilBucketExists(String bucketName); +} diff --git a/test/http-client-benchmarks/src/main/java/software/amazon/awssdk/benchmark/core/SyncS3Wrapper.java b/test/http-client-benchmarks/src/main/java/software/amazon/awssdk/benchmark/core/SyncS3Wrapper.java new file mode 100644 index 000000000000..50484e5f76d8 --- /dev/null +++ b/test/http-client-benchmarks/src/main/java/software/amazon/awssdk/benchmark/core/SyncS3Wrapper.java @@ -0,0 +1,64 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file 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 software.amazon.awssdk.benchmark.core; + +import java.nio.file.Path; +import software.amazon.awssdk.core.sync.RequestBody; +import software.amazon.awssdk.services.s3.S3Client; +import software.amazon.awssdk.services.s3.model.CreateBucketRequest; +import software.amazon.awssdk.services.s3.model.DeleteBucketRequest; +import software.amazon.awssdk.services.s3.model.DeleteObjectRequest; +import software.amazon.awssdk.services.s3.model.ListObjectsV2Request; +import software.amazon.awssdk.services.s3.model.ListObjectsV2Response; +import software.amazon.awssdk.services.s3.model.PutObjectRequest; + +public class SyncS3Wrapper implements S3Wrapper { + private final S3Client s3; + + public SyncS3Wrapper(S3Client s3) { + this.s3 = s3; + } + + @Override + public void createBucket(CreateBucketRequest request) { + s3.createBucket(request); + } + + @Override + public void putObject(PutObjectRequest request, Path file) { + s3.putObject(request, RequestBody.fromFile(file)); + } + + @Override + public ListObjectsV2Response listObjectsV2(ListObjectsV2Request request) { + return s3.listObjectsV2(request); + } + + @Override + public void deleteObject(DeleteObjectRequest request) { + s3.deleteObject(request); + } + + @Override + public void deleteBucket(DeleteBucketRequest request) { + s3.deleteBucket(request); + } + + @Override + public void waitUntilBucketExists(String bucketName) { + s3.waiter().waitUntilBucketExists(r -> r.bucket(bucketName)); + } +} From a3209f1f25dfd5c3a52f881ab5f6df89fa687ea0 Mon Sep 17 00:00:00 2001 From: Dongie Agnir Date: Tue, 2 Dec 2025 10:35:37 -0800 Subject: [PATCH 2/7] Checkstyle fixes --- .../amazon/awssdk/benchmark/AsyncVirtualThreadBenchmark.java | 4 +++- .../amazon/awssdk/benchmark/VirtualThreadBenchmark.java | 5 +++-- .../software/amazon/awssdk/benchmark/core/ObjectSize.java | 2 +- .../amazon/awssdk/benchmark/core/S3BenchmarkHelper.java | 2 +- .../software/amazon/awssdk/benchmark/core/S3Wrapper.java | 5 +++++ 5 files changed, 13 insertions(+), 5 deletions(-) diff --git a/test/http-client-benchmarks/src/main/java/software/amazon/awssdk/benchmark/AsyncVirtualThreadBenchmark.java b/test/http-client-benchmarks/src/main/java/software/amazon/awssdk/benchmark/AsyncVirtualThreadBenchmark.java index 04b8ca1ce731..827aa547144a 100644 --- a/test/http-client-benchmarks/src/main/java/software/amazon/awssdk/benchmark/AsyncVirtualThreadBenchmark.java +++ b/test/http-client-benchmarks/src/main/java/software/amazon/awssdk/benchmark/AsyncVirtualThreadBenchmark.java @@ -70,12 +70,14 @@ public class AsyncVirtualThreadBenchmark { static { try { Path tmp = Paths.get(AsyncVirtualThreadBenchmark.class.getSimpleName() + "-stdout-" + UUID.randomUUID() + ".log"); - PrintStream fileOut = new PrintStream(Files.newOutputStream(tmp, StandardOpenOption.APPEND, StandardOpenOption.CREATE)); + PrintStream fileOut = new PrintStream( + Files.newOutputStream(tmp, StandardOpenOption.APPEND, StandardOpenOption.CREATE)); System.setOut(fileOut); } catch (IOException e) { throw new RuntimeException("Unable to create STDOUT file", e); } } + public enum Client { Netty, Crt diff --git a/test/http-client-benchmarks/src/main/java/software/amazon/awssdk/benchmark/VirtualThreadBenchmark.java b/test/http-client-benchmarks/src/main/java/software/amazon/awssdk/benchmark/VirtualThreadBenchmark.java index fac829213dd9..df7249b378b2 100644 --- a/test/http-client-benchmarks/src/main/java/software/amazon/awssdk/benchmark/VirtualThreadBenchmark.java +++ b/test/http-client-benchmarks/src/main/java/software/amazon/awssdk/benchmark/VirtualThreadBenchmark.java @@ -57,7 +57,6 @@ import software.amazon.awssdk.utils.AttributeMap; import software.amazon.awssdk.utils.IoUtils; import software.amazon.awssdk.utils.JavaSystemSetting; -import software.amazon.awssdk.utils.Logger; /** * Http client benchmark using virtual threads. This class requires Java 21+. @@ -73,12 +72,14 @@ public class VirtualThreadBenchmark { // benchmark run. try { Path tmp = Paths.get(VirtualThreadBenchmark.class.getSimpleName() + "-stdout-" + UUID.randomUUID() + ".log"); - PrintStream fileOut = new PrintStream(Files.newOutputStream(tmp, StandardOpenOption.APPEND, StandardOpenOption.CREATE)); + PrintStream fileOut = new PrintStream( + Files.newOutputStream(tmp, StandardOpenOption.APPEND, StandardOpenOption.CREATE)); System.setOut(fileOut); } catch (IOException e) { throw new RuntimeException("Unable to create STDOUT file", e); } } + public enum Client { Apache5, Apache4, diff --git a/test/http-client-benchmarks/src/main/java/software/amazon/awssdk/benchmark/core/ObjectSize.java b/test/http-client-benchmarks/src/main/java/software/amazon/awssdk/benchmark/core/ObjectSize.java index 6e38185b7f7b..e2187d5bdc82 100644 --- a/test/http-client-benchmarks/src/main/java/software/amazon/awssdk/benchmark/core/ObjectSize.java +++ b/test/http-client-benchmarks/src/main/java/software/amazon/awssdk/benchmark/core/ObjectSize.java @@ -23,7 +23,7 @@ public enum ObjectSize { private final long sizeInBytes; - private ObjectSize(long sizeInBytes) { + ObjectSize(long sizeInBytes) { this.sizeInBytes = sizeInBytes; } diff --git a/test/http-client-benchmarks/src/main/java/software/amazon/awssdk/benchmark/core/S3BenchmarkHelper.java b/test/http-client-benchmarks/src/main/java/software/amazon/awssdk/benchmark/core/S3BenchmarkHelper.java index 3515a49ed23c..354080990673 100644 --- a/test/http-client-benchmarks/src/main/java/software/amazon/awssdk/benchmark/core/S3BenchmarkHelper.java +++ b/test/http-client-benchmarks/src/main/java/software/amazon/awssdk/benchmark/core/S3BenchmarkHelper.java @@ -59,7 +59,7 @@ public S3BenchmarkHelper(String name, S3AsyncClient s3AsyncClient) { public void setup() { try { - this.bucketName = name.toLowerCase(Locale.ENGLISH)+ "-bucket-" + System.currentTimeMillis(); + this.bucketName = name.toLowerCase(Locale.ENGLISH) + "-bucket-" + System.currentTimeMillis(); // Create bucket s3Wrapper.createBucket(CreateBucketRequest.builder().bucket(bucketName).build()); diff --git a/test/http-client-benchmarks/src/main/java/software/amazon/awssdk/benchmark/core/S3Wrapper.java b/test/http-client-benchmarks/src/main/java/software/amazon/awssdk/benchmark/core/S3Wrapper.java index eb2b26d27420..599e93e15094 100644 --- a/test/http-client-benchmarks/src/main/java/software/amazon/awssdk/benchmark/core/S3Wrapper.java +++ b/test/http-client-benchmarks/src/main/java/software/amazon/awssdk/benchmark/core/S3Wrapper.java @@ -28,9 +28,14 @@ */ public interface S3Wrapper { void createBucket(CreateBucketRequest request); + void putObject(PutObjectRequest request, Path file); + ListObjectsV2Response listObjectsV2(ListObjectsV2Request request); + void deleteObject(DeleteObjectRequest request); + void deleteBucket(DeleteBucketRequest request); + void waitUntilBucketExists(String bucketName); } From 658759905a55cb6fcf4a5aba52a284c660458cec Mon Sep 17 00:00:00 2001 From: Dongie Agnir Date: Tue, 2 Dec 2025 13:41:58 -0800 Subject: [PATCH 3/7] Sonar fixes --- .../benchmark/VirtualThreadBenchmark.java | 34 +++++++++---------- .../benchmark/apache4/Apache4Benchmark.java | 2 +- .../benchmark/apache5/Apache5Benchmark.java | 10 ++---- .../awssdk/benchmark/core/ObjectSize.java | 6 ++-- 4 files changed, 22 insertions(+), 30 deletions(-) diff --git a/test/http-client-benchmarks/src/main/java/software/amazon/awssdk/benchmark/VirtualThreadBenchmark.java b/test/http-client-benchmarks/src/main/java/software/amazon/awssdk/benchmark/VirtualThreadBenchmark.java index df7249b378b2..e1720f4223e4 100644 --- a/test/http-client-benchmarks/src/main/java/software/amazon/awssdk/benchmark/VirtualThreadBenchmark.java +++ b/test/http-client-benchmarks/src/main/java/software/amazon/awssdk/benchmark/VirtualThreadBenchmark.java @@ -110,24 +110,7 @@ public void setup() { "Virtual threads require Java 21 or higher. Current version: " + JavaSystemSetting.JAVA_VERSION); } - SdkHttpClient.Builder httpClientBuilder; - - switch (client) { - case Apache5: - httpClientBuilder = Apache5HttpClient.builder(); - break; - case Apache4: - httpClientBuilder = ApacheHttpClient.builder(); - break; - case UrlConnection: - httpClientBuilder = UrlConnectionHttpClient.builder(); - break; - case Crt: - httpClientBuilder = AwsCrtHttpClient.builder(); - break; - default: - throw new IllegalArgumentException("Unknown HTTP client: " + client); - } + SdkHttpClient.Builder httpClientBuilder = httpClientBuilder(); s3Client = S3Client.builder() .region(Region.US_WEST_2) @@ -218,4 +201,19 @@ private void safeExecute(Runnable runnable) { throw new RuntimeException("Error during execution", e); } } + + SdkHttpClient.Builder httpClientBuilder() { + switch (client) { + case Apache5: + return Apache5HttpClient.builder(); + case Apache4: + return ApacheHttpClient.builder(); + case UrlConnection: + return UrlConnectionHttpClient.builder(); + case Crt: + return AwsCrtHttpClient.builder(); + default: + throw new IllegalArgumentException("Unknown HTTP client: " + client); + } + } } diff --git a/test/http-client-benchmarks/src/main/java/software/amazon/awssdk/benchmark/apache4/Apache4Benchmark.java b/test/http-client-benchmarks/src/main/java/software/amazon/awssdk/benchmark/apache4/Apache4Benchmark.java index d026d2c2f8f1..adec6b908736 100644 --- a/test/http-client-benchmarks/src/main/java/software/amazon/awssdk/benchmark/apache4/Apache4Benchmark.java +++ b/test/http-client-benchmarks/src/main/java/software/amazon/awssdk/benchmark/apache4/Apache4Benchmark.java @@ -127,7 +127,7 @@ public void multiThreadedPut(Blackhole blackhole) throws Exception { runMultiThreaded(platformThreadPool, threadCount, blackhole, false); } - private void executeGet(Blackhole blackhole) throws Exception { + private void executeGet(Blackhole blackhole) { ResponseInputStream object = s3Client.getObject( r -> r.bucket(benchmarkHelper.bucketName()).key(benchmarkHelper.objKey(objectSize))); blackhole.consume(object.response()); diff --git a/test/http-client-benchmarks/src/main/java/software/amazon/awssdk/benchmark/apache5/Apache5Benchmark.java b/test/http-client-benchmarks/src/main/java/software/amazon/awssdk/benchmark/apache5/Apache5Benchmark.java index 331e9488beba..fafc50e830be 100644 --- a/test/http-client-benchmarks/src/main/java/software/amazon/awssdk/benchmark/apache5/Apache5Benchmark.java +++ b/test/http-client-benchmarks/src/main/java/software/amazon/awssdk/benchmark/apache5/Apache5Benchmark.java @@ -109,18 +109,12 @@ public void setup() { @Benchmark public void simpleGet(Blackhole blackhole) { - ResponseInputStream object = s3Client.getObject( - r -> r.bucket(benchmarkHelper.bucketName()).key(benchmarkHelper.objKey(objectSize))); - blackhole.consume(object.response()); - IoUtils.drainInputStream(object); + executeGet(blackhole); } @Benchmark public void simplePut(Blackhole blackhole) { - PutObjectResponse response = s3Client.putObject( - r -> r.bucket(benchmarkHelper.bucketName()).key("Apache4Benchmark-" + Thread.currentThread().getName()), - benchmarkHelper.requestBody(objectSize)); - blackhole.consume(response); + executePut(blackhole); } @Benchmark diff --git a/test/http-client-benchmarks/src/main/java/software/amazon/awssdk/benchmark/core/ObjectSize.java b/test/http-client-benchmarks/src/main/java/software/amazon/awssdk/benchmark/core/ObjectSize.java index e2187d5bdc82..6edd1b8e022f 100644 --- a/test/http-client-benchmarks/src/main/java/software/amazon/awssdk/benchmark/core/ObjectSize.java +++ b/test/http-client-benchmarks/src/main/java/software/amazon/awssdk/benchmark/core/ObjectSize.java @@ -17,9 +17,9 @@ public enum ObjectSize { - SMALL(1024 * 1024), - MEDIUM(8 * 1024 * 1024), - LARGE(64 * 1024 * 1024),; + SMALL(1024L * 1024), + MEDIUM(8L * 1024 * 1024), + LARGE(64L * 1024 * 1024),; private final long sizeInBytes; From e134a04720bd3660123c7733d1399d7cb9a001c9 Mon Sep 17 00:00:00 2001 From: Dongie Agnir <261310+dagnir@users.noreply.github.com> Date: Tue, 2 Dec 2025 14:56:14 -0800 Subject: [PATCH 4/7] Update test/http-client-benchmarks/src/main/java/software/amazon/awssdk/benchmark/core/ObjectSize.java Co-authored-by: David Ho <70000000+davidh44@users.noreply.github.com> --- .../java/software/amazon/awssdk/benchmark/core/ObjectSize.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/http-client-benchmarks/src/main/java/software/amazon/awssdk/benchmark/core/ObjectSize.java b/test/http-client-benchmarks/src/main/java/software/amazon/awssdk/benchmark/core/ObjectSize.java index 6edd1b8e022f..f9dafbcb7354 100644 --- a/test/http-client-benchmarks/src/main/java/software/amazon/awssdk/benchmark/core/ObjectSize.java +++ b/test/http-client-benchmarks/src/main/java/software/amazon/awssdk/benchmark/core/ObjectSize.java @@ -19,7 +19,7 @@ public enum ObjectSize { SMALL(1024L * 1024), MEDIUM(8L * 1024 * 1024), - LARGE(64L * 1024 * 1024),; + LARGE(64L * 1024 * 1024); private final long sizeInBytes; From da42c25e19ef7922f141f73449962c54f27dfb07 Mon Sep 17 00:00:00 2001 From: Dongie Agnir <261310+dagnir@users.noreply.github.com> Date: Tue, 2 Dec 2025 14:56:27 -0800 Subject: [PATCH 5/7] Update test/http-client-benchmarks/src/main/java/software/amazon/awssdk/benchmark/apache5/Apache5Benchmark.java Co-authored-by: David Ho <70000000+davidh44@users.noreply.github.com> --- .../amazon/awssdk/benchmark/apache5/Apache5Benchmark.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/http-client-benchmarks/src/main/java/software/amazon/awssdk/benchmark/apache5/Apache5Benchmark.java b/test/http-client-benchmarks/src/main/java/software/amazon/awssdk/benchmark/apache5/Apache5Benchmark.java index fafc50e830be..1a2991c85b3f 100644 --- a/test/http-client-benchmarks/src/main/java/software/amazon/awssdk/benchmark/apache5/Apache5Benchmark.java +++ b/test/http-client-benchmarks/src/main/java/software/amazon/awssdk/benchmark/apache5/Apache5Benchmark.java @@ -92,7 +92,7 @@ public void setup() { .httpClient(httpClient) .build(); - benchmarkHelper = new S3BenchmarkHelper(Apache4Benchmark.class.getSimpleName(), s3Client); + benchmarkHelper = new S3BenchmarkHelper(Apache5Benchmark.class.getSimpleName(), s3Client); benchmarkHelper.setup(); // Always use platform threads From 492756a3046576753c9072533a5045f27e837d09 Mon Sep 17 00:00:00 2001 From: Dongie Agnir <261310+dagnir@users.noreply.github.com> Date: Tue, 2 Dec 2025 14:56:35 -0800 Subject: [PATCH 6/7] Update test/http-client-benchmarks/src/main/java/software/amazon/awssdk/benchmark/apache5/Apache5Benchmark.java Co-authored-by: David Ho <70000000+davidh44@users.noreply.github.com> --- .../amazon/awssdk/benchmark/apache5/Apache5Benchmark.java | 1 - 1 file changed, 1 deletion(-) diff --git a/test/http-client-benchmarks/src/main/java/software/amazon/awssdk/benchmark/apache5/Apache5Benchmark.java b/test/http-client-benchmarks/src/main/java/software/amazon/awssdk/benchmark/apache5/Apache5Benchmark.java index 1a2991c85b3f..9d96b7673700 100644 --- a/test/http-client-benchmarks/src/main/java/software/amazon/awssdk/benchmark/apache5/Apache5Benchmark.java +++ b/test/http-client-benchmarks/src/main/java/software/amazon/awssdk/benchmark/apache5/Apache5Benchmark.java @@ -37,7 +37,6 @@ import org.openjdk.jmh.annotations.Warmup; import org.openjdk.jmh.infra.Blackhole; import software.amazon.awssdk.auth.credentials.DefaultCredentialsProvider; -import software.amazon.awssdk.benchmark.apache4.Apache4Benchmark; import software.amazon.awssdk.benchmark.core.CoreBenchmark; import software.amazon.awssdk.benchmark.core.ObjectSize; import software.amazon.awssdk.benchmark.core.S3BenchmarkHelper; From 87c843473cf0dffc4631f70f1c68ac1cfb0a620b Mon Sep 17 00:00:00 2001 From: Dongie Agnir <261310+dagnir@users.noreply.github.com> Date: Tue, 2 Dec 2025 14:56:43 -0800 Subject: [PATCH 7/7] Update test/http-client-benchmarks/src/main/java/software/amazon/awssdk/benchmark/apache5/Apache5Benchmark.java Co-authored-by: David Ho <70000000+davidh44@users.noreply.github.com> --- .../amazon/awssdk/benchmark/apache5/Apache5Benchmark.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/http-client-benchmarks/src/main/java/software/amazon/awssdk/benchmark/apache5/Apache5Benchmark.java b/test/http-client-benchmarks/src/main/java/software/amazon/awssdk/benchmark/apache5/Apache5Benchmark.java index 9d96b7673700..a398716d1ba3 100644 --- a/test/http-client-benchmarks/src/main/java/software/amazon/awssdk/benchmark/apache5/Apache5Benchmark.java +++ b/test/http-client-benchmarks/src/main/java/software/amazon/awssdk/benchmark/apache5/Apache5Benchmark.java @@ -167,7 +167,7 @@ private void executeGet(Blackhole blackhole) { private void executePut(Blackhole blackhole) { PutObjectResponse response = s3Client.putObject( - r -> r.bucket(benchmarkHelper.bucketName()).key("Apache4Benchmark-" + Thread.currentThread().getName()), + r -> r.bucket(benchmarkHelper.bucketName()).key("Apache5Benchmark-" + Thread.currentThread().getName()), benchmarkHelper.requestBody(objectSize)); blackhole.consume(response); }