diff --git a/flake.lock b/flake.lock index ffba236..e9c5a76 100644 --- a/flake.lock +++ b/flake.lock @@ -19,11 +19,11 @@ "flake-compat_2": { "flake": false, "locked": { - "lastModified": 1673956053, - "narHash": "sha256-4gtG9iQuiKITOjNQQeQIpoIB6b16fm+504Ch3sNKLd8=", + "lastModified": 1696426674, + "narHash": "sha256-kvjfFW7WAETZlt09AgDn1MrtKzP7t90Vf7vypd3OL1U=", "owner": "edolstra", "repo": "flake-compat", - "rev": "35bb57c0c8d8b62bbfd284272c928ceb64ddbde9", + "rev": "0f9255e01c2351cc7d116c072cb317785dd33b33", "type": "github" }, "original": { @@ -69,12 +69,15 @@ } }, "flake-utils_3": { + "inputs": { + "systems": "systems_3" + }, "locked": { - "lastModified": 1659877975, - "narHash": "sha256-zllb8aq3YO3h8B/U0/J1WBgAL8EX5yWf5pMj3G0NAmc=", + "lastModified": 1705309234, + "narHash": "sha256-uNRRNRKmJyCRC/8y1RqBkqWBLM034y4qN7EprSdmgyA=", "owner": "numtide", "repo": "flake-utils", - "rev": "c0e246b9b83f637f4681389ecabcb2681b4f3af0", + "rev": "1ef2e671c3b0c19053962c07dbda38332dcebf26", "type": "github" }, "original": { @@ -108,32 +111,32 @@ }, "nixpkgs": { "locked": { - "lastModified": 1704290814, - "narHash": "sha256-LWvKHp7kGxk/GEtlrGYV68qIvPHkU9iToomNFGagixU=", + "lastModified": 1718089647, + "narHash": "sha256-COO4Xk2EzlZ3x9KCiJildlAA6cYDSPlnY8ms7pKl2Iw=", "owner": "nixos", "repo": "nixpkgs", - "rev": "70bdadeb94ffc8806c0570eb5c2695ad29f0e421", + "rev": "f7207adcc68d9cafa29e3cd252a18743ae512c6a", "type": "github" }, "original": { "owner": "nixos", - "ref": "release-23.05", + "ref": "nixpkgs-unstable", "repo": "nixpkgs", "type": "github" } }, "nixpkgs_2": { "locked": { - "lastModified": 1702350026, - "narHash": "sha256-A+GNZFZdfl4JdDphYKBJ5Ef1HOiFsP18vQe9mqjmUis=", + "lastModified": 1708161998, + "narHash": "sha256-6KnemmUorCvlcAvGziFosAVkrlWZGIc6UNT9GUYr0jQ=", "owner": "NixOS", "repo": "nixpkgs", - "rev": "9463103069725474698139ab10f17a9d125da859", + "rev": "84d981bae8b5e783b3b548de505b22880559515f", "type": "github" }, "original": { "owner": "NixOS", - "ref": "nixos-23.05", + "ref": "nixos-23.11", "repo": "nixpkgs", "type": "github" } @@ -177,6 +180,21 @@ "type": "github" } }, + "systems_3": { + "locked": { + "lastModified": 1681028828, + "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=", + "owner": "nix-systems", + "repo": "default", + "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e", + "type": "github" + }, + "original": { + "owner": "nix-systems", + "repo": "default", + "type": "github" + } + }, "zig": { "inputs": { "flake-compat": "flake-compat_2", @@ -184,11 +202,11 @@ "nixpkgs": "nixpkgs_2" }, "locked": { - "lastModified": 1704888534, - "narHash": "sha256-douEXUiWCVL9NvWKYBc8ydq51qLLUwlBo6lJJoktkGw=", + "lastModified": 1718065467, + "narHash": "sha256-bRghiY4NGJmyE8Kf5KsPvu3W2AJ7Mf5p+SEWAOPvSOY=", "owner": "mitchellh", "repo": "zig-overlay", - "rev": "c69295c92a98947295755a9ac2d49a8d447cc04d", + "rev": "41966f11eec0ea87f178a857bdfb53c2627f763e", "type": "github" }, "original": { diff --git a/flake.nix b/flake.nix index 01f1b72..435accb 100644 --- a/flake.nix +++ b/flake.nix @@ -2,8 +2,8 @@ description = "zap dev shell"; inputs = { - # nixpkgs.url = "github:nixos/nixpkgs/nixpkgs-unstable"; // GLIBC problem! - nixpkgs.url = "github:nixos/nixpkgs/release-23.05"; + nixpkgs.url = "github:nixos/nixpkgs/nixpkgs-unstable"; # GLIBC problem! + # nixpkgs.url = "github:nixos/nixpkgs/release-23.05"; flake-utils.url = "github:numtide/flake-utils"; # required for latest zig @@ -43,8 +43,10 @@ in rec { devShells.default = pkgs.mkShell { nativeBuildInputs = with pkgs; [ + liburing # neovim-nightly-pkgs.neovim - zigpkgs."0.12.0" + zig + # zigpkgs."0.12.0" bat wrk python310 @@ -89,7 +91,7 @@ devShells.build = pkgs.mkShell { nativeBuildInputs = with pkgs; [ - zigpkgs."0.12.0" + zig pkgs.openssl ]; @@ -110,7 +112,7 @@ devShells.masta = pkgs.mkShell { nativeBuildInputs = with pkgs; [ - zigpkgs.master + zig pkgs.openssl ]; diff --git a/wrk/cpp/CMakeLists.txt b/wrk/cpp/CMakeLists.txt new file mode 100644 index 0000000..3ca6e23 --- /dev/null +++ b/wrk/cpp/CMakeLists.txt @@ -0,0 +1,18 @@ +cmake_minimum_required(VERSION 3.10) +project(cpp_beast) + +set(CMAKE_CXX_STANDARD 23) +set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wextra -pedantic ") +set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -march=native -mtune=native -Ofast ") + +find_package(Boost 1.84.0 CONFIG REQUIRED COMPONENTS system) +link_libraries(Boost::system) +add_executable(cpp_beast main.cpp) + +target_link_libraries(cpp_beast uring) +target_compile_definitions(cpp_beast PRIVATE + BOOST_BEAST_USE_STD_STRING_VIEW=1 + BOOST_ASIO_NO_DEPRECATED=1 + #TRUE_HTTP=1 + #CACHED_RESPONSE=1 +) diff --git a/wrk/cpp/main.cpp b/wrk/cpp/main.cpp index 3241b9b..e51c48d 100644 --- a/wrk/cpp/main.cpp +++ b/wrk/cpp/main.cpp @@ -1,80 +1,96 @@ -#include -#include -#include +#include +#include #include -#include +#include +#include +#include +#include namespace beast = boost::beast; -namespace http = beast::http; -namespace net = boost::asio; -using tcp = net::ip::tcp; +namespace http = beast::http; +namespace net = boost::asio; +using tcp = net::ip::tcp; +using namespace net::experimental::awaitable_operators; +using executor_t = net::thread_pool::executor_type; +using acceptor_t = net::deferred_t::as_default_on_t>; +using socket_t = net::deferred_t::as_default_on_t>; -std::string read_html_file(const std::string& file_path) { - std::ifstream file(file_path); - if (!file) { - return "File not found: " + file_path; - } - std::string content((std::istreambuf_iterator(file)), std::istreambuf_iterator()); - file.close(); - return content; +[[maybe_unused]] static std::string read_html_file(std::string const& file_path) { + std::ifstream file(file_path, std::ios::binary); + return {std::istreambuf_iterator(file), {}}; } -void handle_client(tcp::socket socket, const std::string& msg) { - try { - // Construct an HTTP response with the HTML content - http::response response; - response.version(11); - response.result(http::status::ok); - response.reason("OK"); - response.set(http::field::server, "C++ Server"); - response.set(http::field::content_type, "text/html"); - response.body() = msg; - response.prepare_payload(); +static auto const make_response_message = [](bool keep_alive) { + // Construct an HTTP response with the HTML content + std::string_view msg = "Hello from C++!!!"; // or read_html_file("hello.html"); - // Send the response to the client - http::write(socket, response); - } catch (const std::exception& e) { - std::cerr << "Error: " << e.what() << std::endl; + http::response> res{http::status::ok, 11, msg}; + res.set(http::field::server, "C++ Server"); + res.set(http::field::content_type, "text/html"); + res.keep_alive(keep_alive); + res.prepare_payload(); + return res; +}; + +static auto const s_cooked_response = [] { + static auto const text = boost::lexical_cast(make_response_message(true)); + return net::buffer(text); +}(); + +net::awaitable handle_client_async(socket_t socket) try { + socket.set_option(tcp::no_delay(true)); // no difference observed in benchmark + +#ifdef TRUE_HTTP // This affects throughput by only about -10% + beast::flat_buffer buf; + for (http::request req;; req.clear()) { + auto [ec, _] = co_await async_read(socket, buf, req, as_tuple(net::deferred)); + if (ec) + break; + #ifdef CACHED_RESPONSE + // emulate caching server + co_await async_write(socket, s_cooked_response); + #else + // This is a more realistic way but probably NOT what Kestrel is doing for the static route + // It affects throughput by about -25% + co_await async_write(socket, make_response_message(req.keep_alive())); + #endif + if (!req.keep_alive()) + break; } +#else + // Since we're ignoring the requests, we might as well assume they're correct. (INSECURE) + for (beast::flat_buffer buf;;) { + auto [ec, n] = co_await async_read_until(socket, buf, "\r\n\r\n", as_tuple(net::deferred)); + if (ec) + break; + buf.consume(n); + co_await async_write(socket, s_cooked_response); + } +#endif +} catch (beast::system_error const& e) { + std::osyncstream(std::cerr) << "handle_client_async error: " << e.code().message() << std::endl; } -int main() { - try { - net::io_context io_context{BOOST_ASIO_CONCURRENCY_HINT_UNSAFE_IO}; +net::awaitable server(uint16_t port) { + auto ex = co_await net::this_coro::executor; - // Create an endpoint to bind to - tcp::endpoint endpoint(tcp::v4(), 8070); - - // Create and bind the acceptor - tcp::acceptor acceptor(io_context, endpoint); - std::cout << "Server listening on port 8070..." << std::endl; - - // static 17-byte string - std::string msg = "Hello from C++!!!"; - // or - // Read HTML content from a file (e.g., "index.html") - // std::string html_content = read_html_file("hello.html"); - - // std::cout << "str len: " << (html_content.length() == msg.length()) << std::boolalpha << "\n"; - - // Create a thread pool with 4 threads - net::thread_pool pool(4); - - while (true) { - // Wait for a client to connect - tcp::socket socket(io_context); - acceptor.accept(socket); - - // Post a task to the thread pool to handle the client request - net::post(pool, [socket = std::move(socket), msg]() mutable { - handle_client(std::move(socket), msg); - }); - } - - // The thread pool destructor will ensure that all threads are joined properly. - } catch (const std::exception& e) { - std::cerr << "Error: " << e.what() << std::endl; - } - - return 0; + for (acceptor_t acceptor(ex, {{}, port});;) + co_spawn(ex, // + handle_client_async(co_await acceptor.async_accept()), // + net::detached); +} + +int main() try { + // Create a thread pool + net::thread_pool pool(4); + executor_t ex = pool.get_executor(); + + // Create and bind the acceptor + co_spawn(ex, server(8070), net::detached); + + std::cout << "Server listening on port 8070..." << std::endl; + + pool.join(); +} catch (std::exception const& e) { + std::cerr << "Main error: " << e.what() << std::endl; } diff --git a/wrk/csharp/Program.cs b/wrk/csharp/Program.cs index 1b1f7bf..2a7393a 100644 --- a/wrk/csharp/Program.cs +++ b/wrk/csharp/Program.cs @@ -3,6 +3,6 @@ builder.Logging.ClearProviders(); var app = builder.Build(); -app.MapGet("/", () => "Hello from C#"); +app.MapGet("/", () => "Hello from C#1234"); app.Run(); diff --git a/wrk/measure.sh b/wrk/measure.sh index 5520f0a..251a741 100755 --- a/wrk/measure.sh +++ b/wrk/measure.sh @@ -84,8 +84,8 @@ if [ "$SUBJECT" = "csharp" ] ; then fi if [ "$SUBJECT" = "cpp-beast" ] ; then - cd wrk/cpp && zig build -Doptimize=ReleaseFast - $TSK_SRV ./zig-out/bin/cpp-beast 127.0.0.1 8070 . & + cd wrk/cpp && cmake -B build/ . && cmake --build build/ + $TSK_SRV ./build/cpp_beast 127.0.0.1 8070 . & PID=$! URL=http://127.0.0.1:8070 fi