From 8b0e40f5a480a4f042744e24aa0ee07b1019c970 Mon Sep 17 00:00:00 2001 From: Rafi Indra Pramudhito Zuhayr Date: Tue, 15 Sep 2026 08:49:13 +0700 Subject: [PATCH 01/15] build: harden benchmark dependency setup --- cmake/packages/google-benchmark.cmake | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/cmake/packages/google-benchmark.cmake b/cmake/packages/google-benchmark.cmake index e6cd436..e7f03c3 100644 --- a/cmake/packages/google-benchmark.cmake +++ b/cmake/packages/google-benchmark.cmake @@ -2,11 +2,17 @@ find_package(benchmark CONFIG QUIET) if(NOT benchmark_FOUND) include(FetchContent) + + set(BENCHMARK_ENABLE_TESTING OFF CACHE BOOL "" FORCE) + set(BENCHMARK_ENABLE_GTEST_TESTS OFF CACHE BOOL "" FORCE) + set(BENCHMARK_ENABLE_INSTALL OFF CACHE BOOL "" FORCE) + set(BENCHMARK_ENABLE_WERROR OFF CACHE BOOL "" FORCE) + FetchContent_Declare( googlebenchmark GIT_REPOSITORY https://github.com/google/benchmark.git GIT_TAG v1.9.5 + GIT_SHALLOW TRUE ) - set(BENCHMARK_ENABLE_TEST OFF CACHE BOOL "" FORCE) FetchContent_MakeAvailable(googlebenchmark) -endif() \ No newline at end of file +endif() From 0ef8ddc4b939915776daa088f7b006b3e72f93ae Mon Sep 17 00:00:00 2001 From: Rafi Indra Pramudhito Zuhayr Date: Tue, 15 Sep 2026 08:49:33 +0700 Subject: [PATCH 02/15] build: make benchmarks opt-in --- CMakeLists.txt | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index a23429b..6be50b9 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -11,6 +11,8 @@ set(CMAKE_CXX_STANDARD_REQUIRED ON) set(CMAKE_CXX_EXTENSIONS OFF) set(CMAKE_EXPORT_COMPILE_COMMANDS ON) +option(CPP_REQUEST_BUILD_BENCHMARKS "Build cpp_request benchmarks" OFF) + include(CTest) include(GNUInstallDirs) include(CMakePackageConfigHelpers) @@ -23,7 +25,9 @@ add_subdirectory(src) if(BUILD_TESTING) add_subdirectory(tests) endif() -add_subdirectory(benchmarks) +if(CPP_REQUEST_BUILD_BENCHMARKS) + add_subdirectory(benchmarks) +endif() add_subdirectory(examples) set(CPP_REQUEST_INSTALL_CMAKEDIR "${CMAKE_INSTALL_LIBDIR}/cmake/cpp_request") From 8d023da03aad1dc3b396ae6b9fbb27f700bc25ff Mon Sep 17 00:00:00 2001 From: Rafi Indra Pramudhito Zuhayr Date: Tue, 15 Sep 2026 08:49:43 +0700 Subject: [PATCH 03/15] bench: add v0.9 benchmark targets --- benchmarks/CMakeLists.txt | 52 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 52 insertions(+) diff --git a/benchmarks/CMakeLists.txt b/benchmarks/CMakeLists.txt index e69de29..8280484 100644 --- a/benchmarks/CMakeLists.txt +++ b/benchmarks/CMakeLists.txt @@ -0,0 +1,52 @@ +include(${PROJECT_SOURCE_DIR}/cmake/packages/google-benchmark.cmake) +find_package(Threads REQUIRED) + +function(cpp_request_add_benchmark target source) + add_executable(${target} ${source}) + target_include_directories(${target} + PRIVATE + ${PROJECT_SOURCE_DIR}/src + ${CMAKE_CURRENT_SOURCE_DIR} + ) + target_link_libraries(${target} + PRIVATE + cpp_request::internal + benchmark::benchmark_main + Threads::Threads + ) + target_compile_features(${target} PRIVATE cxx_std_17) + + if(WIN32) + target_link_libraries(${target} PRIVATE ws2_32) + endif() +endfunction() + +cpp_request_add_benchmark( + cpp_request_bench_request_serializer + request_serializer_benchmark.cpp +) +cpp_request_add_benchmark( + cpp_request_bench_response_parser + response_parser_benchmark.cpp +) +cpp_request_add_benchmark( + cpp_request_bench_chunked_decoder + chunked_decoder_benchmark.cpp +) +cpp_request_add_benchmark( + cpp_request_bench_loopback_client + loopback_client_benchmark.cpp +) +cpp_request_add_benchmark( + cpp_request_bench_connection_reuse + connection_reuse_benchmark.cpp +) + +add_custom_target(cpp_request_benchmarks + DEPENDS + cpp_request_bench_request_serializer + cpp_request_bench_response_parser + cpp_request_bench_chunked_decoder + cpp_request_bench_loopback_client + cpp_request_bench_connection_reuse +) From 0a1004895d48a1c41d0246efb46db8b9f6697378 Mon Sep 17 00:00:00 2001 From: Rafi Indra Pramudhito Zuhayr Date: Tue, 15 Sep 2026 08:49:53 +0700 Subject: [PATCH 04/15] bench: add request serializer benchmark --- benchmarks/request_serializer_benchmark.cpp | 47 +++++++++++++++++++++ 1 file changed, 47 insertions(+) create mode 100644 benchmarks/request_serializer_benchmark.cpp diff --git a/benchmarks/request_serializer_benchmark.cpp b/benchmarks/request_serializer_benchmark.cpp new file mode 100644 index 0000000..a733d78 --- /dev/null +++ b/benchmarks/request_serializer_benchmark.cpp @@ -0,0 +1,47 @@ +#include + +#include "http/request_serializer.hpp" + +#include + +#include +#include +#include + +namespace { + +void BM_RequestSerializer(benchmark::State& state) { + const std::string body(static_cast(state.range(0)), 'x'); + + cpp_request::Request request{ + cpp_request::Method::Post, + "http://127.0.0.1:8080/api/v1/items?existing=1"}; + request.headers().add("Accept", "application/json"); + request.headers().add("Content-Type", "application/json"); + request.headers().add("X-Request-Id", "benchmark-request"); + request.add_query_param("search", "low level C++"); + request.add_query_param("tag", "network/http"); + request.set_body(body); + + for (auto _ : state) { + auto result = cpp_request::detail::http::serialize_request(request); + if (!result) { + state.SkipWithError("request serialization failed"); + break; + } + + benchmark::DoNotOptimize(result.value().head.data()); + benchmark::DoNotOptimize(result.value().body.data()); + benchmark::ClobberMemory(); + } + + state.SetBytesProcessed( + state.iterations() * static_cast(body.size())); +} + +BENCHMARK(BM_RequestSerializer) + ->Arg(0) + ->Arg(1024) + ->Arg(16 * 1024); + +} // namespace From 675fe2b7322b64f04924c73ba85ac64dc547c803 Mon Sep 17 00:00:00 2001 From: Rafi Indra Pramudhito Zuhayr Date: Tue, 15 Sep 2026 08:50:02 +0700 Subject: [PATCH 05/15] bench: add response parser benchmark --- benchmarks/response_parser_benchmark.cpp | 54 ++++++++++++++++++++++++ 1 file changed, 54 insertions(+) create mode 100644 benchmarks/response_parser_benchmark.cpp diff --git a/benchmarks/response_parser_benchmark.cpp b/benchmarks/response_parser_benchmark.cpp new file mode 100644 index 0000000..94b711b --- /dev/null +++ b/benchmarks/response_parser_benchmark.cpp @@ -0,0 +1,54 @@ +#include + +#include "http/response_parser.hpp" + +#include + +#include +#include + +namespace { + +std::string make_response(std::size_t body_size) { + std::string response = + "HTTP/1.1 200 OK\r\n" + "Content-Type: application/json\r\n" + "Cache-Control: no-cache\r\n" + "Server: cpp-request-benchmark\r\n"; + + for (int index = 0; index < 24; ++index) { + response += "X-Benchmark-" + std::to_string(index) + + ": value-" + std::to_string(index) + "\r\n"; + } + + response += "Content-Length: " + std::to_string(body_size) + "\r\n\r\n"; + response.append(body_size, 'x'); + return response; +} + +void BM_ResponseParser(benchmark::State& state) { + const auto response = make_response(static_cast(state.range(0))); + + for (auto _ : state) { + cpp_request::detail::http::ResponseParser parser{cpp_request::Method::Get}; + auto result = parser.feed(response); + if (!result || !parser.complete()) { + state.SkipWithError("response parsing failed"); + break; + } + + benchmark::DoNotOptimize(parser.response().status_code()); + benchmark::DoNotOptimize(parser.response().body().data()); + benchmark::ClobberMemory(); + } + + state.SetBytesProcessed( + state.iterations() * static_cast(response.size())); +} + +BENCHMARK(BM_ResponseParser) + ->Arg(0) + ->Arg(4 * 1024) + ->Arg(64 * 1024); + +} // namespace From fda760cf9274b80d9052d2247719c06a0d4dd934 Mon Sep 17 00:00:00 2001 From: Rafi Indra Pramudhito Zuhayr Date: Tue, 15 Sep 2026 08:50:16 +0700 Subject: [PATCH 06/15] bench: add chunked decoder benchmark --- benchmarks/chunked_decoder_benchmark.cpp | 63 ++++++++++++++++++++++++ 1 file changed, 63 insertions(+) create mode 100644 benchmarks/chunked_decoder_benchmark.cpp diff --git a/benchmarks/chunked_decoder_benchmark.cpp b/benchmarks/chunked_decoder_benchmark.cpp new file mode 100644 index 0000000..60eb8f2 --- /dev/null +++ b/benchmarks/chunked_decoder_benchmark.cpp @@ -0,0 +1,63 @@ +#include + +#include "http/chunked_decoder.hpp" + +#include +#include +#include + +namespace { + +std::string make_chunked_body(std::size_t body_size, std::size_t chunk_size) { + std::string encoded; + std::size_t remaining = body_size; + + while (remaining != 0) { + const std::size_t amount = remaining < chunk_size ? remaining : chunk_size; + std::ostringstream line; + line << std::hex << amount; + encoded += line.str(); + encoded += "\r\n"; + encoded.append(amount, 'x'); + encoded += "\r\n"; + remaining -= amount; + } + + encoded += "0\r\nX-Benchmark: done\r\n\r\n"; + return encoded; +} + +void BM_ChunkedDecoder(benchmark::State& state) { + const std::size_t body_size = static_cast(state.range(0)); + const std::string encoded = make_chunked_body(body_size, 4096); + std::string input; + std::string output; + + for (auto _ : state) { + state.PauseTiming(); + input = encoded; + output.clear(); + output.reserve(body_size); + state.ResumeTiming(); + + cpp_request::detail::http::ChunkedDecoder decoder; + auto result = decoder.process(input, output); + if (!result || output.size() != body_size) { + state.SkipWithError("chunked decoding failed"); + break; + } + + benchmark::DoNotOptimize(output.data()); + benchmark::ClobberMemory(); + } + + state.SetBytesProcessed( + state.iterations() * static_cast(body_size)); +} + +BENCHMARK(BM_ChunkedDecoder) + ->Arg(4 * 1024) + ->Arg(64 * 1024) + ->Arg(1024 * 1024); + +} // namespace From 626c99e538b999dcbefe36384c73b53aaa45702b Mon Sep 17 00:00:00 2001 From: Rafi Indra Pramudhito Zuhayr Date: Tue, 15 Sep 2026 08:50:39 +0700 Subject: [PATCH 07/15] bench: add local loopback benchmark server --- benchmarks/loopback_benchmark_server.hpp | 210 +++++++++++++++++++++++ 1 file changed, 210 insertions(+) create mode 100644 benchmarks/loopback_benchmark_server.hpp diff --git a/benchmarks/loopback_benchmark_server.hpp b/benchmarks/loopback_benchmark_server.hpp new file mode 100644 index 0000000..1c03f3b --- /dev/null +++ b/benchmarks/loopback_benchmark_server.hpp @@ -0,0 +1,210 @@ +#pragma once + +#include "platform/native_socket.hpp" +#include "platform/network_runtime.hpp" + +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef _WIN32 +#include +#else +#include +#include +#include +#endif + +namespace cpp_request::bench { + +class LoopbackBenchmarkServer final { +public: + explicit LoopbackBenchmarkServer(std::size_t body_size = 32) + : response_( + "HTTP/1.1 200 OK\r\nContent-Length: " + + std::to_string(body_size) + + "\r\n\r\n" + + std::string(body_size, 'x')) { + const auto runtime = detail::platform::ensure_network_runtime(); + if (!runtime.ok) { + return; + } + + detail::platform::NativeSocket listener{ + ::socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)}; + if (!listener) { + return; + } + + sockaddr_in address{}; + address.sin_family = AF_INET; + address.sin_port = 0; + address.sin_addr.s_addr = htonl(INADDR_LOOPBACK); + + if (::bind( + listener.get(), + reinterpret_cast(&address), + static_cast(sizeof(address))) != 0 + || ::listen(listener.get(), 16) != 0) { + return; + } + +#ifdef _WIN32 + int address_size = sizeof(address); +#else + socklen_t address_size = sizeof(address); +#endif + if (::getsockname( + listener.get(), + reinterpret_cast(&address), + &address_size) != 0) { + return; + } + + port_ = ntohs(address.sin_port); + listener_ = std::move(listener); + worker_ = std::thread([this] { run(); }); + } + + ~LoopbackBenchmarkServer() { + stop_.store(true, std::memory_order_relaxed); + listener_.close(); + if (worker_.joinable()) { + worker_.join(); + } + } + + LoopbackBenchmarkServer(const LoopbackBenchmarkServer&) = delete; + LoopbackBenchmarkServer& operator=(const LoopbackBenchmarkServer&) = delete; + + [[nodiscard]] bool valid() const noexcept { + return static_cast(listener_) && port_ != 0; + } + + [[nodiscard]] std::uint16_t port() const noexcept { return port_; } + + [[nodiscard]] std::string url(std::string_view target = "/") const { + return "http://127.0.0.1:" + std::to_string(port_) + std::string{target}; + } + +private: + using NativeSocket = detail::platform::NativeSocket; + using NativeSocketHandle = detail::platform::NativeSocketHandle; + + static bool wait_readable( + NativeSocketHandle socket, + std::chrono::milliseconds timeout) noexcept { + fd_set read_set; + FD_ZERO(&read_set); + FD_SET(socket, &read_set); + + timeval value{}; + value.tv_sec = static_cast(timeout.count() / 1000); + value.tv_usec = static_cast((timeout.count() % 1000) * 1000); + +#ifdef _WIN32 + return ::select(0, &read_set, nullptr, nullptr, &value) > 0; +#else + return ::select(socket + 1, &read_set, nullptr, nullptr, &value) > 0; +#endif + } + + static int receive_native( + NativeSocketHandle socket, + char* data, + std::size_t size) noexcept { +#ifdef _WIN32 + return ::recv(socket, data, static_cast(size), 0); +#else + return static_cast(::recv(socket, data, size, 0)); +#endif + } + + static int send_native( + NativeSocketHandle socket, + const char* data, + std::size_t size) noexcept { +#ifdef _WIN32 + return ::send(socket, data, static_cast(size), 0); +#else + return static_cast(::send(socket, data, size, 0)); +#endif + } + + static bool send_all(NativeSocketHandle socket, std::string_view data) { + std::size_t written = 0; + while (written < data.size()) { + const int count = send_native( + socket, + data.data() + written, + data.size() - written); + if (count <= 0) { + return false; + } + written += static_cast(count); + } + return true; + } + + bool receive_request(NativeSocketHandle socket) const { + std::string request; + std::array buffer{}; + + while (!stop_.load(std::memory_order_relaxed)) { + if (!wait_readable(socket, std::chrono::milliseconds{100})) { + continue; + } + + const int count = receive_native(socket, buffer.data(), buffer.size()); + if (count <= 0) { + return false; + } + + request.append(buffer.data(), static_cast(count)); + if (request.find("\r\n\r\n") != std::string::npos) { + return true; + } + } + return false; + } + + void serve_peer(NativeSocketHandle socket) { + while (!stop_.load(std::memory_order_relaxed)) { + if (!receive_request(socket)) { + return; + } + if (!send_all(socket, response_)) { + return; + } + } + } + + void run() { + while (!stop_.load(std::memory_order_relaxed)) { + if (!listener_ || !wait_readable( + listener_.get(), + std::chrono::milliseconds{100})) { + continue; + } + + NativeSocket peer{::accept(listener_.get(), nullptr, nullptr)}; + if (!peer) { + continue; + } + serve_peer(peer.get()); + } + } + + NativeSocket listener_; + std::uint16_t port_{0}; + std::atomic stop_{false}; + std::thread worker_; + std::string response_; +}; + +} // namespace cpp_request::bench From 30f712957a1b8b28d6d6bb577051faf521b53e45 Mon Sep 17 00:00:00 2001 From: Rafi Indra Pramudhito Zuhayr Date: Tue, 15 Sep 2026 08:50:50 +0700 Subject: [PATCH 08/15] bench: add end-to-end loopback client benchmark --- benchmarks/loopback_client_benchmark.cpp | 55 ++++++++++++++++++++++++ 1 file changed, 55 insertions(+) create mode 100644 benchmarks/loopback_client_benchmark.cpp diff --git a/benchmarks/loopback_client_benchmark.cpp b/benchmarks/loopback_client_benchmark.cpp new file mode 100644 index 0000000..5b955f3 --- /dev/null +++ b/benchmarks/loopback_client_benchmark.cpp @@ -0,0 +1,55 @@ +#include + +#include "loopback_benchmark_server.hpp" + +#include + +#include +#include + +namespace { + +void configure(cpp_request::Client& client) { + client.set_connect_timeout(std::chrono::seconds{2}); + client.set_read_timeout(std::chrono::seconds{2}); + client.set_write_timeout(std::chrono::seconds{2}); +} + +void BM_LoopbackClient(benchmark::State& state) { + const std::size_t body_size = static_cast(state.range(0)); + cpp_request::bench::LoopbackBenchmarkServer server{body_size}; + if (!server.valid()) { + state.SkipWithError("failed to create loopback server"); + return; + } + + cpp_request::Client client; + configure(client); + const std::string url = server.url("/benchmark"); + + auto warmup = client.get(url); + if (!warmup) { + state.SkipWithError("loopback warmup request failed"); + return; + } + + for (auto _ : state) { + auto result = client.get(url); + if (!result) { + state.SkipWithError("loopback request failed"); + break; + } + benchmark::DoNotOptimize(result.value().status_code()); + benchmark::DoNotOptimize(result.value().body().data()); + } + + state.SetBytesProcessed( + state.iterations() * static_cast(body_size)); +} + +BENCHMARK(BM_LoopbackClient) + ->Arg(32) + ->Arg(4 * 1024) + ->UseRealTime(); + +} // namespace From 269a7e69fc84dda8f404ca18e04a25bb95e49376 Mon Sep 17 00:00:00 2001 From: Rafi Indra Pramudhito Zuhayr Date: Tue, 15 Sep 2026 08:51:01 +0700 Subject: [PATCH 09/15] bench: compare keep-alive reuse with reconnect --- benchmarks/connection_reuse_benchmark.cpp | 72 +++++++++++++++++++++++ 1 file changed, 72 insertions(+) create mode 100644 benchmarks/connection_reuse_benchmark.cpp diff --git a/benchmarks/connection_reuse_benchmark.cpp b/benchmarks/connection_reuse_benchmark.cpp new file mode 100644 index 0000000..9348c3d --- /dev/null +++ b/benchmarks/connection_reuse_benchmark.cpp @@ -0,0 +1,72 @@ +#include + +#include "loopback_benchmark_server.hpp" + +#include + +#include + +namespace { + +void configure(cpp_request::Client& client) { + client.set_connect_timeout(std::chrono::seconds{2}); + client.set_read_timeout(std::chrono::seconds{2}); + client.set_write_timeout(std::chrono::seconds{2}); +} + +void BM_ConnectionReuse(benchmark::State& state) { + cpp_request::bench::LoopbackBenchmarkServer server{32}; + if (!server.valid()) { + state.SkipWithError("failed to create loopback server"); + return; + } + + cpp_request::Client client; + configure(client); + const std::string url = server.url("/reuse"); + + auto warmup = client.get(url); + if (!warmup) { + state.SkipWithError("keep-alive warmup request failed"); + return; + } + + for (auto _ : state) { + auto result = client.get(url); + if (!result) { + state.SkipWithError("keep-alive request failed"); + break; + } + benchmark::DoNotOptimize(result.value().body().data()); + } + + state.SetItemsProcessed(state.iterations()); +} + +void BM_ConnectionReconnect(benchmark::State& state) { + cpp_request::bench::LoopbackBenchmarkServer server{32}; + if (!server.valid()) { + state.SkipWithError("failed to create loopback server"); + return; + } + + const std::string url = server.url("/reconnect"); + + for (auto _ : state) { + cpp_request::Client client; + configure(client); + auto result = client.get(url); + if (!result) { + state.SkipWithError("reconnect request failed"); + break; + } + benchmark::DoNotOptimize(result.value().body().data()); + } + + state.SetItemsProcessed(state.iterations()); +} + +BENCHMARK(BM_ConnectionReuse)->UseRealTime(); +BENCHMARK(BM_ConnectionReconnect)->UseRealTime(); + +} // namespace From fa6fa9303eb62302897334bd9f4ecac1818c8459 Mon Sep 17 00:00:00 2001 From: Rafi Indra Pramudhito Zuhayr Date: Tue, 15 Sep 2026 08:51:28 +0700 Subject: [PATCH 10/15] bench: add cross-platform smoke target --- benchmarks/CMakeLists.txt | 35 +++++++++++++++++++++++++++++------ 1 file changed, 29 insertions(+), 6 deletions(-) diff --git a/benchmarks/CMakeLists.txt b/benchmarks/CMakeLists.txt index 8280484..9125d2c 100644 --- a/benchmarks/CMakeLists.txt +++ b/benchmarks/CMakeLists.txt @@ -42,11 +42,34 @@ cpp_request_add_benchmark( connection_reuse_benchmark.cpp ) +set(CPP_REQUEST_BENCHMARK_TARGETS + cpp_request_bench_request_serializer + cpp_request_bench_response_parser + cpp_request_bench_chunked_decoder + cpp_request_bench_loopback_client + cpp_request_bench_connection_reuse +) + add_custom_target(cpp_request_benchmarks - DEPENDS - cpp_request_bench_request_serializer - cpp_request_bench_response_parser - cpp_request_bench_chunked_decoder - cpp_request_bench_loopback_client - cpp_request_bench_connection_reuse + DEPENDS ${CPP_REQUEST_BENCHMARK_TARGETS} +) + +add_custom_target(cpp_request_benchmark_smoke + COMMAND $ + --benchmark_min_time=0.01 + --benchmark_repetitions=1 + COMMAND $ + --benchmark_min_time=0.01 + --benchmark_repetitions=1 + COMMAND $ + --benchmark_min_time=0.01 + --benchmark_repetitions=1 + COMMAND $ + --benchmark_min_time=0.01 + --benchmark_repetitions=1 + COMMAND $ + --benchmark_min_time=0.01 + --benchmark_repetitions=1 + DEPENDS ${CPP_REQUEST_BENCHMARK_TARGETS} + USES_TERMINAL ) From e2cbdac9399f74dc21c6c79662cd2a161c3b2fff Mon Sep 17 00:00:00 2001 From: Rafi Indra Pramudhito Zuhayr Date: Tue, 15 Sep 2026 08:51:37 +0700 Subject: [PATCH 11/15] ci: add benchmark smoke matrix --- .github/workflows/benchmarks.yml | 50 ++++++++++++++++++++++++++++++++ 1 file changed, 50 insertions(+) create mode 100644 .github/workflows/benchmarks.yml diff --git a/.github/workflows/benchmarks.yml b/.github/workflows/benchmarks.yml new file mode 100644 index 0000000..588ccce --- /dev/null +++ b/.github/workflows/benchmarks.yml @@ -0,0 +1,50 @@ +name: Benchmark Smoke + +on: + push: + branches: [ main, master ] + paths: + - 'include/**' + - 'src/**' + - 'benchmarks/**' + - 'cmake/packages/google-benchmark.cmake' + - 'CMakeLists.txt' + - '.github/workflows/benchmarks.yml' + pull_request: + branches: [ main, master ] + paths: + - 'include/**' + - 'src/**' + - 'benchmarks/**' + - 'cmake/packages/google-benchmark.cmake' + - 'CMakeLists.txt' + - '.github/workflows/benchmarks.yml' + +jobs: + benchmark-smoke: + name: Benchmark Smoke (${{ matrix.os }}) + runs-on: ${{ matrix.os }} + + strategy: + fail-fast: false + matrix: + os: [ubuntu-latest, windows-latest, macos-latest] + + steps: + - name: Checkout Repository + uses: actions/checkout@v4 + + - name: Configure Release Benchmarks + shell: bash + run: | + cmake -B build-bench \ + -DCMAKE_BUILD_TYPE=Release \ + -DBUILD_TESTING=OFF \ + -DCPP_REQUEST_BUILD_BENCHMARKS=ON + + - name: Build and Smoke Benchmarks + shell: bash + run: | + cmake --build build-bench \ + --config Release \ + --target cpp_request_benchmark_smoke From 2056a23cdfed308ee6abf7dc027d9f0e4f88db8b Mon Sep 17 00:00:00 2001 From: Rafi Indra Pramudhito Zuhayr Date: Tue, 15 Sep 2026 08:51:54 +0700 Subject: [PATCH 12/15] bench: make serializer benchmark includes explicit --- benchmarks/request_serializer_benchmark.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/benchmarks/request_serializer_benchmark.cpp b/benchmarks/request_serializer_benchmark.cpp index a733d78..a50a5b8 100644 --- a/benchmarks/request_serializer_benchmark.cpp +++ b/benchmarks/request_serializer_benchmark.cpp @@ -5,6 +5,7 @@ #include #include +#include #include #include From 714220978ba337d7c58f4c99aad5bc83b260ec92 Mon Sep 17 00:00:00 2001 From: Rafi Indra Pramudhito Zuhayr Date: Tue, 15 Sep 2026 08:52:09 +0700 Subject: [PATCH 13/15] bench: make loopback benchmark includes explicit --- benchmarks/loopback_client_benchmark.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/benchmarks/loopback_client_benchmark.cpp b/benchmarks/loopback_client_benchmark.cpp index 5b955f3..a41f0d6 100644 --- a/benchmarks/loopback_client_benchmark.cpp +++ b/benchmarks/loopback_client_benchmark.cpp @@ -5,7 +5,9 @@ #include #include +#include #include +#include namespace { From b38fda1313c4bfc3aa020e253ce7958e22e8dd2b Mon Sep 17 00:00:00 2001 From: Rafi Indra Pramudhito Zuhayr Date: Tue, 15 Sep 2026 08:52:33 +0700 Subject: [PATCH 14/15] bench: make reuse benchmark includes explicit --- benchmarks/connection_reuse_benchmark.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/benchmarks/connection_reuse_benchmark.cpp b/benchmarks/connection_reuse_benchmark.cpp index 9348c3d..0829107 100644 --- a/benchmarks/connection_reuse_benchmark.cpp +++ b/benchmarks/connection_reuse_benchmark.cpp @@ -5,6 +5,7 @@ #include #include +#include namespace { From 45470e8d6396e7d81780031f2dc0206c4b74a05d Mon Sep 17 00:00:00 2001 From: Rafi Indra Pramudhito Zuhayr Date: Tue, 15 Sep 2026 08:52:51 +0700 Subject: [PATCH 15/15] bench: harden loopback server shutdown --- benchmarks/loopback_benchmark_server.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/benchmarks/loopback_benchmark_server.hpp b/benchmarks/loopback_benchmark_server.hpp index 1c03f3b..f26cc83 100644 --- a/benchmarks/loopback_benchmark_server.hpp +++ b/benchmarks/loopback_benchmark_server.hpp @@ -11,6 +11,7 @@ #include #include #include +#include #ifdef _WIN32 #include @@ -73,7 +74,6 @@ class LoopbackBenchmarkServer final { ~LoopbackBenchmarkServer() { stop_.store(true, std::memory_order_relaxed); - listener_.close(); if (worker_.joinable()) { worker_.join(); }