-
I feel like I already know the answer to this, and it's not one I want, but is there any such thing as a Stable Version in this project? It just seems like it's a constant stream of new tags, every day or so, with no formal or consistent release notes or any real high-level indication of what's changed and whether a particular release is good or bad. Our product has used this SDK for many years, 1.3.10, 1.4.76, 1.7.83, 1.7.123, 1.7.301, 1.9.335, and back to 1.7.301 for unclear reasons (developer since departed). I inherited the long-overdue dependencies update task, and about six months ago I tried some newer versions, most recently 1.11.376, but it seems that there was an S3 access performance cliff somewhere before that point, so I literally did a brute-force bisection and found a subjective ~25% performance drop between 1.11.211 and 1.11.212, although looking at the (substantial) diff between those two versions gave no real insight as to what might have caused it. Even 1.11.211 was noticeably slower than 1.7.301 in high-level profiling. Obviously I'm aware that any profiling of S3 access is subject to the whims of the Internet, but I tried multiple runs at different times of day and the mean difference was still clear. There have been nearly 150 new versions since that point, now that I'm looking at it again. I'm about to build with the latest 1.11.517 and compare the performance again with my most recent fave, 1.11.211, and with our current 1.7.301. It's also not clear what version of Curl we should be using underneath it, plus we have Apache Arrow in the mix too, wanting to update that from 9 to at least 16. Our unit tests that go through Arrow didn't seem as affected by the later versions, so I tried stealing the Endpoint Provider Cache implementation from Arrow for our own non-Arrow S3 access code paths, which caches Any and all suggestions warmly welcomed. |
Beta Was this translation helpful? Give feedback.
Replies: 4 comments 29 replies
-
hey @simoneves thanks for reaching out and sorry about any frustration you may have, lets see if we cant work through it.
the way versioning works on this project is each version number is 1.11.x -> receives daily updates so the latest version of 1.11.x will receive model updates, and bug fixes, the older versions will not. So if you want the freshest version of the SDK 1.11.x is the latest version and will have all AWS models and bug fixes. So to answer your question, there is no "stable version" there is more so a "currently worked on version".
Each day we release new model for aws services. these models add new functionality for services. For instance last fridays added several features for different AWS services. for instance one of them is
which you can see reflected in that diff. This diff is not very helpful though and we plan to move to something like how go-v2 does it, but I cannot give a time frame on when we will get to that. all releases should be "good" and go through a daily CI.
The version that you are speaking on specifically introduced support for s3 express which we had to do some work for to support. But we've also pushed bug fixes for it and pushed several s3 features for it since, so really the best baseline is against the current HEAD of the project. Also you don't mention what the performance drop is in? what are you seeing a performance drop with? specifically with a reproducible example what are you doing where you see a performance drop
We dont have have a recommended version, its more or less bring what version you want to or that your system supports. I would defer to the curl project to determine the best version. That said there are some known buggy versions of curl on mac specifically that we have called out in the past.
Are you creating and destroying lots of S3Clients? |
Beta Was this translation helpful? Give feedback.
-
No you're fine! we love finding areas where we can help people, and if we're doing something bad we like hearing about it before we do more bad!
So this is something that is known and is actually something that pyarrow has actually called out #2880 to us before. tl;dr the logic of how endpoints are created each time is kind of expensive, so avoid creating lots of clients. If they share a endpoint provider like how pyarrow does it, this hit can be minimized to a single time. One general piece of advice i can give is that if possible, avoid calling client creation too many times. The client is thread safe and one should prefer to call the async methods on the client, instead of trying to manage many clients.
Yeah this might be tricky to find. If you can narrow down the usage that is slower we would love to take a look at it and hopefully fix it. where we spend a lot of time monitoring performance is the hot paths like GetObject or PutObject, which we should have caught. but if we didnt, more than happy to sort it out. I know this complicates a simple update task but if you want to try to use the s3-crt client instead of the normal "S3Client" you will almost certainly see a performance increase. It is essentially the same s3 client except GetObject, PutObject, and CopyObject are super charged through the CRT, which customers have usually found to be better. FWIW the big change between 1.7/1.8 and 1.9 was the introduction of the CRT libraries into the SDK which is why the old engineer may have downgraded in the past, as they can be tricky to work with, but should be a drop in replacement.. |
Beta Was this translation helpful? Give feedback.
-
So before going further how are you actually building and distributing the old version, from our end it doesnt compile with warnings enabled on current platforms. So how are you actually building it/installing it/using it? i.e. FROM public.ecr.aws/amazonlinux/amazonlinux:2023
RUN yum groupinstall "Development Tools" -y
RUN yum install -y curl-devel openssl-devel ninja-build cmake3
# build and install SDK
RUN git clone -b 1.7.301 --recurse-submodules https://github.com/aws/aws-sdk-cpp && \
cd aws-sdk-cpp && \
mkdir build && \
cd build && \
cmake -DBUILD_ONLY="s3" -DCMAKE_INSTALL_PREFIX=/sdk-install -DAUTORUN_UNIT_TESTS=OFF .. && \
cmake --build . && \
cmake --install .
So im even having a hard time trying to profile a diff between the two. That aside when you talk about measuring different things, its really hard to know what you are measuring and how, so lets focus on one example and build from there. heres a googlebenchmark test that profiles calling get object 20 times on a 100MB file. What performance difference do you see while using that
CMakeLists.txt cmake_minimum_required(VERSION 3.13)
project(sdk_benchmark)
set(CMAKE_CXX_STANDARD 20)
include(FetchContent)
FetchContent_Declare(gbench
GIT_REPOSITORY https://github.com/google/benchmark
GIT_TAG v1.8.3
)
FetchContent_MakeAvailable(gbench)
find_package(AWSSDK REQUIRED COMPONENTS s3)
add_executable(${PROJECT_NAME} "src/main.cpp")
target_link_libraries(${PROJECT_NAME} benchmark::benchmark ${AWSSDK_LINK_LIBRARIES}) src/main.cpp #include <aws/core/Aws.h>
#include <aws/s3/S3Client.h>
#include <aws/s3/model/ListBucketsRequest.h>
#include <aws/s3/model/ListObjectsV2Request.h>
#include <aws/s3/model/CreateBucketRequest.h>
#include <aws/s3/model/PutObjectRequest.h>
#include <aws/s3/model/GetObjectRequest.h>
#include <benchmark/benchmark.h>
#include <fstream>
using namespace Aws;
using namespace Aws::S3;
using namespace Aws::S3::Model;
static const char *BUCKET_NAME = "your-bucket-name-unique";
static const char *KEY = "key";
static const char *LOG_TAG = "s3benchmark";
static const int ITERATIONS = 20;
static SDKOptions s_options;
struct TestFilePair {
const char *file_name;
StorageClass storage_class;
};
static void CreateBucketUploadTestObjects() {
S3Client client{};
// Create the bucket if it doesnt exist
auto list_buckets_outcome = client.ListBuckets(ListBucketsRequest());
assert(list_buckets_outcome.IsSuccess());
Vector<Bucket> buckets{};
auto listed_buckets = list_buckets_outcome.GetResult().GetBuckets();
buckets.insert(buckets.end(), listed_buckets.begin(), listed_buckets.end());
while (!list_buckets_outcome.GetResult().GetContinuationToken().empty()) {
list_buckets_outcome = client.ListBuckets(ListBucketsRequest().WithContinuationToken(list_buckets_outcome.GetResult().GetContinuationToken()));
listed_buckets = list_buckets_outcome.GetResult().GetBuckets();
assert(list_buckets_outcome.IsSuccess());
buckets.insert(buckets.end(), listed_buckets.begin(), listed_buckets.end());
}
if (std::ranges::find_if(buckets, [](const Bucket &bucket) { return bucket.GetName() == BUCKET_NAME; }) == buckets.end()) {
auto create_bucket_output = client.CreateBucket(CreateBucketRequest().WithBucket(BUCKET_NAME));
assert(create_bucket_output.IsSuccess());
benchmark::DoNotOptimize(create_bucket_output);
}
// Create Objects if they dont exist
auto list_objects_outcome = client.ListObjectsV2(ListObjectsV2Request().WithBucket(BUCKET_NAME));
assert(list_objects_outcome.IsSuccess());
Vector<Object> objects{};
objects.insert(objects.end(), list_objects_outcome.GetResult().GetContents().begin(), list_objects_outcome.GetResult().GetContents().end());
while (!list_objects_outcome.GetResult().GetContinuationToken().empty()) {
list_objects_outcome = client.ListObjectsV2(ListObjectsV2Request().WithBucket(BUCKET_NAME).WithContinuationToken(list_objects_outcome.GetResult().GetContinuationToken()));
assert(list_objects_outcome.IsSuccess());
objects.insert(objects.end(), list_objects_outcome.GetResult().GetContents().begin(), list_objects_outcome.GetResult().GetContents().end());
}
if (std::ranges::find_if(objects, [](const Object& object) { return object.GetKey() == KEY; }) == objects.end()) {
auto put_object_request = PutObjectRequest().WithBucket(BUCKET_NAME).WithKey(KEY);
std::shared_ptr<IOStream> body = Aws::MakeShared<FStream>(LOG_TAG, "test-files/100mb.txt", std::ios::binary | std::ios::in);
put_object_request.SetBody(body);
auto put_object_response = client.PutObject(put_object_request);
assert(put_object_response.IsSuccess());
benchmark::DoNotOptimize(put_object_response);
}
}
static void DoSetup(const benchmark::State &state) {
InitAPI(s_options);
{
CreateBucketUploadTestObjects();
}
}
static void DoTeardown(const benchmark::State &state) {
ShutdownAPI(s_options);
}
static void BM_S3StandardTierFile(benchmark::State& state) {
S3Client client{};
for (auto _ : state) {
auto get_object_output = client.GetObject(GetObjectRequest().WithBucket(BUCKET_NAME).WithKey(KEY));
assert(get_object_output.IsSuccess());
benchmark::DoNotOptimize(get_object_output);
}
}
BENCHMARK(BM_S3StandardTierFile)
->Setup(DoSetup)
->Teardown(DoTeardown)
->MeasureProcessCPUTime()
->Iterations(ITERATIONS)
->Unit(benchmark::kMillisecond)
->UseRealTime();
BENCHMARK_MAIN(); Also goes without saying but appreciate you looking into it indepth |
Beta Was this translation helpful? Give feedback.
-
@sbiscigl thank you for the above, but... I added some more profiling, and it seems that even just constructing a After some wailing and gnashing of teeth with my remote colleagues, one of them then came across this... ...and the related section of this... https://github.com/aws/aws-sdk-cpp/wiki/What%E2%80%99s-New-in-AWS-SDK-for-CPP-Version-1.8 To cut a long story short, if one does indeed set Even the build with 1.7.301 runs WAY faster, presumably due to the expensive client inits I was seeing even with that now also being quick. The particular unit test which used to take 67s, and which increased to 122s with 1.11.211 and 215s with 1.11.212 and later, now runs in just under 6s. This is both excellent AND very frustrating, but oh well. I guess my question now is, what would be the OFFICIAL C++ API way of disabling the automatic EC2 region lookup, rather than having to do a It also begs the question of why But frankly, I don't care, because even that I will report back again when I've done some more testing. |
Beta Was this translation helpful? Give feedback.
@sbiscigl thank you for the above, but...
I added some more profiling, and it seems that even just constructing a
ClientConfiguration
(or these days, anS3ClientConfiguration
) was what was taking the two seconds. Literally a low-level timer around the constructor line showed that.After some wailing and gnashing of teeth with my remote colleagues, one of them then came across this...
#1410
...and the related section of this...
https://github.com/aws/aws-sdk-cpp/wiki/What%E2%80%99s-New-in-AWS-SDK-for-CPP-Version-1.8
To cut a long story short, if one does indeed set
AWS_EC2_METADATA_DISABLED=true
everything then runs blazing fast.Even the build with 1.7.301 runs WAY faster, presumably due …