mirror of
https://github.com/zigzap/zap.git
synced 2025-10-21 15:44:10 +00:00
Rewrite beast C++ test server
This happens to use C++20 coroutines because I'm lazy. It can equivalently be written without. I reworded measure.sh to use cmake instead of zig build. Again, I'm lazy and don't wish to learn zig build today. The flake had dependency issues - for one, the beast archive references in cpp/build.zig.zon is gone; - secondly the upstream zig flake does not have zig 12.0 ¯\_(ツ)_/¯ Luckily that's in Nixpkgs, so I switched to nixpkgs-unstable. - I also added a `liburing` dependency to the devShell. This was to test with uring, but it didn't produce better results
This commit is contained in:
parent
3f9c7d0f6b
commit
b87f3a2a01
6 changed files with 147 additions and 93 deletions
52
flake.lock
generated
52
flake.lock
generated
|
@ -19,11 +19,11 @@
|
||||||
"flake-compat_2": {
|
"flake-compat_2": {
|
||||||
"flake": false,
|
"flake": false,
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1673956053,
|
"lastModified": 1696426674,
|
||||||
"narHash": "sha256-4gtG9iQuiKITOjNQQeQIpoIB6b16fm+504Ch3sNKLd8=",
|
"narHash": "sha256-kvjfFW7WAETZlt09AgDn1MrtKzP7t90Vf7vypd3OL1U=",
|
||||||
"owner": "edolstra",
|
"owner": "edolstra",
|
||||||
"repo": "flake-compat",
|
"repo": "flake-compat",
|
||||||
"rev": "35bb57c0c8d8b62bbfd284272c928ceb64ddbde9",
|
"rev": "0f9255e01c2351cc7d116c072cb317785dd33b33",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
|
@ -69,12 +69,15 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"flake-utils_3": {
|
"flake-utils_3": {
|
||||||
|
"inputs": {
|
||||||
|
"systems": "systems_3"
|
||||||
|
},
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1659877975,
|
"lastModified": 1705309234,
|
||||||
"narHash": "sha256-zllb8aq3YO3h8B/U0/J1WBgAL8EX5yWf5pMj3G0NAmc=",
|
"narHash": "sha256-uNRRNRKmJyCRC/8y1RqBkqWBLM034y4qN7EprSdmgyA=",
|
||||||
"owner": "numtide",
|
"owner": "numtide",
|
||||||
"repo": "flake-utils",
|
"repo": "flake-utils",
|
||||||
"rev": "c0e246b9b83f637f4681389ecabcb2681b4f3af0",
|
"rev": "1ef2e671c3b0c19053962c07dbda38332dcebf26",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
|
@ -108,32 +111,32 @@
|
||||||
},
|
},
|
||||||
"nixpkgs": {
|
"nixpkgs": {
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1704290814,
|
"lastModified": 1718089647,
|
||||||
"narHash": "sha256-LWvKHp7kGxk/GEtlrGYV68qIvPHkU9iToomNFGagixU=",
|
"narHash": "sha256-COO4Xk2EzlZ3x9KCiJildlAA6cYDSPlnY8ms7pKl2Iw=",
|
||||||
"owner": "nixos",
|
"owner": "nixos",
|
||||||
"repo": "nixpkgs",
|
"repo": "nixpkgs",
|
||||||
"rev": "70bdadeb94ffc8806c0570eb5c2695ad29f0e421",
|
"rev": "f7207adcc68d9cafa29e3cd252a18743ae512c6a",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
"owner": "nixos",
|
"owner": "nixos",
|
||||||
"ref": "release-23.05",
|
"ref": "nixpkgs-unstable",
|
||||||
"repo": "nixpkgs",
|
"repo": "nixpkgs",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"nixpkgs_2": {
|
"nixpkgs_2": {
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1702350026,
|
"lastModified": 1708161998,
|
||||||
"narHash": "sha256-A+GNZFZdfl4JdDphYKBJ5Ef1HOiFsP18vQe9mqjmUis=",
|
"narHash": "sha256-6KnemmUorCvlcAvGziFosAVkrlWZGIc6UNT9GUYr0jQ=",
|
||||||
"owner": "NixOS",
|
"owner": "NixOS",
|
||||||
"repo": "nixpkgs",
|
"repo": "nixpkgs",
|
||||||
"rev": "9463103069725474698139ab10f17a9d125da859",
|
"rev": "84d981bae8b5e783b3b548de505b22880559515f",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
"owner": "NixOS",
|
"owner": "NixOS",
|
||||||
"ref": "nixos-23.05",
|
"ref": "nixos-23.11",
|
||||||
"repo": "nixpkgs",
|
"repo": "nixpkgs",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
}
|
}
|
||||||
|
@ -177,6 +180,21 @@
|
||||||
"type": "github"
|
"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": {
|
"zig": {
|
||||||
"inputs": {
|
"inputs": {
|
||||||
"flake-compat": "flake-compat_2",
|
"flake-compat": "flake-compat_2",
|
||||||
|
@ -184,11 +202,11 @@
|
||||||
"nixpkgs": "nixpkgs_2"
|
"nixpkgs": "nixpkgs_2"
|
||||||
},
|
},
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1704888534,
|
"lastModified": 1718065467,
|
||||||
"narHash": "sha256-douEXUiWCVL9NvWKYBc8ydq51qLLUwlBo6lJJoktkGw=",
|
"narHash": "sha256-bRghiY4NGJmyE8Kf5KsPvu3W2AJ7Mf5p+SEWAOPvSOY=",
|
||||||
"owner": "mitchellh",
|
"owner": "mitchellh",
|
||||||
"repo": "zig-overlay",
|
"repo": "zig-overlay",
|
||||||
"rev": "c69295c92a98947295755a9ac2d49a8d447cc04d",
|
"rev": "41966f11eec0ea87f178a857bdfb53c2627f763e",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
|
|
12
flake.nix
12
flake.nix
|
@ -2,8 +2,8 @@
|
||||||
description = "zap dev shell";
|
description = "zap dev shell";
|
||||||
|
|
||||||
inputs = {
|
inputs = {
|
||||||
# nixpkgs.url = "github:nixos/nixpkgs/nixpkgs-unstable"; // GLIBC problem!
|
nixpkgs.url = "github:nixos/nixpkgs/nixpkgs-unstable"; # GLIBC problem!
|
||||||
nixpkgs.url = "github:nixos/nixpkgs/release-23.05";
|
# nixpkgs.url = "github:nixos/nixpkgs/release-23.05";
|
||||||
flake-utils.url = "github:numtide/flake-utils";
|
flake-utils.url = "github:numtide/flake-utils";
|
||||||
|
|
||||||
# required for latest zig
|
# required for latest zig
|
||||||
|
@ -43,8 +43,10 @@
|
||||||
in rec {
|
in rec {
|
||||||
devShells.default = pkgs.mkShell {
|
devShells.default = pkgs.mkShell {
|
||||||
nativeBuildInputs = with pkgs; [
|
nativeBuildInputs = with pkgs; [
|
||||||
|
liburing
|
||||||
# neovim-nightly-pkgs.neovim
|
# neovim-nightly-pkgs.neovim
|
||||||
zigpkgs."0.12.0"
|
zig
|
||||||
|
# zigpkgs."0.12.0"
|
||||||
bat
|
bat
|
||||||
wrk
|
wrk
|
||||||
python310
|
python310
|
||||||
|
@ -89,7 +91,7 @@
|
||||||
|
|
||||||
devShells.build = pkgs.mkShell {
|
devShells.build = pkgs.mkShell {
|
||||||
nativeBuildInputs = with pkgs; [
|
nativeBuildInputs = with pkgs; [
|
||||||
zigpkgs."0.12.0"
|
zig
|
||||||
pkgs.openssl
|
pkgs.openssl
|
||||||
];
|
];
|
||||||
|
|
||||||
|
@ -110,7 +112,7 @@
|
||||||
|
|
||||||
devShells.masta = pkgs.mkShell {
|
devShells.masta = pkgs.mkShell {
|
||||||
nativeBuildInputs = with pkgs; [
|
nativeBuildInputs = with pkgs; [
|
||||||
zigpkgs.master
|
zig
|
||||||
pkgs.openssl
|
pkgs.openssl
|
||||||
];
|
];
|
||||||
|
|
||||||
|
|
18
wrk/cpp/CMakeLists.txt
Normal file
18
wrk/cpp/CMakeLists.txt
Normal file
|
@ -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
|
||||||
|
)
|
136
wrk/cpp/main.cpp
136
wrk/cpp/main.cpp
|
@ -1,80 +1,96 @@
|
||||||
#include <iostream>
|
#include <boost/asio.hpp>
|
||||||
#include <fstream>
|
#include <boost/asio/experimental/awaitable_operators.hpp>
|
||||||
#include <string>
|
|
||||||
#include <boost/beast.hpp>
|
#include <boost/beast.hpp>
|
||||||
#include <boost/asio/thread_pool.hpp>
|
#include <boost/lexical_cast.hpp>
|
||||||
|
#include <fstream>
|
||||||
|
#include <iostream>
|
||||||
|
#include <syncstream>
|
||||||
|
|
||||||
namespace beast = boost::beast;
|
namespace beast = boost::beast;
|
||||||
namespace http = beast::http;
|
namespace http = beast::http;
|
||||||
namespace net = boost::asio;
|
namespace net = boost::asio;
|
||||||
using tcp = net::ip::tcp;
|
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<net::basic_socket_acceptor<tcp, executor_t>>;
|
||||||
|
using socket_t = net::deferred_t::as_default_on_t<net::basic_stream_socket<tcp, executor_t>>;
|
||||||
|
|
||||||
std::string read_html_file(const std::string& file_path) {
|
[[maybe_unused]] static std::string read_html_file(std::string const& file_path) {
|
||||||
std::ifstream file(file_path);
|
std::ifstream file(file_path, std::ios::binary);
|
||||||
if (!file) {
|
return {std::istreambuf_iterator<char>(file), {}};
|
||||||
return "File not found: " + file_path;
|
|
||||||
}
|
|
||||||
std::string content((std::istreambuf_iterator<char>(file)), std::istreambuf_iterator<char>());
|
|
||||||
file.close();
|
|
||||||
return content;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void handle_client(tcp::socket socket, const std::string& msg) {
|
static auto const make_response_message = [](bool keep_alive) {
|
||||||
try {
|
|
||||||
// Construct an HTTP response with the HTML content
|
// Construct an HTTP response with the HTML content
|
||||||
http::response<http::string_body> response;
|
std::string_view msg = "Hello from C++!!!"; // or read_html_file("hello.html");
|
||||||
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();
|
|
||||||
|
|
||||||
// Send the response to the client
|
http::response<http::span_body<char const>> res{http::status::ok, 11, msg};
|
||||||
http::write(socket, response);
|
res.set(http::field::server, "C++ Server");
|
||||||
} catch (const std::exception& e) {
|
res.set(http::field::content_type, "text/html");
|
||||||
std::cerr << "Error: " << e.what() << std::endl;
|
res.keep_alive(keep_alive);
|
||||||
|
res.prepare_payload();
|
||||||
|
return res;
|
||||||
|
};
|
||||||
|
|
||||||
|
static auto const s_cooked_response = [] {
|
||||||
|
static auto const text = boost::lexical_cast<std::string>(make_response_message(true));
|
||||||
|
return net::buffer(text);
|
||||||
|
}();
|
||||||
|
|
||||||
|
net::awaitable<void, executor_t> 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<http::empty_body> 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() {
|
net::awaitable<void, executor_t> server(uint16_t port) {
|
||||||
try {
|
auto ex = co_await net::this_coro::executor;
|
||||||
net::io_context io_context{BOOST_ASIO_CONCURRENCY_HINT_UNSAFE_IO};
|
|
||||||
|
|
||||||
// Create an endpoint to bind to
|
for (acceptor_t acceptor(ex, {{}, port});;)
|
||||||
tcp::endpoint endpoint(tcp::v4(), 8070);
|
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
|
// Create and bind the acceptor
|
||||||
tcp::acceptor acceptor(io_context, endpoint);
|
co_spawn(ex, server(8070), net::detached);
|
||||||
|
|
||||||
std::cout << "Server listening on port 8070..." << std::endl;
|
std::cout << "Server listening on port 8070..." << std::endl;
|
||||||
|
|
||||||
// static 17-byte string
|
pool.join();
|
||||||
std::string msg = "Hello from C++!!!";
|
} catch (std::exception const& e) {
|
||||||
// or
|
std::cerr << "Main error: " << e.what() << std::endl;
|
||||||
// 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;
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,6 +3,6 @@ builder.Logging.ClearProviders();
|
||||||
|
|
||||||
var app = builder.Build();
|
var app = builder.Build();
|
||||||
|
|
||||||
app.MapGet("/", () => "Hello from C#");
|
app.MapGet("/", () => "Hello from C#1234");
|
||||||
|
|
||||||
app.Run();
|
app.Run();
|
||||||
|
|
|
@ -84,8 +84,8 @@ if [ "$SUBJECT" = "csharp" ] ; then
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if [ "$SUBJECT" = "cpp-beast" ] ; then
|
if [ "$SUBJECT" = "cpp-beast" ] ; then
|
||||||
cd wrk/cpp && zig build -Doptimize=ReleaseFast
|
cd wrk/cpp && cmake -B build/ . && cmake --build build/
|
||||||
$TSK_SRV ./zig-out/bin/cpp-beast 127.0.0.1 8070 . &
|
$TSK_SRV ./build/cpp_beast 127.0.0.1 8070 . &
|
||||||
PID=$!
|
PID=$!
|
||||||
URL=http://127.0.0.1:8070
|
URL=http://127.0.0.1:8070
|
||||||
fi
|
fi
|
||||||
|
|
Loading…
Add table
Reference in a new issue