From bcbad7c024cf102842b89cb60925be05d90a1e96 Mon Sep 17 00:00:00 2001 From: Horia Mercan Date: Wed, 25 Jun 2025 09:59:22 +0300 Subject: [PATCH 01/12] [aws] Add architecture configuration for cpp base images. Ensure the right path for creating package code zip. --- config/systems.json | 4 +++- dockerfiles/aws/cpp/Dockerfile.build | 4 ++-- sebs/aws/aws.py | 2 +- 3 files changed, 6 insertions(+), 4 deletions(-) diff --git a/config/systems.json b/config/systems.json index f646cec1..8a7ed472 100644 --- a/config/systems.json +++ b/config/systems.json @@ -124,7 +124,9 @@ }, "cpp": { "base_images": { - "all": "amazon/aws-lambda-provided:al2.2022.04.27.09" + "x64":{ + "all": "amazon/aws-lambda-provided:al2.2022.04.27.09" + } }, "dependencies": [ "runtime", "sdk", "boost", "hiredis" diff --git a/dockerfiles/aws/cpp/Dockerfile.build b/dockerfiles/aws/cpp/Dockerfile.build index 2699806e..d91a4322 100755 --- a/dockerfiles/aws/cpp/Dockerfile.build +++ b/dockerfiles/aws/cpp/Dockerfile.build @@ -15,8 +15,8 @@ ENV GOSU_VERSION 1.14 RUN curl -o /usr/local/bin/gosu -SL "https://github.com/tianon/gosu/releases/download/${GOSU_VERSION}/gosu-amd64" \ && chmod +x /usr/local/bin/gosu RUN mkdir -p /sebs/ -COPY docker/entrypoint.sh /sebs/entrypoint.sh -COPY docker/cpp_installer.sh /sebs/installer.sh +COPY dockerfiles/entrypoint.sh /sebs/entrypoint.sh +COPY dockerfiles/cpp_installer.sh /sebs/installer.sh RUN chmod +x /sebs/entrypoint.sh RUN chmod +x /sebs/installer.sh diff --git a/sebs/aws/aws.py b/sebs/aws/aws.py index a32c9830..8a7ff425 100644 --- a/sebs/aws/aws.py +++ b/sebs/aws/aws.py @@ -179,7 +179,7 @@ def package_code( raise NotImplementedError() return ( - os.path.join(directory, "{}.zip".format(benchmark)), + benchmark_archive, bytes_size, container_uri, ) From 97d2db41977d75d3447ceec5c8fb368a91e35f52 Mon Sep 17 00:00:00 2001 From: Horia Mercan Date: Sun, 29 Jun 2025 21:01:07 +0300 Subject: [PATCH 02/12] Implemented 210-thumbnail benchmark in cpp. --- .../210.thumbnailer/config.json | 2 +- .../210.thumbnailer/cpp/Base64.hpp | 126 ++++++++++++++++++ .../210.thumbnailer/cpp/function.hpp | 27 ++++ .../210.thumbnailer/cpp/main.cpp | 117 ++++++++++++++++ benchmarks/wrappers/aws/cpp/storage.cpp | 46 +++++-- benchmarks/wrappers/aws/cpp/storage.hpp | 19 ++- sebs/benchmark.py | 4 + 7 files changed, 325 insertions(+), 16 deletions(-) create mode 100644 benchmarks/200.multimedia/210.thumbnailer/cpp/Base64.hpp create mode 100644 benchmarks/200.multimedia/210.thumbnailer/cpp/function.hpp create mode 100644 benchmarks/200.multimedia/210.thumbnailer/cpp/main.cpp diff --git a/benchmarks/200.multimedia/210.thumbnailer/config.json b/benchmarks/200.multimedia/210.thumbnailer/config.json index 8edb99e5..aa5677a7 100644 --- a/benchmarks/200.multimedia/210.thumbnailer/config.json +++ b/benchmarks/200.multimedia/210.thumbnailer/config.json @@ -1,6 +1,6 @@ { "timeout": 60, "memory": 256, - "languages": ["python", "nodejs"], + "languages": ["python", "nodejs", "cpp"], "modules": ["storage"] } diff --git a/benchmarks/200.multimedia/210.thumbnailer/cpp/Base64.hpp b/benchmarks/200.multimedia/210.thumbnailer/cpp/Base64.hpp new file mode 100644 index 00000000..ac2b6882 --- /dev/null +++ b/benchmarks/200.multimedia/210.thumbnailer/cpp/Base64.hpp @@ -0,0 +1,126 @@ + +#ifndef _MACARON_BASE64_H_ +#define _MACARON_BASE64_H_ + +/** + * The MIT License (MIT) + * Copyright (c) 2016 tomykaira + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +#include + +namespace macaron { + +class Base64 { + public: + + static std::string Encode(const std::string data) { + static constexpr char sEncodingTable[] = { + 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', + 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', + 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', + 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', + 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', + 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', + 'w', 'x', 'y', 'z', '0', '1', '2', '3', + '4', '5', '6', '7', '8', '9', '+', '/' + }; + + size_t in_len = data.size(); + size_t out_len = 4 * ((in_len + 2) / 3); + std::string ret(out_len, '\0'); + size_t i; + char *p = const_cast(ret.c_str()); + + for (i = 0; i < in_len - 2; i += 3) { + *p++ = sEncodingTable[(data[i] >> 2) & 0x3F]; + *p++ = sEncodingTable[((data[i] & 0x3) << 4) | ((int) (data[i + 1] & 0xF0) >> 4)]; + *p++ = sEncodingTable[((data[i + 1] & 0xF) << 2) | ((int) (data[i + 2] & 0xC0) >> 6)]; + *p++ = sEncodingTable[data[i + 2] & 0x3F]; + } + if (i < in_len) { + *p++ = sEncodingTable[(data[i] >> 2) & 0x3F]; + if (i == (in_len - 1)) { + *p++ = sEncodingTable[((data[i] & 0x3) << 4)]; + *p++ = '='; + } + else { + *p++ = sEncodingTable[((data[i] & 0x3) << 4) | ((int) (data[i + 1] & 0xF0) >> 4)]; + *p++ = sEncodingTable[((data[i + 1] & 0xF) << 2)]; + } + *p++ = '='; + } + + return ret; + } + + static std::string Decode(const std::string& input, std::string& out) { + static constexpr unsigned char kDecodingTable[] = { + 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, + 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, + 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 62, 64, 64, 64, 63, + 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 64, 64, 64, 64, 64, 64, + 64, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, + 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 64, 64, 64, 64, 64, + 64, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, + 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 64, 64, 64, 64, 64, + 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, + 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, + 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, + 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, + 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, + 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, + 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, + 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64 + }; + + size_t in_len = input.size(); + if (in_len % 4 != 0) return "Input data size is not a multiple of 4"; + + size_t out_len = in_len / 4 * 3; + if (input[in_len - 1] == '=') out_len--; + if (input[in_len - 2] == '=') out_len--; + + out.resize(out_len); + + for (size_t i = 0, j = 0; i < in_len;) { + uint32_t a = input[i] == '=' ? 0 & i++ : kDecodingTable[static_cast(input[i++])]; + uint32_t b = input[i] == '=' ? 0 & i++ : kDecodingTable[static_cast(input[i++])]; + uint32_t c = input[i] == '=' ? 0 & i++ : kDecodingTable[static_cast(input[i++])]; + uint32_t d = input[i] == '=' ? 0 & i++ : kDecodingTable[static_cast(input[i++])]; + + uint32_t triple = (a << 3 * 6) + (b << 2 * 6) + (c << 1 * 6) + (d << 0 * 6); + + if (j < out_len) out[j++] = (triple >> 2 * 8) & 0xFF; + if (j < out_len) out[j++] = (triple >> 1 * 8) & 0xFF; + if (j < out_len) out[j++] = (triple >> 0 * 8) & 0xFF; + } + + return ""; + } + +}; + +} + +#endif /* _MACARON_BASE64_H_ */ + diff --git a/benchmarks/200.multimedia/210.thumbnailer/cpp/function.hpp b/benchmarks/200.multimedia/210.thumbnailer/cpp/function.hpp new file mode 100644 index 00000000..9b8b1773 --- /dev/null +++ b/benchmarks/200.multimedia/210.thumbnailer/cpp/function.hpp @@ -0,0 +1,27 @@ + +#include +#include + +void thumbnailer(cv::Mat &in, int64_t width, int64_t height, cv::Mat &out) +{ + try + { + // Calculate thumbnail size while maintaining aspect ratio + int orig_width = in.cols; + int orig_height = in.rows; + + double scale_w = static_cast(width) / orig_width; + double scale_h = static_cast(height) / orig_height; + double scale = std::min(scale_w, scale_h); // Use smaller scale to fit within bounds + + int new_width = static_cast(orig_width * scale); + int new_height = static_cast(orig_height * scale); + + // Resize image (equivalent to PIL's thumbnail method) + cv::resize(in, out, cv::Size(new_width, new_height), cv::INTER_LINEAR); + } + catch (const cv::Exception &e) + { + std::cerr << "OpenCV error: " << e.what() << std::endl; + } +} diff --git a/benchmarks/200.multimedia/210.thumbnailer/cpp/main.cpp b/benchmarks/200.multimedia/210.thumbnailer/cpp/main.cpp new file mode 100644 index 00000000..016e5010 --- /dev/null +++ b/benchmarks/200.multimedia/210.thumbnailer/cpp/main.cpp @@ -0,0 +1,117 @@ + +#include +#include + +#include +#include +#include + +#include +#include + +#include "Base64.hpp" +#include "function.hpp" +#include "storage.hpp" +#include "utils.hpp" + +Aws::Utils::Json::JsonValue function(Aws::Utils::Json::JsonView request) +{ + Storage client_ = Storage::get_client(); + + auto bucket_obj = request.GetObject("bucket"); + if (!bucket_obj.IsObject()) + { + Aws::Utils::Json::JsonValue error; + error.WithString("error", "Bucket object is not valid."); + return error; + } + auto bucket_name = bucket_obj.GetString("bucket"); + auto input_key_prefix = bucket_obj.GetString("input"); + auto output_key_prefix = bucket_obj.GetString("output"); + + auto image_name = request.GetObject("object").GetString("key"); + auto width = request.GetObject("object").GetInteger("width"); + auto height = request.GetObject("object").GetInteger("height"); + + std::string body_str; + uint64_t download_time; + { + std::string input_key = input_key_prefix + "/" + image_name; + auto ans = client_.download_file(bucket_name, input_key); + body_str = std::get<0>(ans); + download_time = std::get<1>(ans); + + if (body_str.empty()) + { + Aws::Utils::Json::JsonValue error; + error.WithString("error", "Failed to download object from S3: " + input_key); + return error; + } + } + + std::vector vectordata(body_str.begin(), body_str.end()); + cv::Mat image = imdecode(cv::Mat(vectordata), 1); + + // Apply the thumbnailer function and measure the computing time + cv::Mat image2; + uint64_t computing_time; + { + auto start_time = timeSinceEpochMicrosec(); + thumbnailer(image, width, height, image2); + computing_time = timeSinceEpochMicrosec() - start_time; + } + + std::vector out_buffer; + cv::imencode(".jpg", image2, out_buffer); + + // Create a unique key name for the output image + std::string key_name; + { + std::string output_key = output_key_prefix + "/" + image_name; + std::string name, extension; + if (output_key.find_last_of('.') != std::string::npos) + { + name = output_key.substr(0, output_key.find_last_of('.')); + extension = output_key.substr(output_key.find_last_of('.')); + } + else + { + name = output_key; + extension = ""; + } + key_name = name + "." + + boost::uuids::to_string(boost::uuids::random_generator()()) + + extension; + } + + // Upload the resulting image to S3 + // If the upload fails, return an error + Aws::String upload_data(out_buffer.begin(), out_buffer.end()); + + uint64_t upload_time = client_.upload_random_file( + bucket_name, key_name, true, upload_data); + + if (upload_time == 0) + { + Aws::Utils::Json::JsonValue error; + error.WithString("error", "Failed to upload object to S3: " + key_name); + return error; + } + + + Aws::Utils::Json::JsonValue val; + Aws::Utils::Json::JsonValue result; + Aws::Utils::Json::JsonValue measurements; + + result.WithString("bucket", bucket_name); + result.WithString("key", key_name); + val.WithObject("result", result); + + measurements.WithInteger("download_time", download_time); + measurements.WithInteger("upload_time", upload_time); + measurements.WithInteger("compute_time", computing_time); + measurements.WithInteger("download_size", vectordata.size()); + measurements.WithInteger("upload_size", out_buffer.size()); + val.WithObject("measurements", measurements); + return val; +} \ No newline at end of file diff --git a/benchmarks/wrappers/aws/cpp/storage.cpp b/benchmarks/wrappers/aws/cpp/storage.cpp index 0ed61946..9e5a2231 100644 --- a/benchmarks/wrappers/aws/cpp/storage.cpp +++ b/benchmarks/wrappers/aws/cpp/storage.cpp @@ -15,7 +15,6 @@ Storage Storage::get_client() { Aws::Client::ClientConfiguration config; - config.caFile = "/etc/pki/tls/certs/ca-bundle.crt"; char const TAG[] = "LAMBDA_ALLOC"; auto credentialsProvider = @@ -26,8 +25,8 @@ Storage Storage::get_client() { uint64_t Storage::download_file(Aws::String const &bucket, Aws::String const &key, int &required_retries, - bool report_dl_time) { - + bool report_dl_time, + Aws::IOStream &output_stream) { Aws::S3::Model::GetObjectRequest request; request.WithBucket(bucket).WithKey(key); auto bef = timeSinceEpochMicrosec(); @@ -40,11 +39,10 @@ uint64_t Storage::download_file(Aws::String const &bucket, if (outcome.IsSuccess()) { auto &s = outcome.GetResult().GetBody(); uint64_t finishedTime = timeSinceEpochMicrosec(); - // Perform NOP on result to prevent optimizations - std::stringstream ss; - ss << s.rdbuf(); - std::string first(" "); - ss.get(&first[0], 1); + + output_stream.clear(); + output_stream << s.rdbuf(); + required_retries = retries; if (report_dl_time) { return finishedTime - bef; @@ -63,9 +61,30 @@ uint64_t Storage::download_file(Aws::String const &bucket, return 0; } +std::tuple Storage::download_file( + Aws::String const &bucket, Aws::String const &key) { + Aws::S3::Model::GetObjectRequest request; + request.WithBucket(bucket).WithKey(key); + auto bef = timeSinceEpochMicrosec(); + + Aws::S3::Model::GetObjectOutcome outcome = this->_client.GetObject(request); + if (!outcome.IsSuccess()) { + std::cerr << "Error: GetObject: " << outcome.GetError().GetMessage() + << std::endl; + return {"", 0}; + } + auto &s = outcome.GetResult().GetBody(); + uint64_t finishedTime = timeSinceEpochMicrosec(); + + std::string content((std::istreambuf_iterator(s)), + std::istreambuf_iterator()); + return {content, finishedTime - bef}; +} + uint64_t Storage::upload_random_file(Aws::String const &bucket, - Aws::String const &key, int size, - char *pBuf) { + Aws::String const &key, + bool report_dl_time, + Aws::String data) { /** * We use Boost's bufferstream to wrap the array as an IOStream. Usign a * light-weight streambuf wrapper, as many solutions (e.g. @@ -74,16 +93,19 @@ uint64_t Storage::upload_random_file(Aws::String const &bucket, * functioning tellp(), etc... (for instance to get the body length). */ const std::shared_ptr input_data = - std::make_shared(pBuf, size); + Aws::MakeShared("LAMBDA_ALLOC", std::move(data)); Aws::S3::Model::PutObjectRequest request; request.WithBucket(bucket).WithKey(key); request.SetBody(input_data); uint64_t bef_upload = timeSinceEpochMicrosec(); Aws::S3::Model::PutObjectOutcome outcome = this->_client.PutObject(request); + int64_t finishedTime = timeSinceEpochMicrosec(); if (!outcome.IsSuccess()) { std::cerr << "Error: PutObject: " << outcome.GetError().GetMessage() << std::endl; + return 0; } - return bef_upload; + return report_dl_time ? finishedTime - bef_upload + : finishedTime; } diff --git a/benchmarks/wrappers/aws/cpp/storage.hpp b/benchmarks/wrappers/aws/cpp/storage.hpp index 2b548ef2..0dfad0a4 100644 --- a/benchmarks/wrappers/aws/cpp/storage.hpp +++ b/benchmarks/wrappers/aws/cpp/storage.hpp @@ -6,8 +6,8 @@ class Storage { - Aws::S3::S3Client _client; public: + Aws::S3::S3Client _client; Storage(Aws::S3::S3Client && client): _client(client) @@ -18,11 +18,24 @@ class Storage uint64_t download_file(Aws::String const &bucket, Aws::String const &key, int &required_retries, - bool report_dl_time); + bool report_dl_time, + Aws::IOStream &output_stream); + + /* + * Downloads a file from S3 + * @param bucket The S3 bucket name + * @param key The S3 object key + * @return A tuple containing the file content as a string and the elapsed + * time in microseconds. + * If the download fails, an empty string and 0 are returned. + */ + std::tuple download_file(Aws::String const &bucket, + Aws::String const &key); uint64_t upload_random_file(Aws::String const &bucket, Aws::String const &key, - int size, char* pBuf); + bool report_dl_time, + Aws::String data); }; diff --git a/sebs/benchmark.py b/sebs/benchmark.py index 5dd8e621..4a2ae902 100644 --- a/sebs/benchmark.py +++ b/sebs/benchmark.py @@ -434,6 +434,10 @@ def add_deployment_package_cpp(self, output_dir): find_package(Boost REQUIRED) target_include_directories(${PROJECT_NAME} PRIVATE ${Boost_INCLUDE_DIRS}) target_link_libraries(${PROJECT_NAME} PRIVATE ${Boost_LIBRARIES}) + + find_package(OpenCV REQUIRED) + include_directories(${OpenCV_INCLUDE_DIRS}) + target_link_libraries(${PROJECT_NAME} PRIVATE ${OpenCV_LIBS}) find_package(AWSSDK COMPONENTS s3 dynamodb core) target_link_libraries(${PROJECT_NAME} PUBLIC ${AWSSDK_LINK_LIBRARIES}) From 53f42913022932bf9166d0f6371e746dc1c3da31 Mon Sep 17 00:00:00 2001 From: Horia Mercan Date: Sun, 29 Jun 2025 22:32:46 +0300 Subject: [PATCH 03/12] [aws] Added docker dependencies for opencv --- config/systems.json | 2 +- dockerfiles/aws/cpp/Dockerfile.build | 2 ++ .../aws/cpp/Dockerfile.dependencies-opencv | 15 +++++++++++++++ 3 files changed, 18 insertions(+), 1 deletion(-) create mode 100644 dockerfiles/aws/cpp/Dockerfile.dependencies-opencv diff --git a/config/systems.json b/config/systems.json index 8a7ed472..0dea6fa0 100644 --- a/config/systems.json +++ b/config/systems.json @@ -129,7 +129,7 @@ } }, "dependencies": [ - "runtime", "sdk", "boost", "hiredis" + "runtime", "sdk", "boost", "hiredis", "opencv" ], "versions": ["all"], "images": ["build"], diff --git a/dockerfiles/aws/cpp/Dockerfile.build b/dockerfiles/aws/cpp/Dockerfile.build index d91a4322..5d0b4ca2 100755 --- a/dockerfiles/aws/cpp/Dockerfile.build +++ b/dockerfiles/aws/cpp/Dockerfile.build @@ -4,6 +4,7 @@ ARG BASE_IMAGE FROM ${BASE_REPOSITORY}:dependencies-sdk.aws.cpp.all as sdk FROM ${BASE_REPOSITORY}:dependencies-boost.aws.cpp.all as boost FROM ${BASE_REPOSITORY}:dependencies-hiredis.aws.cpp.all as hiredis +FROM ${BASE_REPOSITORY}:dependencies-opencv.aws.cpp.all as opencv FROM ${BASE_REPOSITORY}:dependencies-runtime.aws.cpp.all as runtime FROM ${BASE_IMAGE} as builder @@ -24,6 +25,7 @@ COPY --from=sdk /opt /opt COPY --from=boost /opt /opt COPY --from=runtime /opt /opt COPY --from=hiredis /opt /opt +COPY --from=opencv /opt/opencv /opt/opencv # useradd and groupmod is installed in /usr/sbin which is not in PATH ENV PATH=/usr/sbin:$PATH diff --git a/dockerfiles/aws/cpp/Dockerfile.dependencies-opencv b/dockerfiles/aws/cpp/Dockerfile.dependencies-opencv new file mode 100644 index 00000000..30efd16f --- /dev/null +++ b/dockerfiles/aws/cpp/Dockerfile.dependencies-opencv @@ -0,0 +1,15 @@ +ARG BASE_IMAGE +FROM ${BASE_IMAGE} as builder +ARG WORKERS +ENV WORKERS=${WORKERS} + + +RUN yum install -y unzip cmake3 curl libcurl libcurl-devel git gcc gcc-c++ make tar gzip zip zlib-devel openssl-devel openssl-static +RUN curl -LO https://github.com/opencv/opencv/archive/refs/tags/4.5.0.zip +RUN unzip 4.5.0.zip +RUN mkdir build && cd build && cmake3 -D CMAKE_BUILD_TYPE=RELEASE -D BUILD_EXAMPLES=Off -D OPENCV_GENERATE_PKGCONFIG=ON -DCMAKE_INSTALL_PREFIX=/opt/opencv -DBUILD_LIST=core,imgproc,imgcodecs ../opencv-4.5.0 +RUN cd build && make -j8 && make install + +FROM ${BASE_IMAGE} + +COPY --from=builder /opt /opt From 4260f45d9ef132ab9eaf392954ba6d3eb95a9628 Mon Sep 17 00:00:00 2001 From: Horia Mercan Date: Sun, 29 Jun 2025 22:32:46 +0300 Subject: [PATCH 04/12] [aws] Added docker dependencies for opencv --- config/systems.json | 2 +- dockerfiles/aws/cpp/Dockerfile.build | 2 ++ .../aws/cpp/Dockerfile.dependencies-opencv | 15 +++++++++++++++ 3 files changed, 18 insertions(+), 1 deletion(-) create mode 100644 dockerfiles/aws/cpp/Dockerfile.dependencies-opencv diff --git a/config/systems.json b/config/systems.json index 8a7ed472..0dea6fa0 100644 --- a/config/systems.json +++ b/config/systems.json @@ -129,7 +129,7 @@ } }, "dependencies": [ - "runtime", "sdk", "boost", "hiredis" + "runtime", "sdk", "boost", "hiredis", "opencv" ], "versions": ["all"], "images": ["build"], diff --git a/dockerfiles/aws/cpp/Dockerfile.build b/dockerfiles/aws/cpp/Dockerfile.build index d91a4322..5d0b4ca2 100755 --- a/dockerfiles/aws/cpp/Dockerfile.build +++ b/dockerfiles/aws/cpp/Dockerfile.build @@ -4,6 +4,7 @@ ARG BASE_IMAGE FROM ${BASE_REPOSITORY}:dependencies-sdk.aws.cpp.all as sdk FROM ${BASE_REPOSITORY}:dependencies-boost.aws.cpp.all as boost FROM ${BASE_REPOSITORY}:dependencies-hiredis.aws.cpp.all as hiredis +FROM ${BASE_REPOSITORY}:dependencies-opencv.aws.cpp.all as opencv FROM ${BASE_REPOSITORY}:dependencies-runtime.aws.cpp.all as runtime FROM ${BASE_IMAGE} as builder @@ -24,6 +25,7 @@ COPY --from=sdk /opt /opt COPY --from=boost /opt /opt COPY --from=runtime /opt /opt COPY --from=hiredis /opt /opt +COPY --from=opencv /opt/opencv /opt/opencv # useradd and groupmod is installed in /usr/sbin which is not in PATH ENV PATH=/usr/sbin:$PATH diff --git a/dockerfiles/aws/cpp/Dockerfile.dependencies-opencv b/dockerfiles/aws/cpp/Dockerfile.dependencies-opencv new file mode 100644 index 00000000..30efd16f --- /dev/null +++ b/dockerfiles/aws/cpp/Dockerfile.dependencies-opencv @@ -0,0 +1,15 @@ +ARG BASE_IMAGE +FROM ${BASE_IMAGE} as builder +ARG WORKERS +ENV WORKERS=${WORKERS} + + +RUN yum install -y unzip cmake3 curl libcurl libcurl-devel git gcc gcc-c++ make tar gzip zip zlib-devel openssl-devel openssl-static +RUN curl -LO https://github.com/opencv/opencv/archive/refs/tags/4.5.0.zip +RUN unzip 4.5.0.zip +RUN mkdir build && cd build && cmake3 -D CMAKE_BUILD_TYPE=RELEASE -D BUILD_EXAMPLES=Off -D OPENCV_GENERATE_PKGCONFIG=ON -DCMAKE_INSTALL_PREFIX=/opt/opencv -DBUILD_LIST=core,imgproc,imgcodecs ../opencv-4.5.0 +RUN cd build && make -j8 && make install + +FROM ${BASE_IMAGE} + +COPY --from=builder /opt /opt From ab33d2ced06c8c5c1c43ab613715d621c0ed8871 Mon Sep 17 00:00:00 2001 From: Horia Mercan Date: Wed, 2 Jul 2025 08:06:35 +0300 Subject: [PATCH 05/12] Delete Base64.hpp file --- .../210.thumbnailer/cpp/Base64.hpp | 126 ------------------ .../210.thumbnailer/cpp/main.cpp | 1 - 2 files changed, 127 deletions(-) delete mode 100644 benchmarks/200.multimedia/210.thumbnailer/cpp/Base64.hpp diff --git a/benchmarks/200.multimedia/210.thumbnailer/cpp/Base64.hpp b/benchmarks/200.multimedia/210.thumbnailer/cpp/Base64.hpp deleted file mode 100644 index ac2b6882..00000000 --- a/benchmarks/200.multimedia/210.thumbnailer/cpp/Base64.hpp +++ /dev/null @@ -1,126 +0,0 @@ - -#ifndef _MACARON_BASE64_H_ -#define _MACARON_BASE64_H_ - -/** - * The MIT License (MIT) - * Copyright (c) 2016 tomykaira - * - * Permission is hereby granted, free of charge, to any person obtaining - * a copy of this software and associated documentation files (the - * "Software"), to deal in the Software without restriction, including - * without limitation the rights to use, copy, modify, merge, publish, - * distribute, sublicense, and/or sell copies of the Software, and to - * permit persons to whom the Software is furnished to do so, subject to - * the following conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE - * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION - * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION - * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ - -#include - -namespace macaron { - -class Base64 { - public: - - static std::string Encode(const std::string data) { - static constexpr char sEncodingTable[] = { - 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', - 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', - 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', - 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', - 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', - 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', - 'w', 'x', 'y', 'z', '0', '1', '2', '3', - '4', '5', '6', '7', '8', '9', '+', '/' - }; - - size_t in_len = data.size(); - size_t out_len = 4 * ((in_len + 2) / 3); - std::string ret(out_len, '\0'); - size_t i; - char *p = const_cast(ret.c_str()); - - for (i = 0; i < in_len - 2; i += 3) { - *p++ = sEncodingTable[(data[i] >> 2) & 0x3F]; - *p++ = sEncodingTable[((data[i] & 0x3) << 4) | ((int) (data[i + 1] & 0xF0) >> 4)]; - *p++ = sEncodingTable[((data[i + 1] & 0xF) << 2) | ((int) (data[i + 2] & 0xC0) >> 6)]; - *p++ = sEncodingTable[data[i + 2] & 0x3F]; - } - if (i < in_len) { - *p++ = sEncodingTable[(data[i] >> 2) & 0x3F]; - if (i == (in_len - 1)) { - *p++ = sEncodingTable[((data[i] & 0x3) << 4)]; - *p++ = '='; - } - else { - *p++ = sEncodingTable[((data[i] & 0x3) << 4) | ((int) (data[i + 1] & 0xF0) >> 4)]; - *p++ = sEncodingTable[((data[i + 1] & 0xF) << 2)]; - } - *p++ = '='; - } - - return ret; - } - - static std::string Decode(const std::string& input, std::string& out) { - static constexpr unsigned char kDecodingTable[] = { - 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, - 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, - 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 62, 64, 64, 64, 63, - 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 64, 64, 64, 64, 64, 64, - 64, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, - 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 64, 64, 64, 64, 64, - 64, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, - 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 64, 64, 64, 64, 64, - 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, - 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, - 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, - 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, - 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, - 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, - 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, - 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64 - }; - - size_t in_len = input.size(); - if (in_len % 4 != 0) return "Input data size is not a multiple of 4"; - - size_t out_len = in_len / 4 * 3; - if (input[in_len - 1] == '=') out_len--; - if (input[in_len - 2] == '=') out_len--; - - out.resize(out_len); - - for (size_t i = 0, j = 0; i < in_len;) { - uint32_t a = input[i] == '=' ? 0 & i++ : kDecodingTable[static_cast(input[i++])]; - uint32_t b = input[i] == '=' ? 0 & i++ : kDecodingTable[static_cast(input[i++])]; - uint32_t c = input[i] == '=' ? 0 & i++ : kDecodingTable[static_cast(input[i++])]; - uint32_t d = input[i] == '=' ? 0 & i++ : kDecodingTable[static_cast(input[i++])]; - - uint32_t triple = (a << 3 * 6) + (b << 2 * 6) + (c << 1 * 6) + (d << 0 * 6); - - if (j < out_len) out[j++] = (triple >> 2 * 8) & 0xFF; - if (j < out_len) out[j++] = (triple >> 1 * 8) & 0xFF; - if (j < out_len) out[j++] = (triple >> 0 * 8) & 0xFF; - } - - return ""; - } - -}; - -} - -#endif /* _MACARON_BASE64_H_ */ - diff --git a/benchmarks/200.multimedia/210.thumbnailer/cpp/main.cpp b/benchmarks/200.multimedia/210.thumbnailer/cpp/main.cpp index 016e5010..0b61ea00 100644 --- a/benchmarks/200.multimedia/210.thumbnailer/cpp/main.cpp +++ b/benchmarks/200.multimedia/210.thumbnailer/cpp/main.cpp @@ -9,7 +9,6 @@ #include #include -#include "Base64.hpp" #include "function.hpp" #include "storage.hpp" #include "utils.hpp" From 29f506c1d05243c5dd0e1d3e97786fc9a828ed08 Mon Sep 17 00:00:00 2001 From: Horia Mercan Date: Wed, 2 Jul 2025 08:25:12 +0300 Subject: [PATCH 06/12] Optimize storage.cpp by using boost's bufferstream --- .../200.multimedia/210.thumbnailer/cpp/main.cpp | 3 ++- benchmarks/wrappers/aws/cpp/storage.cpp | 11 +++++++++-- benchmarks/wrappers/aws/cpp/storage.hpp | 3 ++- 3 files changed, 13 insertions(+), 4 deletions(-) diff --git a/benchmarks/200.multimedia/210.thumbnailer/cpp/main.cpp b/benchmarks/200.multimedia/210.thumbnailer/cpp/main.cpp index 0b61ea00..1e5b76c4 100644 --- a/benchmarks/200.multimedia/210.thumbnailer/cpp/main.cpp +++ b/benchmarks/200.multimedia/210.thumbnailer/cpp/main.cpp @@ -88,7 +88,8 @@ Aws::Utils::Json::JsonValue function(Aws::Utils::Json::JsonView request) Aws::String upload_data(out_buffer.begin(), out_buffer.end()); uint64_t upload_time = client_.upload_random_file( - bucket_name, key_name, true, upload_data); + bucket_name, key_name, true, + reinterpret_cast(out_buffer.data()), out_buffer.size()); if (upload_time == 0) { diff --git a/benchmarks/wrappers/aws/cpp/storage.cpp b/benchmarks/wrappers/aws/cpp/storage.cpp index 9e5a2231..fdb21d73 100644 --- a/benchmarks/wrappers/aws/cpp/storage.cpp +++ b/benchmarks/wrappers/aws/cpp/storage.cpp @@ -84,7 +84,13 @@ std::tuple Storage::download_file( uint64_t Storage::upload_random_file(Aws::String const &bucket, Aws::String const &key, bool report_dl_time, - Aws::String data) { + char * data, + size_t data_size) { + if (data == nullptr || data_size == 0) { + std::cerr << "Error: upload_random_file called with null data or zero size." + << std::endl; + return 0; + } /** * We use Boost's bufferstream to wrap the array as an IOStream. Usign a * light-weight streambuf wrapper, as many solutions (e.g. @@ -93,7 +99,8 @@ uint64_t Storage::upload_random_file(Aws::String const &bucket, * functioning tellp(), etc... (for instance to get the body length). */ const std::shared_ptr input_data = - Aws::MakeShared("LAMBDA_ALLOC", std::move(data)); + std::make_shared( + data, data_size); Aws::S3::Model::PutObjectRequest request; request.WithBucket(bucket).WithKey(key); diff --git a/benchmarks/wrappers/aws/cpp/storage.hpp b/benchmarks/wrappers/aws/cpp/storage.hpp index 0dfad0a4..c7ad3c4b 100644 --- a/benchmarks/wrappers/aws/cpp/storage.hpp +++ b/benchmarks/wrappers/aws/cpp/storage.hpp @@ -35,7 +35,8 @@ class Storage uint64_t upload_random_file(Aws::String const &bucket, Aws::String const &key, bool report_dl_time, - Aws::String data); + char * data, + size_t data_size); }; From 4e2ac2b2db67fc1d50b8fb3204ee8f46cb2f60f3 Mon Sep 17 00:00:00 2001 From: Horia Mercan Date: Tue, 8 Jul 2025 00:34:39 +0300 Subject: [PATCH 07/12] Added cpp support for graph pagerank --- .../501.graph-pagerank/cpp/function.hpp | 56 +++++++++++++++++++ .../501.graph-pagerank/cpp/main.cpp | 44 +++++++++++++++ dockerfiles/aws/cpp/Dockerfile.build | 6 +- .../aws/cpp/Dockerfile.dependencies-igraph | 41 ++++++++++++++ sebs/benchmark.py | 3 + 5 files changed, 149 insertions(+), 1 deletion(-) create mode 100644 benchmarks/500.scientific/501.graph-pagerank/cpp/function.hpp create mode 100644 benchmarks/500.scientific/501.graph-pagerank/cpp/main.cpp create mode 100644 dockerfiles/aws/cpp/Dockerfile.dependencies-igraph diff --git a/benchmarks/500.scientific/501.graph-pagerank/cpp/function.hpp b/benchmarks/500.scientific/501.graph-pagerank/cpp/function.hpp new file mode 100644 index 00000000..d04edd97 --- /dev/null +++ b/benchmarks/500.scientific/501.graph-pagerank/cpp/function.hpp @@ -0,0 +1,56 @@ + +#include + +#include + +#include "utils.hpp" + +igraph_real_t graph_pagerank +(int size, uint64_t seed, uint64_t &graph_generation_time_ms, uint64_t &compute_pr_time_ms) +{ + igraph_t graph; + igraph_vector_t pagerank; + igraph_real_t value; + + igraph_rng_seed(igraph_rng_default(), seed); + { + uint64_t start_time = timeSinceEpochMicrosec(); + igraph_barabasi_game( + /* graph= */ &graph, + /* n= */ size, + /* power= */ 1, + /* m= */ 10, + /* outseq= */ NULL, + /* outpref= */ 0, + /* A= */ 1.0, + /* directed= */ 0, + /* algo= */ IGRAPH_BARABASI_PSUMTREE_MULTIPLE, + /* start_from= */ 0 + ); + graph_generation_time_ms = (timeSinceEpochMicrosec() - start_time); + } + + igraph_vector_init(&pagerank, 0); + { + uint64_t start_time = timeSinceEpochMicrosec(); + igraph_pagerank(&graph, IGRAPH_PAGERANK_ALGO_PRPACK, + &pagerank, &value, + igraph_vss_all(), IGRAPH_DIRECTED, + /* damping */ 0.85, /* weights */ NULL, + NULL /* not needed with PRPACK method */); + compute_pr_time_ms = (timeSinceEpochMicrosec() - start_time); + } + /* Check that the eigenvalue is 1, as expected. */ + if (fabs(value - 1.0) > 32*DBL_EPSILON) { + fprintf(stderr, "PageRank failed to converge.\n"); + return 1; + } + + igraph_real_t result = VECTOR(pagerank)[0]; + + igraph_vector_destroy(&pagerank); + igraph_destroy(&graph); + + return result; +} + diff --git a/benchmarks/500.scientific/501.graph-pagerank/cpp/main.cpp b/benchmarks/500.scientific/501.graph-pagerank/cpp/main.cpp new file mode 100644 index 00000000..02c91fad --- /dev/null +++ b/benchmarks/500.scientific/501.graph-pagerank/cpp/main.cpp @@ -0,0 +1,44 @@ + +#include +#include + +#include "function.hpp" +#include "storage.hpp" +#include "utils.hpp" + +#include +#include +#include +#include +#include // Required for ULLONG_MAX + +Aws::Utils::Json::JsonValue function(Aws::Utils::Json::JsonView request) +{ + Storage client_ = Storage::get_client(); + + auto size = request.GetInteger("size"); + + uint64_t seed; + if (request.ValueExists("seed")) { + seed = request.GetInteger("seed"); + } else { + double random_value = 0.0; + seed = static_cast(random_value * ULLONG_MAX); + } + + uint64_t graph_generation_time_ms; + uint64_t compute_pr_time_ms; + igraph_real_t value = graph_pagerank + (size, seed, graph_generation_time_ms, compute_pr_time_ms); + + Aws::Utils::Json::JsonValue val; + Aws::Utils::Json::JsonValue result; + Aws::Utils::Json::JsonValue measurements; + + measurements.WithInteger("graph_generating_time", graph_generation_time_ms); + measurements.WithInteger("compute_time", compute_pr_time_ms); + + val.WithDouble("value", static_cast(value)); + val.WithObject("measurements", std::move(measurements)); + return val; +} \ No newline at end of file diff --git a/dockerfiles/aws/cpp/Dockerfile.build b/dockerfiles/aws/cpp/Dockerfile.build index 5d0b4ca2..f4080587 100755 --- a/dockerfiles/aws/cpp/Dockerfile.build +++ b/dockerfiles/aws/cpp/Dockerfile.build @@ -4,8 +4,10 @@ ARG BASE_IMAGE FROM ${BASE_REPOSITORY}:dependencies-sdk.aws.cpp.all as sdk FROM ${BASE_REPOSITORY}:dependencies-boost.aws.cpp.all as boost FROM ${BASE_REPOSITORY}:dependencies-hiredis.aws.cpp.all as hiredis -FROM ${BASE_REPOSITORY}:dependencies-opencv.aws.cpp.all as opencv FROM ${BASE_REPOSITORY}:dependencies-runtime.aws.cpp.all as runtime +FROM ${BASE_REPOSITORY}:dependencies-opencv.aws.cpp.all as opencv +FROM ${BASE_REPOSITORY}:dependencies-torch.aws.cpp.all as torch +FROM ${BASE_REPOSITORY}:dependencies-igraph.aws.cpp.all as igraph FROM ${BASE_IMAGE} as builder @@ -26,6 +28,8 @@ COPY --from=boost /opt /opt COPY --from=runtime /opt /opt COPY --from=hiredis /opt /opt COPY --from=opencv /opt/opencv /opt/opencv +COPY --from=torch /opt /opt +COPY --from=igraph /opt /opt # useradd and groupmod is installed in /usr/sbin which is not in PATH ENV PATH=/usr/sbin:$PATH diff --git a/dockerfiles/aws/cpp/Dockerfile.dependencies-igraph b/dockerfiles/aws/cpp/Dockerfile.dependencies-igraph new file mode 100644 index 00000000..9ec62def --- /dev/null +++ b/dockerfiles/aws/cpp/Dockerfile.dependencies-igraph @@ -0,0 +1,41 @@ +ARG BASE_IMAGE + +FROM ${BASE_IMAGE} as builder + +ARG WORKERS +ENV WORKERS=${WORKERS} + +WORKDIR /app/builder + +RUN yum update -y && \ + yum install -y cmake3 wget unzip curl git gcc gcc-c++ \ + make tar gzip libpng-devel zlib-devel \ + libjpeg-turbo-devel python3-devel python3-pip && \ + yum clean all && \ + rm -rf /var/cache/yum + +RUN wget https://github.com/Kitware/CMake/releases/download/v3.25.2/cmake-3.25.2-linux-x86_64.tar.gz && \ + tar -xzf cmake-3.25.2-linux-x86_64.tar.gz && \ + cp -r cmake-3.25.2-linux-x86_64/* /usr/local/ && \ + rm -rf cmake-3.25.2-linux-x86_64* + +# Verify CMake version +RUN cmake --version + +# Download and extract igraph +RUN wget https://github.com/igraph/igraph/releases/download/0.10.15/igraph-0.10.15.tar.gz && \ + tar -xzf igraph-0.10.15.tar.gz + +RUN mkdir build_igraph && \ + cd build_igraph && \ + cmake -DCMAKE_INSTALL_PREFIX=/app/builder/install \ + -DIGRAPH_OPENMP_SUPPORT=OFF \ + ../igraph-0.10.15 && \ + make -j${WORKERS} && \ + make install + +FROM ${BASE_IMAGE} + +WORKDIR /app + +COPY --from=builder /app/builder/install /opt \ No newline at end of file diff --git a/sebs/benchmark.py b/sebs/benchmark.py index 4a2ae902..058d8a63 100644 --- a/sebs/benchmark.py +++ b/sebs/benchmark.py @@ -438,6 +438,9 @@ def add_deployment_package_cpp(self, output_dir): find_package(OpenCV REQUIRED) include_directories(${OpenCV_INCLUDE_DIRS}) target_link_libraries(${PROJECT_NAME} PRIVATE ${OpenCV_LIBS}) + + find_package(igraph REQUIRED) + target_link_libraries(${PROJECT_NAME} PRIVATE igraph::igraph) find_package(AWSSDK COMPONENTS s3 dynamodb core) target_link_libraries(${PROJECT_NAME} PUBLIC ${AWSSDK_LINK_LIBRARIES}) From f447cd322c656901a801100964823d50d2f0a170 Mon Sep 17 00:00:00 2001 From: Horia Mercan Date: Tue, 8 Jul 2025 22:32:15 +0300 Subject: [PATCH 08/12] Added C++ in config for Pagerank Benchmark --- benchmarks/500.scientific/501.graph-pagerank/config.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/benchmarks/500.scientific/501.graph-pagerank/config.json b/benchmarks/500.scientific/501.graph-pagerank/config.json index e80fb435..db8a2363 100644 --- a/benchmarks/500.scientific/501.graph-pagerank/config.json +++ b/benchmarks/500.scientific/501.graph-pagerank/config.json @@ -1,6 +1,6 @@ { "timeout": 120, "memory": 512, - "languages": ["python"], + "languages": ["python", "cpp"], "modules": [] } From 1b525438d0c0012ad2f6fb41c5f3fadc5fcb390c Mon Sep 17 00:00:00 2001 From: Horia Mercan Date: Wed, 9 Jul 2025 00:37:14 +0300 Subject: [PATCH 09/12] Added cpp_dependencies config for building CMakeList.txt for thumbnailer and pagerank --- .../210.thumbnailer/config.json | 3 +- .../501.graph-pagerank/config.json | 3 +- config/systems.json | 2 +- dockerfiles/aws/cpp/Dockerfile.build | 2 - sebs/benchmark.py | 28 +++--- sebs/cpp_dependencies.py | 86 +++++++++++++++++++ 6 files changed, 109 insertions(+), 15 deletions(-) create mode 100644 sebs/cpp_dependencies.py diff --git a/benchmarks/200.multimedia/210.thumbnailer/config.json b/benchmarks/200.multimedia/210.thumbnailer/config.json index aa5677a7..f0c722d8 100644 --- a/benchmarks/200.multimedia/210.thumbnailer/config.json +++ b/benchmarks/200.multimedia/210.thumbnailer/config.json @@ -2,5 +2,6 @@ "timeout": 60, "memory": 256, "languages": ["python", "nodejs", "cpp"], - "modules": ["storage"] + "modules": ["storage"], + "cpp_dependencies": ["opencv", "boost"] } diff --git a/benchmarks/500.scientific/501.graph-pagerank/config.json b/benchmarks/500.scientific/501.graph-pagerank/config.json index db8a2363..90e8c7f8 100644 --- a/benchmarks/500.scientific/501.graph-pagerank/config.json +++ b/benchmarks/500.scientific/501.graph-pagerank/config.json @@ -2,5 +2,6 @@ "timeout": 120, "memory": 512, "languages": ["python", "cpp"], - "modules": [] + "modules": [], + "cpp_dependencies": ["igraph"] } diff --git a/config/systems.json b/config/systems.json index 0dea6fa0..cd23377c 100644 --- a/config/systems.json +++ b/config/systems.json @@ -129,7 +129,7 @@ } }, "dependencies": [ - "runtime", "sdk", "boost", "hiredis", "opencv" + "runtime", "sdk", "boost", "hiredis", "opencv", "igraph" ], "versions": ["all"], "images": ["build"], diff --git a/dockerfiles/aws/cpp/Dockerfile.build b/dockerfiles/aws/cpp/Dockerfile.build index f4080587..0caf6423 100755 --- a/dockerfiles/aws/cpp/Dockerfile.build +++ b/dockerfiles/aws/cpp/Dockerfile.build @@ -6,7 +6,6 @@ FROM ${BASE_REPOSITORY}:dependencies-boost.aws.cpp.all as boost FROM ${BASE_REPOSITORY}:dependencies-hiredis.aws.cpp.all as hiredis FROM ${BASE_REPOSITORY}:dependencies-runtime.aws.cpp.all as runtime FROM ${BASE_REPOSITORY}:dependencies-opencv.aws.cpp.all as opencv -FROM ${BASE_REPOSITORY}:dependencies-torch.aws.cpp.all as torch FROM ${BASE_REPOSITORY}:dependencies-igraph.aws.cpp.all as igraph FROM ${BASE_IMAGE} as builder @@ -28,7 +27,6 @@ COPY --from=boost /opt /opt COPY --from=runtime /opt /opt COPY --from=hiredis /opt /opt COPY --from=opencv /opt/opencv /opt/opencv -COPY --from=torch /opt /opt COPY --from=igraph /opt /opt # useradd and groupmod is installed in /usr/sbin which is not in PATH diff --git a/sebs/benchmark.py b/sebs/benchmark.py index 058d8a63..a9cd67a2 100644 --- a/sebs/benchmark.py +++ b/sebs/benchmark.py @@ -10,6 +10,8 @@ import docker +from sebs.cpp_dependencies import CppDependencies + from sebs.config import SeBSConfig from sebs.cache import Cache from sebs.faas.config import Resources @@ -29,11 +31,13 @@ def __init__( memory: int, languages: List["Language"], modules: List[BenchmarkModule], + cpp_dependencies: Optional[List[CppDependencies]] = None, ): self._timeout = timeout self._memory = memory self._languages = languages self._modules = modules + self._cpp_dependencies = cpp_dependencies or [] @property def timeout(self) -> int: @@ -69,6 +73,9 @@ def deserialize(json_object: dict) -> "BenchmarkConfig": json_object["memory"], [Language.deserialize(x) for x in json_object["languages"]], [BenchmarkModule(x) for x in json_object["modules"]], + cpp_dependencies=[ + CppDependencies.deserialize(x) for x in json_object.get("cpp_dependencies", []) + ], ) @@ -415,6 +422,7 @@ def add_deployment_package_cpp(self, output_dir): # FIXME: Configure CMakeLists.txt dependencies # FIXME: Configure for AWS - this should be generic # FIXME: optional hiredis + cmake_script = """ cmake_minimum_required(VERSION 3.9) set(CMAKE_CXX_STANDARD 11) @@ -430,18 +438,12 @@ def add_deployment_package_cpp(self, output_dir): find_package(aws-lambda-runtime) target_link_libraries(${PROJECT_NAME} PRIVATE AWS::aws-lambda-runtime) - - find_package(Boost REQUIRED) - target_include_directories(${PROJECT_NAME} PRIVATE ${Boost_INCLUDE_DIRS}) - target_link_libraries(${PROJECT_NAME} PRIVATE ${Boost_LIBRARIES}) + """ - find_package(OpenCV REQUIRED) - include_directories(${OpenCV_INCLUDE_DIRS}) - target_link_libraries(${PROJECT_NAME} PRIVATE ${OpenCV_LIBS}) + for dependency in self._benchmark_config._cpp_dependencies: + cmake_script += CppDependencies.to_cmake_list(dependency) - find_package(igraph REQUIRED) - target_link_libraries(${PROJECT_NAME} PRIVATE igraph::igraph) - + cmake_script += """ find_package(AWSSDK COMPONENTS s3 dynamodb core) target_link_libraries(${PROJECT_NAME} PUBLIC ${AWSSDK_LINK_LIBRARIES}) @@ -455,6 +457,12 @@ def add_deployment_package_cpp(self, output_dir): # this line creates a target that packages your binary and zips it up aws_lambda_package_target(${PROJECT_NAME}) """ + + self.logging.info( + "Dependencies for CPP benchmark {benchmark} are " + + str(len(self._benchmark_config._cpp_dependencies)) + " dependencies." + ) + build_script = os.path.join(output_dir, "CMakeLists.txt") with open(build_script, "w") as script_file: script_file.write(textwrap.dedent(cmake_script)) diff --git a/sebs/cpp_dependencies.py b/sebs/cpp_dependencies.py new file mode 100644 index 00000000..b30bdfa1 --- /dev/null +++ b/sebs/cpp_dependencies.py @@ -0,0 +1,86 @@ +from __future__ import annotations +from enum import Enum +from typing import Optional + +class CppDependencyConfig: + def __init__(self, docker_img: str, cmake_package: str, cmake_libs: str, cmake_dir: Optional[str] = None): + self.docker_img = docker_img + self.cmake_package = cmake_package + self.cmake_dir = cmake_dir + self.cmake_libs = cmake_libs + +class CppDependencies(str, Enum): + """ + Enum for C++ dependencies used in the benchmarks. + """ + TORCH = "torch" + OPENCV = "opencv" + IGRAPH = "igraph" + BOOST = "boost" + HIREDIS = "hiredis" + + @staticmethod + def _dependency_dictionary() -> dict[str, CppDependencyConfig]: + return { + CppDependencies.TORCH: CppDependencyConfig( + docker_img="dependencies-torch.aws.cpp.all", + cmake_package="Torch", + cmake_libs="${TORCH_LIBRARIES}", + cmake_dir="${TORCH_INCLUDE_DIRS}" + ), + CppDependencies.OPENCV: CppDependencyConfig( + docker_img="dependencies-opencv.aws.cpp.all", + cmake_package="OpenCV", + cmake_libs="${OpenCV_LIBS}", + cmake_dir="${OpenCV_INCLUDE_DIRS}" + ), + CppDependencies.IGRAPH: CppDependencyConfig( + docker_img="dependencies-igraph.aws.cpp.all", + cmake_package="igraph", + cmake_libs="igraph::igraph" + ), + CppDependencies.BOOST: CppDependencyConfig( + docker_img="dependencies-boost.aws.cpp.all", + cmake_package="Boost", + cmake_libs="${Boost_LIBRARIES}", + cmake_dir="${Boost_INCLUDE_DIRS}" + ), + CppDependencies.HIREDIS: CppDependencyConfig( + docker_img="dependencies-hiredis.aws.cpp.all", + cmake_package="hiredis", + cmake_libs="hiredis::hiredis" + ), + + } + + @staticmethod + def deserialize(val: str) -> CppDependencies: + for member in CppDependencies: + if member.value == val: + return member + raise Exception(f"Unknown C++ dependency type {val}") + + @staticmethod + def to_cmake_list(dependency: CppDependencies) -> str: + """ + Returns the CMake target for the given C++ dependency. + """ + if dependency not in CppDependencies: + raise ValueError(f"Unknown C++ dependency {dependency}") + dependency_config = CppDependencies._dependency_dictionary()[dependency] + return \ + ''' + find_package({cmake_package} REQUIRED) + '''.format( + cmake_package=dependency_config.cmake_package, + ) + ("" if not dependency_config.cmake_dir else \ + ''' + target_include_directories(${{PROJECT_NAME}} PRIVATE {cmake_dir}) + '''.format( + cmake_dir=dependency_config.cmake_dir + )) + \ + ''' + target_link_libraries(${{PROJECT_NAME}} PRIVATE {cmake_libs}) + '''.format( + cmake_libs=dependency_config.cmake_libs + ) From fc82d4abc4c6519754a83e81c9679cdb9a2a764a Mon Sep 17 00:00:00 2001 From: Horia Mercan Date: Wed, 9 Jul 2025 01:30:48 +0300 Subject: [PATCH 10/12] Added graph-bfs benchmark in C++ AWS --- .../500.scientific/503.graph-bfs/config.json | 5 +- .../500.scientific/503.graph-bfs/cpp/main.cpp | 83 +++++++++++++++++++ tools/build_docker_images.py | 41 +-------- 3 files changed, 89 insertions(+), 40 deletions(-) create mode 100644 benchmarks/500.scientific/503.graph-bfs/cpp/main.cpp diff --git a/benchmarks/500.scientific/503.graph-bfs/config.json b/benchmarks/500.scientific/503.graph-bfs/config.json index e80fb435..90e8c7f8 100644 --- a/benchmarks/500.scientific/503.graph-bfs/config.json +++ b/benchmarks/500.scientific/503.graph-bfs/config.json @@ -1,6 +1,7 @@ { "timeout": 120, "memory": 512, - "languages": ["python"], - "modules": [] + "languages": ["python", "cpp"], + "modules": [], + "cpp_dependencies": ["igraph"] } diff --git a/benchmarks/500.scientific/503.graph-bfs/cpp/main.cpp b/benchmarks/500.scientific/503.graph-bfs/cpp/main.cpp new file mode 100644 index 00000000..a160d2d0 --- /dev/null +++ b/benchmarks/500.scientific/503.graph-bfs/cpp/main.cpp @@ -0,0 +1,83 @@ +#include +#include + +#include +#include +#include +#include + +#include "utils.hpp" + +Aws::Utils::Json::JsonValue function(Aws::Utils::Json::JsonView request) +{ + int size = request.GetInteger("size"); + + uint64_t seed; + if (request.ValueExists("seed")) { + seed = request.GetInteger("seed"); + igraph_rng_seed(igraph_rng_default(), seed); + } else { + std::random_device rd; + seed = rd(); + } + igraph_rng_seed(igraph_rng_default(), seed); + + auto graph_gen_start = timeSinceEpochMicrosec(); + igraph_t graph; + igraph_barabasi_game( + &graph, + size, + 1, // power + 10, // m + nullptr,// outseq + 0, // outpref + 1.0, // A + 0, // directed + IGRAPH_BARABASI_PSUMTREE_MULTIPLE, + 0 // start_from + ); + auto graph_gen_end = timeSinceEpochMicrosec(); + + // Measure BFS time + auto bfs_start = timeSinceEpochMicrosec(); + igraph_vector_int_t order; + igraph_vector_int_init(&order, 0); + // Documentation: https://igraph.org/c/pdf/0.9.7/igraph-docs.pdf + igraph_bfs( + &graph, + 0, // root vertex + nullptr, // roots + IGRAPH_ALL, // neimode + true, // unreachable + nullptr, // restricted + &order, // order + nullptr, // rank + nullptr, // father + nullptr, // pred + nullptr, //succ, + nullptr, // dist + nullptr, // callback + nullptr // extra + ); + auto bfs_end = timeSinceEpochMicrosec(); + + // Calculate times in microseconds + auto graph_generating_time = graph_gen_end - graph_gen_start; + auto process_time =bfs_end - bfs_start; + + Aws::Utils::Json::JsonValue result; + + igraph_real_t bfs_result = VECTOR(order)[0]; + result.WithDouble("result", static_cast(bfs_result)); + + Aws::Utils::Json::JsonValue measurement; + measurement.WithInt64("graph_generating_time", graph_generating_time); + measurement.WithInt64("compute_time", process_time); + + result.WithObject("measurement", std::move(measurement)); + + igraph_vector_int_destroy(&order); + igraph_destroy(&graph); + + return result; +} \ No newline at end of file diff --git a/tools/build_docker_images.py b/tools/build_docker_images.py index e4db1508..48093ad2 100755 --- a/tools/build_docker_images.py +++ b/tools/build_docker_images.py @@ -45,18 +45,14 @@ def build(image_type, system, language=None, version=None, version_name=None): if version: target += "." + version sebs_version = config["general"].get("SeBS_version", "unknown") - target += "-" + sebs_version + target += "-" + sebs_version # This should not be appended for dependencies' images # if we pass an integer, the build will fail with 'connection reset by peer' -<<<<<<< HEAD - buildargs = {"VERSION": version, "WORKERS": str(args.parallel)} -======= buildargs = { "VERSION": version, 'WORKERS': str(args.parallel), 'BASE_REPOSITORY': config["general"]["docker_repository"] } ->>>>>>> a9f3c27 ([aws][system] Add C++ dependencies images) if version: buildargs["BASE_IMAGE"] = version_name print( @@ -106,49 +102,18 @@ def build_systems(system, system_config): if args.language: if "dependencies" in system_config["languages"][args.language]: language_config = system_config["languages"][args.language] -<<<<<<< HEAD - # for all dependencies - if args.type_tag: - # for all image versions - for version, base_image in language_config["base_images"].items(): - build( - f"{args.type}.{args.type_tag}", - system, - args.language, - version, - base_image, - ) - else: - for dep in system_config["languages"][args.language][ - "dependencies" - ]: - # for all image versions - for version, base_image in language_config[ - "base_images" - ].items(): - build( - f"{args.type}.{dep}", - system, - args.language, - version, - base_image, - ) - else: - raise RuntimeError("Language must be specified for dependencies") -======= # for all dependencies if args.type_tag: # for all image versions - for version, base_image in language_config["base_images"].items(): + for version, base_image in language_config["base_images"]['x64'].items(): build(f"{args.type}-{args.type_tag}", system, args.language, version, base_image) else: for dep in system_config["languages"][args.language]["dependencies"]: # for all image versions - for version, base_image in language_config["base_images"].items(): + for version, base_image in language_config["base_images"]['x64'].items(): build(f"{args.type}-{dep}", system, args.language, version, base_image) else: raise RuntimeError('Language must be specified for dependencies') ->>>>>>> a9f3c27 ([aws][system] Add C++ dependencies images) else: if args.language: build_language( From 83c54672e77b81827e03d99d2de3806abd1ff9f2 Mon Sep 17 00:00:00 2001 From: Horia Mercan Date: Fri, 11 Jul 2025 00:54:59 +0300 Subject: [PATCH 11/12] Added Torch & TorchVIsion dependencies to C++. Added image-recognition benchmark logic. Add namespace for storage.hpp to avoid name collisions --- .../210.thumbnailer/cpp/main.cpp | 2 +- .../411.image-recognition/config.json | 5 +- .../411.image-recognition/cpp/main.cpp | 122 ++++++++++++++++++ .../501.graph-pagerank/cpp/main.cpp | 2 +- benchmarks/wrappers/aws/cpp/storage.cpp | 10 +- benchmarks/wrappers/aws/cpp/storage.hpp | 4 + config/systems.json | 2 +- dockerfiles/aws/cpp/Dockerfile.build | 11 ++ .../aws/cpp/Dockerfile.dependencies-torch | 51 ++++++++ sebs/benchmark.py | 5 +- sebs/cpp_dependencies.py | 2 +- tools/build_docker_images.py | 3 +- 12 files changed, 205 insertions(+), 14 deletions(-) create mode 100644 benchmarks/400.inference/411.image-recognition/cpp/main.cpp create mode 100644 dockerfiles/aws/cpp/Dockerfile.dependencies-torch diff --git a/benchmarks/200.multimedia/210.thumbnailer/cpp/main.cpp b/benchmarks/200.multimedia/210.thumbnailer/cpp/main.cpp index 1e5b76c4..9f3ccebd 100644 --- a/benchmarks/200.multimedia/210.thumbnailer/cpp/main.cpp +++ b/benchmarks/200.multimedia/210.thumbnailer/cpp/main.cpp @@ -15,7 +15,7 @@ Aws::Utils::Json::JsonValue function(Aws::Utils::Json::JsonView request) { - Storage client_ = Storage::get_client(); + sebs::Storage client_ = sebs::Storage::get_client(); auto bucket_obj = request.GetObject("bucket"); if (!bucket_obj.IsObject()) diff --git a/benchmarks/400.inference/411.image-recognition/config.json b/benchmarks/400.inference/411.image-recognition/config.json index 94ede792..56db1084 100644 --- a/benchmarks/400.inference/411.image-recognition/config.json +++ b/benchmarks/400.inference/411.image-recognition/config.json @@ -1,6 +1,7 @@ { "timeout": 60, "memory": 512, - "languages": ["python"], - "modules": ["storage"] + "languages": ["python", "cpp"], + "modules": ["storage"], + "cpp_dependencies": ["torch", "opencv"] } diff --git a/benchmarks/400.inference/411.image-recognition/cpp/main.cpp b/benchmarks/400.inference/411.image-recognition/cpp/main.cpp new file mode 100644 index 00000000..4e6afb9a --- /dev/null +++ b/benchmarks/400.inference/411.image-recognition/cpp/main.cpp @@ -0,0 +1,122 @@ +#include +#include + +#include +#include +#include + +#include +#include + +#include "utils.hpp" +#include "storage.hpp" + +#include +#include + +#include +#include +#include +#include + + +#define kIMAGE_SIZE 224 +#define kCHANNELS 3 +#define kTOP_K 3 + + +bool load_image(cv::Mat &image) +{ + + // image = cv::imread(file_name); // CV_8UC3 + if (image.empty() || !image.data) + { + return false; + } + cv::cvtColor(image, image, cv::COLOR_BGR2RGB); + + int w = image.size().width, h = image.size().height; + cv::Size scale((int)256 * ((float)w) / h, 256); + cv::resize(image, image, scale); + w = image.size().width, h = image.size().height; + image = image(cv::Range(16, 240), cv::Range(80, 304)); + image.convertTo(image, CV_32FC3, 1.0f / 255.0f); + + return true; +} + +int recognition(cv::Mat &image) +{ + + static bool initialized = false; + static torch::jit::script::Module module; + if (!initialized) + { + try + { + std::cout << "Initialize ResNet50 model" << std::endl; + module = torch::jit::load("./resnet50.pt"); + } + catch (const c10::Error &e) + { + std::cerr << "error loading the model\n"; + return -1; + } + initialized = true; + } + + if (load_image(image)) + { + + auto input_tensor = torch::from_blob( + image.data, {1, kIMAGE_SIZE, kIMAGE_SIZE, kCHANNELS}); + input_tensor = input_tensor.permute({0, 3, 1, 2}); + // input_tensor = input_tensor.permute({0, 1, 2, 3}); + input_tensor[0][0] = input_tensor[0][0].sub_(0.485).div_(0.229); + input_tensor[0][1] = input_tensor[0][1].sub_(0.456).div_(0.224); + input_tensor[0][2] = input_tensor[0][2].sub_(0.406).div_(0.225); + + torch::Tensor out_tensor = module.forward({input_tensor}).toTensor(); + auto results = out_tensor.sort(-1, true); + auto softmaxs = std::get<0>(results)[0].softmax(0); + auto indexs = std::get<1>(results)[0]; + + std::cout << indexs[0].item() << " " << softmaxs[0].item() << std::endl; + return indexs[0].item(); + // for(int i = 0; i < 100; ++i) + // std::cout << softmaxs[i].item() << ' '; + // std::cout << '\n'; + // for(int i = 0; i < 100; ++i) + // std::cout << indexs[i].item() << ' '; + // std::cout << '\n'; + } + + return -1; +} + +Aws::Utils::Json::JsonValue function(Aws::Utils::Json::JsonView request) +{ + sebs::Storage client_ = sebs::Storage::get_client(); + + auto bucket_obj = request.GetObject("bucket"); + if (!bucket_obj.IsObject()) + { + Aws::Utils::Json::JsonValue error; + error.WithString("error", "Bucket object is not valid."); + return error; + } + + auto bucket_name = bucket_obj.GetString("bucket"); + auto input_prefix = bucket_obj.GetString("input"); + auto model_prefix = bucket_obj.GetString("model"); + auto key = request.GetObject("object").GetString("input"); + auto model_key = request.GetObject("object").GetString("model"); + + + + Aws::Utils::Json::JsonValue val; + Aws::Utils::Json::JsonValue result; + Aws::Utils::Json::JsonValue measurements; + + return val; +} \ No newline at end of file diff --git a/benchmarks/500.scientific/501.graph-pagerank/cpp/main.cpp b/benchmarks/500.scientific/501.graph-pagerank/cpp/main.cpp index 02c91fad..401f35b5 100644 --- a/benchmarks/500.scientific/501.graph-pagerank/cpp/main.cpp +++ b/benchmarks/500.scientific/501.graph-pagerank/cpp/main.cpp @@ -14,7 +14,7 @@ Aws::Utils::Json::JsonValue function(Aws::Utils::Json::JsonView request) { - Storage client_ = Storage::get_client(); + sebs::Storage client_ = sebs::Storage::get_client(); auto size = request.GetInteger("size"); diff --git a/benchmarks/wrappers/aws/cpp/storage.cpp b/benchmarks/wrappers/aws/cpp/storage.cpp index fdb21d73..3926bd00 100644 --- a/benchmarks/wrappers/aws/cpp/storage.cpp +++ b/benchmarks/wrappers/aws/cpp/storage.cpp @@ -13,17 +13,17 @@ #include "storage.hpp" #include "utils.hpp" -Storage Storage::get_client() { +sebs::Storage sebs::Storage::get_client() { Aws::Client::ClientConfiguration config; char const TAG[] = "LAMBDA_ALLOC"; auto credentialsProvider = Aws::MakeShared(TAG); - Aws::S3::S3Client client(credentialsProvider, config); + Aws::S3::S3Client client(credentialsProvider, nullptr, config); return Storage(std::move(client)); } -uint64_t Storage::download_file(Aws::String const &bucket, +uint64_t sebs::Storage::download_file(Aws::String const &bucket, Aws::String const &key, int &required_retries, bool report_dl_time, Aws::IOStream &output_stream) { @@ -61,7 +61,7 @@ uint64_t Storage::download_file(Aws::String const &bucket, return 0; } -std::tuple Storage::download_file( +std::tuple sebs::Storage::download_file( Aws::String const &bucket, Aws::String const &key) { Aws::S3::Model::GetObjectRequest request; request.WithBucket(bucket).WithKey(key); @@ -81,7 +81,7 @@ std::tuple Storage::download_file( return {content, finishedTime - bef}; } -uint64_t Storage::upload_random_file(Aws::String const &bucket, +uint64_t sebs::Storage::upload_random_file(Aws::String const &bucket, Aws::String const &key, bool report_dl_time, char * data, diff --git a/benchmarks/wrappers/aws/cpp/storage.hpp b/benchmarks/wrappers/aws/cpp/storage.hpp index c7ad3c4b..d0253fff 100644 --- a/benchmarks/wrappers/aws/cpp/storage.hpp +++ b/benchmarks/wrappers/aws/cpp/storage.hpp @@ -4,6 +4,8 @@ #include #include +namespace sebs { + class Storage { public: @@ -40,3 +42,5 @@ class Storage }; +}; + diff --git a/config/systems.json b/config/systems.json index cd23377c..e75f4d52 100644 --- a/config/systems.json +++ b/config/systems.json @@ -129,7 +129,7 @@ } }, "dependencies": [ - "runtime", "sdk", "boost", "hiredis", "opencv", "igraph" + "runtime", "sdk", "boost", "hiredis", "opencv", "igraph", "torch" ], "versions": ["all"], "images": ["build"], diff --git a/dockerfiles/aws/cpp/Dockerfile.build b/dockerfiles/aws/cpp/Dockerfile.build index 0caf6423..66079e52 100755 --- a/dockerfiles/aws/cpp/Dockerfile.build +++ b/dockerfiles/aws/cpp/Dockerfile.build @@ -1,12 +1,14 @@ ARG BASE_REPOSITORY ARG BASE_IMAGE + FROM ${BASE_REPOSITORY}:dependencies-sdk.aws.cpp.all as sdk FROM ${BASE_REPOSITORY}:dependencies-boost.aws.cpp.all as boost FROM ${BASE_REPOSITORY}:dependencies-hiredis.aws.cpp.all as hiredis FROM ${BASE_REPOSITORY}:dependencies-runtime.aws.cpp.all as runtime FROM ${BASE_REPOSITORY}:dependencies-opencv.aws.cpp.all as opencv FROM ${BASE_REPOSITORY}:dependencies-igraph.aws.cpp.all as igraph +FROM ${BASE_REPOSITORY}:dependencies-torch.aws.cpp.all as torch FROM ${BASE_IMAGE} as builder @@ -19,6 +21,11 @@ RUN curl -o /usr/local/bin/gosu -SL "https://github.com/tianon/gosu/releases/dow RUN mkdir -p /sebs/ COPY dockerfiles/entrypoint.sh /sebs/entrypoint.sh COPY dockerfiles/cpp_installer.sh /sebs/installer.sh + +# Set environment variables for LibTorch +ENV LD_LIBRARY_PATH=/opt/libtorch/lib:$LD_LIBRARY_PATH +ENV CMAKE_PREFIX_PATH=/opt/libtorch:$CMAKE_PREFIX_PATH + RUN chmod +x /sebs/entrypoint.sh RUN chmod +x /sebs/installer.sh @@ -28,6 +35,10 @@ COPY --from=runtime /opt /opt COPY --from=hiredis /opt /opt COPY --from=opencv /opt/opencv /opt/opencv COPY --from=igraph /opt /opt +COPY --from=torch /opt /opt + +# Ensure libtorch.so symlink exists for runtime compatibility +RUN ln -sf /opt/libtorch/lib/libtorch_cpu.so /opt/libtorch/lib/libtorch.so # useradd and groupmod is installed in /usr/sbin which is not in PATH ENV PATH=/usr/sbin:$PATH diff --git a/dockerfiles/aws/cpp/Dockerfile.dependencies-torch b/dockerfiles/aws/cpp/Dockerfile.dependencies-torch new file mode 100644 index 00000000..817cac1d --- /dev/null +++ b/dockerfiles/aws/cpp/Dockerfile.dependencies-torch @@ -0,0 +1,51 @@ +ARG BASE_IMAGE + +FROM ${BASE_IMAGE} as builder + +ARG WORKERS +ENV WORKERS=${WORKERS} + +WORKDIR /app/builder + +RUN yum update -y && \ + yum install -y cmake3 wget unzip curl git gcc gcc-c++ \ + make tar gzip libpng-devel zlib-devel \ + libjpeg-turbo-devel python3-devel python3-pip && \ + yum clean all && \ + rm -rf /var/cache/yum + +# Download and extract libtorch. +RUN curl -L -o torchlib.zip https://download.pytorch.org/libtorch/cpu/libtorch-cxx11-abi-shared-with-deps-1.7.0%2Bcpu.zip +RUN unzip -q torchlib.zip +RUN rm torchlib.zip + +# Move libtorch directly to /opt +RUN mv libtorch /opt/libtorch + + +# Set environment variables for LibTorch +ENV LD_LIBRARY_PATH=/opt/libtorch/lib:$LD_LIBRARY_PATH +ENV CMAKE_PREFIX_PATH=/opt/libtorch:$CMAKE_PREFIX_PATH + +ENV TORCHVISION_VERSION=0.8.0 + +# Download and extract torchvision source. +RUN curl -LO https://github.com/pytorch/vision/archive/refs/tags/v${TORCHVISION_VERSION}.zip && \ + unzip -q v${TORCHVISION_VERSION}.zip && \ + rm v${TORCHVISION_VERSION}.zip + +# Build and install torchvision directly to /opt +RUN mkdir build_torchvision && \ + cd build_torchvision && \ + cmake3 \ + -D CMAKE_BUILD_TYPE=RELEASE \ + -DCMAKE_INSTALL_PREFIX=/opt \ + -DCMAKE_PREFIX_PATH=/opt/libtorch/share/cmake \ + ../vision-${TORCHVISION_VERSION} && \ + make -j${WORKERS} && \ + make install + +FROM ${BASE_IMAGE} + +# Copy everything from /opt in the builder to /opt in the final image +COPY --from=builder /opt /opt \ No newline at end of file diff --git a/sebs/benchmark.py b/sebs/benchmark.py index a9cd67a2..1d8781dd 100644 --- a/sebs/benchmark.py +++ b/sebs/benchmark.py @@ -425,7 +425,8 @@ def add_deployment_package_cpp(self, output_dir): cmake_script = """ cmake_minimum_required(VERSION 3.9) - set(CMAKE_CXX_STANDARD 11) + set(CMAKE_CXX_STANDARD 14) + set(CMAKE_CXX_FLAGS "-Os") project(benchmark LANGUAGES CXX) add_executable( ${PROJECT_NAME} "handler.cpp" "key-value.cpp" @@ -433,7 +434,7 @@ def add_deployment_package_cpp(self, output_dir): ) target_include_directories(${PROJECT_NAME} PRIVATE ".") - target_compile_features(${PROJECT_NAME} PRIVATE "cxx_std_11") + target_compile_features(${PROJECT_NAME} PRIVATE "cxx_std_14") target_compile_options(${PROJECT_NAME} PRIVATE "-Wall" "-Wextra") find_package(aws-lambda-runtime) diff --git a/sebs/cpp_dependencies.py b/sebs/cpp_dependencies.py index b30bdfa1..43b2a451 100644 --- a/sebs/cpp_dependencies.py +++ b/sebs/cpp_dependencies.py @@ -25,7 +25,7 @@ def _dependency_dictionary() -> dict[str, CppDependencyConfig]: CppDependencies.TORCH: CppDependencyConfig( docker_img="dependencies-torch.aws.cpp.all", cmake_package="Torch", - cmake_libs="${TORCH_LIBRARIES}", + cmake_libs="${TORCH_LIBRARIES} -lm", cmake_dir="${TORCH_INCLUDE_DIRS}" ), CppDependencies.OPENCV: CppDependencyConfig( diff --git a/tools/build_docker_images.py b/tools/build_docker_images.py index 48093ad2..65a67e59 100755 --- a/tools/build_docker_images.py +++ b/tools/build_docker_images.py @@ -45,7 +45,8 @@ def build(image_type, system, language=None, version=None, version_name=None): if version: target += "." + version sebs_version = config["general"].get("SeBS_version", "unknown") - target += "-" + sebs_version # This should not be appended for dependencies' images + if not (args.type == "dependencies"): + target += "-" + sebs_version # This should not be appended for dependencies' images # if we pass an integer, the build will fail with 'connection reset by peer' buildargs = { From a0e545342dacb352652839612d14284ee1eb6d9c Mon Sep 17 00:00:00 2001 From: Horia Mercan Date: Tue, 14 Oct 2025 01:55:27 +0300 Subject: [PATCH 12/12] [lint] Remove dead code --- .../400.inference/411.image-recognition/cpp/main.cpp | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/benchmarks/400.inference/411.image-recognition/cpp/main.cpp b/benchmarks/400.inference/411.image-recognition/cpp/main.cpp index 4e6afb9a..4d5bf9b0 100644 --- a/benchmarks/400.inference/411.image-recognition/cpp/main.cpp +++ b/benchmarks/400.inference/411.image-recognition/cpp/main.cpp @@ -28,7 +28,6 @@ bool load_image(cv::Mat &image) { - // image = cv::imread(file_name); // CV_8UC3 if (image.empty() || !image.data) { return false; @@ -71,7 +70,7 @@ int recognition(cv::Mat &image) auto input_tensor = torch::from_blob( image.data, {1, kIMAGE_SIZE, kIMAGE_SIZE, kCHANNELS}); input_tensor = input_tensor.permute({0, 3, 1, 2}); - // input_tensor = input_tensor.permute({0, 1, 2, 3}); + input_tensor[0][0] = input_tensor[0][0].sub_(0.485).div_(0.229); input_tensor[0][1] = input_tensor[0][1].sub_(0.456).div_(0.224); input_tensor[0][2] = input_tensor[0][2].sub_(0.406).div_(0.225); @@ -83,12 +82,8 @@ int recognition(cv::Mat &image) std::cout << indexs[0].item() << " " << softmaxs[0].item() << std::endl; return indexs[0].item(); - // for(int i = 0; i < 100; ++i) - // std::cout << softmaxs[i].item() << ' '; - // std::cout << '\n'; - // for(int i = 0; i < 100; ++i) - // std::cout << indexs[i].item() << ' '; - // std::cout << '\n'; + + } return -1; @@ -112,7 +107,6 @@ Aws::Utils::Json::JsonValue function(Aws::Utils::Json::JsonView request) auto key = request.GetObject("object").GetString("input"); auto model_key = request.GetObject("object").GetString("model"); - Aws::Utils::Json::JsonValue val; Aws::Utils::Json::JsonValue result;