-
Notifications
You must be signed in to change notification settings - Fork 916
Refactor s3-benchmarks and include v1 transfermanager tests #2575
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -15,6 +15,11 @@ | |
|
||
package software.amazon.awssdk.s3benchmarks; | ||
|
||
import static software.amazon.awssdk.s3benchmarks.BenchmarkUtils.DEFAULT_WARMUP_CONCURRENCY; | ||
import static software.amazon.awssdk.s3benchmarks.BenchmarkUtils.PRE_WARMUP_ITERATIONS; | ||
import static software.amazon.awssdk.s3benchmarks.BenchmarkUtils.PRE_WARMUP_RUNS; | ||
import static software.amazon.awssdk.transfer.s3.SizeConstant.KB; | ||
import static software.amazon.awssdk.transfer.s3.SizeConstant.MB; | ||
import static software.amazon.awssdk.utils.FunctionalUtils.runAndLogError; | ||
|
||
import java.io.File; | ||
|
@@ -28,46 +33,39 @@ | |
import software.amazon.awssdk.core.async.AsyncResponseTransformer; | ||
import software.amazon.awssdk.services.s3.S3Client; | ||
import software.amazon.awssdk.services.s3.model.GetObjectRequest; | ||
import software.amazon.awssdk.services.s3.model.HeadObjectResponse; | ||
import software.amazon.awssdk.services.s3.model.PutObjectRequest; | ||
import software.amazon.awssdk.testutils.RandomTempFile; | ||
import software.amazon.awssdk.transfer.s3.S3TransferManager; | ||
import software.amazon.awssdk.transfer.s3.internal.S3CrtAsyncClient; | ||
import software.amazon.awssdk.utils.Logger; | ||
|
||
public abstract class BaseTransferManagerBenchmark implements TransferManagerBenchmark { | ||
protected static final int WARMUP_ITERATIONS = 10; | ||
protected static final int BENCHMARK_ITERATIONS = 10; | ||
|
||
private static final Logger logger = Logger.loggerFor("TransferManagerBenchmark"); | ||
private static final String WARMUP_KEY = "warmupobject"; | ||
|
||
protected final S3TransferManager transferManager; | ||
protected final S3CrtAsyncClient s3; | ||
protected final S3Client s3Sync; | ||
protected final String bucket; | ||
protected final String key; | ||
protected final String path; | ||
private final File file; | ||
protected final int warmupConcurrency; | ||
protected final File tmpFile; | ||
|
||
BaseTransferManagerBenchmark(TransferManagerBenchmarkConfig config) { | ||
logger.info(() -> "Benchmark config: " + config); | ||
Long partSizeInMb = config.partSizeInMb() == null ? null : config.partSizeInMb() * 1024 * 1024L; | ||
Long partSizeInMb = config.partSizeInMb() == null ? null : config.partSizeInMb() * MB; | ||
s3 = S3CrtAsyncClient.builder() | ||
.targetThroughputInGbps(config.targetThroughput()) | ||
.minimumPartSizeInBytes(partSizeInMb) | ||
.build(); | ||
s3Sync = S3Client.builder() | ||
.build(); | ||
transferManager = S3TransferManager.builder() | ||
.s3ClientConfiguration(b -> b.targetThroughputInGbps(config.targetThroughput()) | ||
.minimumPartSizeInBytes(partSizeInMb)) | ||
.build(); | ||
bucket = config.bucket(); | ||
key = config.key(); | ||
path = config.filePath(); | ||
warmupConcurrency = config.warmupConcurrency() == null ? DEFAULT_WARMUP_CONCURRENCY : config.warmupConcurrency(); | ||
try { | ||
file = new RandomTempFile(1024 * 1000L); | ||
tmpFile = new RandomTempFile(1000 * KB); | ||
} catch (IOException e) { | ||
logger.error(() -> "Failed to create the file"); | ||
throw new RuntimeException("Failed to create the temp file", e); | ||
|
@@ -95,36 +93,15 @@ protected void additionalWarmup() { | |
|
||
protected abstract void doRunBenchmark(); | ||
|
||
protected final void printOutResult(List<Double> metrics, String name) { | ||
logger.info(() -> String.format("=============== %s Result ================", name)); | ||
logger.info(() -> "" + metrics); | ||
double averageLatency = metrics.stream() | ||
.mapToDouble(a -> a) | ||
.average() | ||
.orElse(0.0); | ||
|
||
double lowestLatency = metrics.stream() | ||
.mapToDouble(a -> a) | ||
.min().orElse(0.0); | ||
|
||
HeadObjectResponse headObjectResponse = s3Sync.headObject(b -> b.bucket(bucket).key(key)); | ||
double contentLengthInGigabit = (headObjectResponse.contentLength() / (1000 * 1000 * 1000.0)) * 8.0; | ||
logger.info(() -> "Average latency (s): " + averageLatency); | ||
logger.info(() -> "Object size (Gigabit): " + contentLengthInGigabit); | ||
logger.info(() -> "Average throughput (Gbps): " + contentLengthInGigabit / averageLatency); | ||
logger.info(() -> "Highest average throughput (Gbps): " + contentLengthInGigabit / lowestLatency); | ||
logger.info(() -> "=========================================================="); | ||
} | ||
|
||
private void cleanup() { | ||
s3Sync.deleteObject(b -> b.bucket(bucket).key(WARMUP_KEY)); | ||
transferManager.close(); | ||
s3.close(); | ||
} | ||
|
||
private void warmUp() throws InterruptedException { | ||
logger.info(() -> "Starting to warm up"); | ||
|
||
for (int i = 0; i < WARMUP_ITERATIONS; i++) { | ||
for (int i = 0; i < PRE_WARMUP_ITERATIONS; i++) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. "warmup" or "pre-warmup"? The latter seems to imply:
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is actually pre-warmup. I'll extract this logic out to make it more clear. Pre-warmup is just jvm/sdk warming up, sending downloading and uploading requests for smaller object. Warmup is to send the same request as the actual run. Warmup is needed for crt based s3 client because it takes sometime to resolve the IP addresses and create new connections. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think for the sake of simplicity, I would still consider merging all of this into the pure "warmup" phase, and running more iterations if needed. |
||
warmUpUploadBatch(); | ||
warmUpDownloadBatch(); | ||
|
||
|
@@ -136,7 +113,7 @@ private void warmUp() throws InterruptedException { | |
|
||
private void warmUpDownloadBatch() { | ||
List<CompletableFuture<?>> futures = new ArrayList<>(); | ||
for (int i = 0; i < 20; i++) { | ||
for (int i = 0; i < PRE_WARMUP_RUNS; i++) { | ||
Path tempFile = RandomTempFile.randomUncreatedFile().toPath(); | ||
futures.add(s3.getObject(GetObjectRequest.builder().bucket(bucket).key(WARMUP_KEY).build(), | ||
AsyncResponseTransformer.toFile(tempFile)).whenComplete((r, t) -> runAndLogError( | ||
|
@@ -148,9 +125,9 @@ private void warmUpDownloadBatch() { | |
|
||
private void warmUpUploadBatch() { | ||
List<CompletableFuture<?>> futures = new ArrayList<>(); | ||
for (int i = 0; i < 20; i++) { | ||
for (int i = 0; i < PRE_WARMUP_RUNS; i++) { | ||
futures.add(s3.putObject(PutObjectRequest.builder().bucket(bucket).key(WARMUP_KEY).build(), | ||
AsyncRequestBody.fromFile(file))); | ||
AsyncRequestBody.fromFile(tmpFile))); | ||
} | ||
|
||
CompletableFuture.allOf(futures.toArray(new CompletableFuture<?>[0])).join(); | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,52 @@ | ||
/* | ||
* 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.s3benchmarks; | ||
|
||
import java.util.List; | ||
import software.amazon.awssdk.utils.Logger; | ||
|
||
public class BenchmarkUtils { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. nit: final |
||
protected static final int PRE_WARMUP_ITERATIONS = 10; | ||
protected static final int PRE_WARMUP_RUNS = 20; | ||
protected static final int BENCHMARK_ITERATIONS = 10; | ||
protected static final int DEFAULT_WARMUP_CONCURRENCY = 100; | ||
protected static final String WARMUP_KEY = "warmupobject"; | ||
|
||
private static final Logger logger = Logger.loggerFor("TransferManagerBenchmark"); | ||
|
||
private BenchmarkUtils() { | ||
} | ||
|
||
public static void printOutResult(List<Double> metrics, String name, long contentLengthInByte) { | ||
logger.info(() -> String.format("=============== %s Result ================", name)); | ||
logger.info(() -> "" + metrics); | ||
double averageLatency = metrics.stream() | ||
.mapToDouble(a -> a) | ||
.average() | ||
.orElse(0.0); | ||
|
||
double lowestLatency = metrics.stream() | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Percentiles might be interesting here as well, rather than just min/max. https://guava.dev/releases/23.0/api/docs/com/google/common/math/Quantiles.html There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Ah yeah, good idea |
||
.mapToDouble(a -> a) | ||
.min().orElse(0.0); | ||
|
||
double contentLengthInGigabit = (contentLengthInByte / (1000 * 1000 * 1000.0)) * 8.0; | ||
logger.info(() -> "Average latency (s): " + averageLatency); | ||
logger.info(() -> "Object size (Gigabit): " + contentLengthInGigabit); | ||
logger.info(() -> "Average throughput (Gbps): " + contentLengthInGigabit / averageLatency); | ||
logger.info(() -> "Highest average throughput (Gbps): " + contentLengthInGigabit / lowestLatency); | ||
logger.info(() -> "=========================================================="); | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I removed S3TransferManager and used the internal S3CrtClient for testing because S3TransferManager doesn't allow customizing S3 client , and we can't reuse the same S3 crt client.