From eccce5c877cc18be722f61dafa1e88df2bf4402b Mon Sep 17 00:00:00 2001 From: Diogo Martins Date: Sat, 6 Jun 2026 16:03:45 +0100 Subject: [PATCH 1/2] [Rust] Add tokio-uring - HTTP/1.1 Minimal HTTP/1.1 server on tokio-uring (the io_uring-backed runtime with a completion/owned-buffer API) for the H1-isolated profiles (baseline, pipelined, limited-conn). No HTTP framework: a hand-rolled request parser (Content-Length + chunked bodies, keep-alive, pipelining, fragmented-read reassembly) on tokio-uring's owned-buffer TcpStream, one runtime per core with SO_REUSEPORT. Endpoints: /baseline11?a=&b= -> a+b(+body) text/plain; /pipeline -> ok. Validated against the baseline/pipelined contract natively and via the Docker build. --- frameworks/tokio-uring/.dockerignore | 2 + frameworks/tokio-uring/.gitignore | 1 + frameworks/tokio-uring/Cargo.lock | 253 +++++++++++++++++++++ frameworks/tokio-uring/Cargo.toml | 14 ++ frameworks/tokio-uring/Dockerfile | 13 ++ frameworks/tokio-uring/README.md | 24 ++ frameworks/tokio-uring/meta.json | 15 ++ frameworks/tokio-uring/src/main.rs | 315 +++++++++++++++++++++++++++ 8 files changed, 637 insertions(+) create mode 100644 frameworks/tokio-uring/.dockerignore create mode 100644 frameworks/tokio-uring/.gitignore create mode 100644 frameworks/tokio-uring/Cargo.lock create mode 100644 frameworks/tokio-uring/Cargo.toml create mode 100644 frameworks/tokio-uring/Dockerfile create mode 100644 frameworks/tokio-uring/README.md create mode 100644 frameworks/tokio-uring/meta.json create mode 100644 frameworks/tokio-uring/src/main.rs diff --git a/frameworks/tokio-uring/.dockerignore b/frameworks/tokio-uring/.dockerignore new file mode 100644 index 00000000..c7f83c2c --- /dev/null +++ b/frameworks/tokio-uring/.dockerignore @@ -0,0 +1,2 @@ +target +.git diff --git a/frameworks/tokio-uring/.gitignore b/frameworks/tokio-uring/.gitignore new file mode 100644 index 00000000..ea8c4bf7 --- /dev/null +++ b/frameworks/tokio-uring/.gitignore @@ -0,0 +1 @@ +/target diff --git a/frameworks/tokio-uring/Cargo.lock b/frameworks/tokio-uring/Cargo.lock new file mode 100644 index 00000000..caedb023 --- /dev/null +++ b/frameworks/tokio-uring/Cargo.lock @@ -0,0 +1,253 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 4 + +[[package]] +name = "bitflags" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" + +[[package]] +name = "futures-core" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7e3450815272ef58cec6d564423f6e755e25379b217b0bc688e295ba24df6b1d" + +[[package]] +name = "futures-task" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "037711b3d59c33004d3856fbdc83b99d4ff37a24768fa1be9ce3538a1cde4393" + +[[package]] +name = "futures-util" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "389ca41296e6190b48053de0321d02a77f32f8a5d2461dd38762c0593805c6d6" +dependencies = [ + "futures-core", + "futures-task", + "pin-project-lite", + "slab", +] + +[[package]] +name = "httparena-tokio-uring" +version = "0.1.0" +dependencies = [ + "socket2 0.5.10", + "tokio-uring", +] + +[[package]] +name = "io-uring" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "595a0399f411a508feb2ec1e970a4a30c249351e30208960d58298de8660b0e5" +dependencies = [ + "bitflags", + "libc", +] + +[[package]] +name = "libc" +version = "0.2.186" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68ab91017fe16c622486840e4c83c9a37afeff978bd239b5293d61ece587de66" + +[[package]] +name = "mio" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "02bd0af71c67b473010cbbc60715ee815645a4dc942899111f494b4b737d6fda" +dependencies = [ + "libc", + "wasi", + "windows-sys 0.61.2", +] + +[[package]] +name = "pin-project-lite" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a89322df9ebe1c1578d689c92318e070967d1042b512afbe49518723f4e6d5cd" + +[[package]] +name = "slab" +version = "0.4.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c790de23124f9ab44544d7ac05d60440adc586479ce501c1d6d7da3cd8c9cf5" + +[[package]] +name = "socket2" +version = "0.4.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9f7916fc008ca5542385b89a3d3ce689953c143e9304a9bf8beec1de48994c0d" +dependencies = [ + "libc", + "winapi", +] + +[[package]] +name = "socket2" +version = "0.5.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e22376abed350d73dd1cd119b57ffccad95b4e585a7cda43e286245ce23c0678" +dependencies = [ + "libc", + "windows-sys 0.52.0", +] + +[[package]] +name = "socket2" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "52d1cfed4120b4d927bf7c0f86d2087a4a7d6027c906d9f9d525a80573b9be51" +dependencies = [ + "libc", + "windows-sys 0.61.2", +] + +[[package]] +name = "tokio" +version = "1.52.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8fc7f01b389ac15039e4dc9531aa973a135d7a4135281b12d7c1bc79fd57fffe" +dependencies = [ + "libc", + "mio", + "pin-project-lite", + "socket2 0.6.4", + "windows-sys 0.61.2", +] + +[[package]] +name = "tokio-uring" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "748482e3e13584a34664a710168ad5068e8cb1d968aa4ffa887e83ca6dd27967" +dependencies = [ + "futures-util", + "io-uring", + "libc", + "slab", + "socket2 0.4.10", + "tokio", +] + +[[package]] +name = "wasi" +version = "0.11.1+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b" + +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" + +[[package]] +name = "windows-link" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5" + +[[package]] +name = "windows-sys" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" +dependencies = [ + "windows-targets", +] + +[[package]] +name = "windows-sys" +version = "0.61.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc" +dependencies = [ + "windows-link", +] + +[[package]] +name = "windows-targets" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" +dependencies = [ + "windows_aarch64_gnullvm", + "windows_aarch64_msvc", + "windows_i686_gnu", + "windows_i686_gnullvm", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" + +[[package]] +name = "windows_i686_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" + +[[package]] +name = "windows_i686_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" + +[[package]] +name = "windows_i686_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" diff --git a/frameworks/tokio-uring/Cargo.toml b/frameworks/tokio-uring/Cargo.toml new file mode 100644 index 00000000..bb09c43a --- /dev/null +++ b/frameworks/tokio-uring/Cargo.toml @@ -0,0 +1,14 @@ +[package] +name = "httparena-tokio-uring" +version = "0.1.0" +edition = "2021" + +[dependencies] +tokio-uring = "0.5" +socket2 = { version = "0.5", features = ["all"] } + +[profile.release] +codegen-units = 1 +lto = "thin" +opt-level = 3 +panic = "abort" diff --git a/frameworks/tokio-uring/Dockerfile b/frameworks/tokio-uring/Dockerfile new file mode 100644 index 00000000..eb496224 --- /dev/null +++ b/frameworks/tokio-uring/Dockerfile @@ -0,0 +1,13 @@ +FROM rust:1.95 AS build +WORKDIR /app +COPY Cargo.toml Cargo.lock ./ +RUN mkdir src && echo "fn main() {}" > src/main.rs \ + && cargo build --release \ + && rm -rf src target/release/httparena-tokio-uring target/release/deps/httparena_tokio_uring* +COPY src ./src +RUN RUSTFLAGS="-C target-cpu=native" cargo build --release + +FROM debian:bookworm-slim +COPY --from=build /app/target/release/httparena-tokio-uring /server +EXPOSE 8080 +CMD ["/server"] diff --git a/frameworks/tokio-uring/README.md b/frameworks/tokio-uring/README.md new file mode 100644 index 00000000..9486c575 --- /dev/null +++ b/frameworks/tokio-uring/README.md @@ -0,0 +1,24 @@ +# tokio-uring + +A minimal **HTTP/1.1** server on **tokio-uring** — the io_uring-backed Rust +runtime with a completion/owned-buffer API — for the H1-isolated profiles +(`baseline`, `pipelined`, `limited-conn`). No HTTP framework. + +## Serving model +One `tokio_uring::start` per core, each with its own `SO_REUSEPORT` listener +(`socket2` + `from_std`). Reads/writes use tokio-uring's owned-buffer model: a +`Vec` is passed by value into `read`/`write_all` and handed back, reused +across iterations. Responses are batched per read. + +## Hand-rolled HTTP/1.1 +Request line + headers, `Content-Length` **and** `Transfer-Encoding: chunked` +bodies, keep-alive, request pipelining, and fragmented-read reassembly. + +| Endpoint | Response | +|---|---| +| `GET/POST /baseline11?a=&b=` | `text/plain` — `a + b` (+ POST body as an integer) | +| `GET /pipeline` | `text/plain` — `ok` | + +io_uring requires `seccomp=unconfined` under Docker (the harness sets this; +`engine: "io_uring"` makes validate.sh enable it). `PORT` overrides the listen +port for local testing. diff --git a/frameworks/tokio-uring/meta.json b/frameworks/tokio-uring/meta.json new file mode 100644 index 00000000..51959e96 --- /dev/null +++ b/frameworks/tokio-uring/meta.json @@ -0,0 +1,15 @@ +{ + "display_name": "tokio-uring", + "language": "Rust", + "type": "engine", + "engine": "io_uring", + "description": "Minimal HTTP/1.1 server on tokio-uring — the io_uring-backed Rust runtime with a completion/owned-buffer API. A hand-rolled request parser (Content-Length + chunked bodies, keep-alive, pipelining, fragmented reads), one runtime per core with SO_REUSEPORT. No HTTP framework.", + "repo": "https://github.com/tokio-rs/tokio-uring", + "enabled": true, + "tests": [ + "baseline", + "pipelined", + "limited-conn" + ], + "maintainers": [] +} diff --git a/frameworks/tokio-uring/src/main.rs b/frameworks/tokio-uring/src/main.rs new file mode 100644 index 00000000..4914d599 --- /dev/null +++ b/frameworks/tokio-uring/src/main.rs @@ -0,0 +1,315 @@ +//! tokio-uring — a minimal HTTP/1.1 server on tokio-uring for the H1-isolated +//! profiles (baseline, pipelined, limited-conn). No HTTP framework: the request +//! parser (request line, headers, Content-Length + chunked bodies, keep-alive, +//! pipelining, fragmented reads) is hand-rolled on tokio-uring's owned-buffer +//! TcpStream. +//! +//! Serving model: one tokio_uring::start per core, each with its own +//! SO_REUSEPORT listener (socket2 + from_std). Listens on 0.0.0.0:8080 +//! (PORT overrides for testing). +//! +//! Endpoints: +//! GET/POST /baseline11?a=&b= -> text/plain "a + b (+ body)" +//! GET /pipeline -> text/plain "ok" + +use socket2::{Domain, Protocol, Socket, Type}; +use std::net::SocketAddr; +use tokio_uring::net::{TcpListener, TcpStream}; + +const READ_SIZE: usize = 16 * 1024; +const MAX_BUF: usize = 1 << 20; + +fn main() { + let threads = std::thread::available_parallelism() + .map(|n| n.get()) + .unwrap_or(1); + let mut handles = Vec::with_capacity(threads); + for _ in 0..threads { + handles.push(std::thread::spawn(|| tokio_uring::start(serve()))); + } + for h in handles { + let _ = h.join(); + } +} + +async fn serve() { + let listener = bind_reuseport(); + loop { + match listener.accept().await { + Ok((stream, _)) => { + tokio_uring::spawn(handle(stream)); + } + Err(_) => continue, + } + } +} + +fn bind_reuseport() -> TcpListener { + let port = std::env::var("PORT").ok().and_then(|p| p.parse().ok()).unwrap_or(8080u16); + let addr: SocketAddr = format!("0.0.0.0:{port}").parse().unwrap(); + let socket = Socket::new(Domain::IPV4, Type::STREAM, Some(Protocol::TCP)).unwrap(); + socket.set_reuse_address(true).unwrap(); + socket.set_reuse_port(true).unwrap(); + socket.bind(&addr.into()).unwrap(); + socket.listen(1024).unwrap(); + TcpListener::from_std(socket.into()) +} + +async fn handle(stream: TcpStream) { + let _ = stream.set_nodelay(true); + let mut buf: Vec = Vec::with_capacity(READ_SIZE); + let mut out: Vec = Vec::with_capacity(READ_SIZE); + let mut rbuf: Vec = vec![0u8; READ_SIZE]; + + loop { + let mut pos = 0; + let mut keep = true; + loop { + match process(&buf[pos..], &mut out) { + Outcome::Incomplete => break, + Outcome::Complete { consumed, keep_alive } => { + pos += consumed; + if !keep_alive { + keep = false; + break; + } + } + } + } + if pos > 0 { + buf.drain(..pos); + } + if !out.is_empty() { + let (res, mut o) = stream.write_all(out).await; + o.clear(); + out = o; + if res.is_err() { + return; + } + } + if !keep || buf.len() > MAX_BUF { + return; + } + let (res, b) = stream.read(rbuf).await; + rbuf = b; + let n = match res { + Ok(0) | Err(_) => return, + Ok(n) => n, + }; + buf.extend_from_slice(&rbuf[..n]); + } +} + +// ── Hand-rolled HTTP/1.1 ───────────────────────────────────────────────────── + +enum Outcome { + Incomplete, + Complete { consumed: usize, keep_alive: bool }, +} + +/// Parse one request from buf; append its response to out. Returns Incomplete if +/// the request isn't fully buffered yet. +fn process(buf: &[u8], out: &mut Vec) -> Outcome { + let he = match find(buf, b"\r\n\r\n") { + Some(h) => h, + None => return Outcome::Incomplete, + }; + + let mut lines = lines_crlf(&buf[..he]); + let req_line = lines.next().unwrap_or(b""); + let mut rl = req_line.split(|&c| c == b' '); + let _method = rl.next().unwrap_or(b""); + let target = rl.next().unwrap_or(b""); + + let mut content_length: Option = None; + let mut chunked = false; + let mut close = false; + for line in lines { + if let Some(c) = find(line, b":") { + let name = &line[..c]; + let val = trim(&line[c + 1..]); + if ci_eq(name, b"content-length") { + content_length = parse_usize(val); + } else if ci_eq(name, b"transfer-encoding") && ci_contains(val, b"chunked") { + chunked = true; + } else if ci_eq(name, b"connection") && ci_eq(val, b"close") { + close = true; + } + } + } + + let body_start = he + 4; + let (body_int, consumed) = if chunked { + match decode_chunked(&buf[body_start..]) { + Some((body, used)) => (parse_i64(&body), body_start + used), + None => return Outcome::Incomplete, + } + } else if let Some(cl) = content_length { + if buf.len() < body_start + cl { + return Outcome::Incomplete; + } + (parse_i64(&buf[body_start..body_start + cl]), body_start + cl) + } else { + (0, body_start) + }; + + respond(out, target, body_int); + Outcome::Complete { consumed, keep_alive: !close } +} + +fn respond(out: &mut Vec, target: &[u8], body_int: i64) { + let q = find(target, b"?"); + let path = match q { + Some(i) => &target[..i], + None => target, + }; + if path == b"/pipeline" { + write_resp(out, b"ok"); + } else { + let query = match q { + Some(i) => &target[i + 1..], + None => &[][..], + }; + let (a, b) = parse_ab(query); + let s = (a + b + body_int).to_string(); + write_resp(out, s.as_bytes()); + } +} + +fn write_resp(out: &mut Vec, body: &[u8]) { + out.extend_from_slice(b"HTTP/1.1 200 OK\r\nContent-Type: text/plain\r\nContent-Length: "); + out.extend_from_slice(body.len().to_string().as_bytes()); + out.extend_from_slice(b"\r\n\r\n"); + out.extend_from_slice(body); +} + +fn parse_ab(query: &[u8]) -> (i64, i64) { + let (mut a, mut b) = (0i64, 0i64); + for kv in query.split(|&c| c == b'&') { + if let Some(eq) = find(kv, b"=") { + let (k, v) = (&kv[..eq], &kv[eq + 1..]); + if k == b"a" { + a = parse_i64(v); + } else if k == b"b" { + b = parse_i64(v); + } + } + } + (a, b) +} + +/// Decode a chunked body. Returns (decoded_bytes, bytes_consumed) or None if the +/// terminating 0-chunk isn't fully buffered yet. +fn decode_chunked(buf: &[u8]) -> Option<(Vec, usize)> { + let mut body = Vec::new(); + let mut pos = 0; + loop { + let nl = find(&buf[pos..], b"\r\n")?; + let size = parse_hex(&buf[pos..pos + nl])?; + pos += nl + 2; + if size == 0 { + let end = find(&buf[pos..], b"\r\n")?; // final CRLF (no trailers) + return Some((body, pos + end + 2)); + } + if buf.len() < pos + size + 2 { + return None; + } + body.extend_from_slice(&buf[pos..pos + size]); + pos += size; + if &buf[pos..pos + 2] != b"\r\n" { + return None; + } + pos += 2; + } +} + +// ── byte helpers ───────────────────────────────────────────────────────────── + +fn find(h: &[u8], n: &[u8]) -> Option { + if n.is_empty() || h.len() < n.len() { + return None; + } + h.windows(n.len()).position(|w| w == n) +} + +fn lines_crlf(b: &[u8]) -> impl Iterator { + b.split(|&c| c == b'\n').map(|l| { + if l.last() == Some(&b'\r') { + &l[..l.len() - 1] + } else { + l + } + }) +} + +fn trim(mut b: &[u8]) -> &[u8] { + while matches!(b.first(), Some(b' ') | Some(b'\t')) { + b = &b[1..]; + } + while matches!(b.last(), Some(b' ') | Some(b'\t')) { + b = &b[..b.len() - 1]; + } + b +} + +fn ci_eq(a: &[u8], b: &[u8]) -> bool { + a.len() == b.len() && a.iter().zip(b).all(|(x, y)| x.eq_ignore_ascii_case(y)) +} + +fn ci_contains(h: &[u8], n: &[u8]) -> bool { + !n.is_empty() && h.len() >= n.len() && h.windows(n.len()).any(|w| ci_eq(w, n)) +} + +fn parse_i64(b: &[u8]) -> i64 { + let b = trim(b); + let (neg, b) = match b.first() { + Some(b'-') => (true, &b[1..]), + _ => (false, b), + }; + let mut n = 0i64; + for &c in b { + if c.is_ascii_digit() { + n = n * 10 + (c - b'0') as i64; + } else { + break; + } + } + if neg { + -n + } else { + n + } +} + +fn parse_usize(b: &[u8]) -> Option { + let b = trim(b); + if b.is_empty() || !b.iter().all(|c| c.is_ascii_digit()) { + return None; + } + let mut n = 0usize; + for &c in b { + n = n.checked_mul(10)?.checked_add((c - b'0') as usize)?; + } + Some(n) +} + +fn parse_hex(b: &[u8]) -> Option { + let mut n = 0usize; + let mut any = false; + for &c in b { + let d = match c { + b'0'..=b'9' => c - b'0', + b'a'..=b'f' => c - b'a' + 10, + b'A'..=b'F' => c - b'A' + 10, + b';' | b' ' => break, // chunk extensions / padding + _ => return if any { Some(n) } else { None }, + }; + n = n.checked_mul(16)?.checked_add(d as usize)?; + any = true; + } + if any { + Some(n) + } else { + None + } +} From 2ed62c7b2ab3cd49f3a18592326a9747906f3d5a Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Sat, 6 Jun 2026 15:30:59 +0000 Subject: [PATCH 2/2] Benchmark results: tokio-uring --- site/data/baseline-4096.json | 20 +++++++++++++++++++ site/data/baseline-512.json | 20 +++++++++++++++++++ site/data/frameworks.json | 7 +++++++ site/data/limited-conn-4096.json | 20 +++++++++++++++++++ site/data/limited-conn-512.json | 20 +++++++++++++++++++ site/data/pipelined-4096.json | 19 ++++++++++++++++++ site/data/pipelined-512.json | 19 ++++++++++++++++++ .../static/logs/baseline/4096/tokio-uring.log | 0 site/static/logs/baseline/512/tokio-uring.log | 0 .../logs/limited-conn/4096/tokio-uring.log | 0 .../logs/limited-conn/512/tokio-uring.log | 0 .../logs/pipelined/4096/tokio-uring.log | 0 .../static/logs/pipelined/512/tokio-uring.log | 0 13 files changed, 125 insertions(+) create mode 100644 site/static/logs/baseline/4096/tokio-uring.log create mode 100644 site/static/logs/baseline/512/tokio-uring.log create mode 100644 site/static/logs/limited-conn/4096/tokio-uring.log create mode 100644 site/static/logs/limited-conn/512/tokio-uring.log create mode 100644 site/static/logs/pipelined/4096/tokio-uring.log create mode 100644 site/static/logs/pipelined/512/tokio-uring.log diff --git a/site/data/baseline-4096.json b/site/data/baseline-4096.json index b901c2e6..9a8183a4 100644 --- a/site/data/baseline-4096.json +++ b/site/data/baseline-4096.json @@ -1306,6 +1306,26 @@ "status_4xx": 0, "status_5xx": 0 }, + { + "framework": "tokio-uring", + "language": "Rust", + "rps": 3602450, + "avg_latency": "1.14ms", + "p99_latency": "1.69ms", + "cpu": "6619.8%", + "memory": "170MiB", + "connections": 4096, + "threads": 64, + "duration": "5s", + "pipeline": 1, + "bandwidth": "226.68MB/s", + "input_bw": "278.28MB/s", + "reconnects": 0, + "status_2xx": 18012254, + "status_3xx": 0, + "status_4xx": 0, + "status_5xx": 0 + }, { "framework": "traefik", "language": "Go", diff --git a/site/data/baseline-512.json b/site/data/baseline-512.json index 4f014b60..5c8293d5 100644 --- a/site/data/baseline-512.json +++ b/site/data/baseline-512.json @@ -1306,6 +1306,26 @@ "status_4xx": 0, "status_5xx": 0 }, + { + "framework": "tokio-uring", + "language": "Rust", + "rps": 3418714, + "avg_latency": "149us", + "p99_latency": "305us", + "cpu": "6575.9%", + "memory": "45MiB", + "connections": 512, + "threads": 64, + "duration": "5s", + "pipeline": 1, + "bandwidth": "215.13MB/s", + "input_bw": "264.09MB/s", + "reconnects": 0, + "status_2xx": 17093574, + "status_3xx": 0, + "status_4xx": 0, + "status_5xx": 0 + }, { "framework": "traefik", "language": "Go", diff --git a/site/data/frameworks.json b/site/data/frameworks.json index 461a48d9..0076088d 100644 --- a/site/data/frameworks.json +++ b/site/data/frameworks.json @@ -618,6 +618,13 @@ "type": "engine", "engine": "io_uring" }, + "tokio-uring": { + "dir": "tokio-uring", + "description": "Minimal HTTP/1.1 server on tokio-uring \u2014 the io_uring-backed Rust runtime with a completion/owned-buffer API. A hand-rolled request parser (Content-Length + chunked bodies, keep-alive, pipelining, fragmented reads), one runtime per core with SO_REUSEPORT. No HTTP framework.", + "repo": "https://github.com/tokio-rs/tokio-uring", + "type": "engine", + "engine": "io_uring" + }, "tokio-ws": { "dir": "tokio-ws", "description": "Hand-rolled WebSocket echo server on raw tokio \u2014 no WebSocket library. A current-thread runtime per core with SO_REUSEPORT sharding, a from-scratch RFC 6455 handshake (hand-written SHA-1 + base64) and a manual frame parser/masking + echo write path.", diff --git a/site/data/limited-conn-4096.json b/site/data/limited-conn-4096.json index e3691171..7738e191 100644 --- a/site/data/limited-conn-4096.json +++ b/site/data/limited-conn-4096.json @@ -1306,6 +1306,26 @@ "status_4xx": 0, "status_5xx": 0 }, + { + "framework": "tokio-uring", + "language": "Rust", + "rps": 2639285, + "avg_latency": "1.52ms", + "p99_latency": "20.80ms", + "cpu": "6393.3%", + "memory": "174MiB", + "connections": 4096, + "threads": 64, + "duration": "5s", + "pipeline": 1, + "bandwidth": "166.05MB/s", + "input_bw": "203.88MB/s", + "reconnects": 1319747, + "status_2xx": 13196427, + "status_3xx": 0, + "status_4xx": 0, + "status_5xx": 0 + }, { "framework": "traefik", "language": "Go", diff --git a/site/data/limited-conn-512.json b/site/data/limited-conn-512.json index c2fc938b..335a6c13 100644 --- a/site/data/limited-conn-512.json +++ b/site/data/limited-conn-512.json @@ -1306,6 +1306,26 @@ "status_4xx": 0, "status_5xx": 0 }, + { + "framework": "tokio-uring", + "language": "Rust", + "rps": 2237040, + "avg_latency": "215us", + "p99_latency": "731us", + "cpu": "5825.5%", + "memory": "94MiB", + "connections": 512, + "threads": 64, + "duration": "5s", + "pipeline": 1, + "bandwidth": "140.77MB/s", + "input_bw": "172.81MB/s", + "reconnects": 1118523, + "status_2xx": 11185203, + "status_3xx": 0, + "status_4xx": 0, + "status_5xx": 0 + }, { "framework": "traefik", "language": "Go", diff --git a/site/data/pipelined-4096.json b/site/data/pipelined-4096.json index 4187741a..89fe0e9b 100644 --- a/site/data/pipelined-4096.json +++ b/site/data/pipelined-4096.json @@ -1236,6 +1236,25 @@ "status_4xx": 0, "status_5xx": 0 }, + { + "framework": "tokio-uring", + "language": "Rust", + "rps": 48871257, + "avg_latency": "1.34ms", + "p99_latency": "2.12ms", + "cpu": "6569.8%", + "memory": "177MiB", + "connections": 4096, + "threads": 64, + "duration": "5s", + "pipeline": 16, + "bandwidth": "3.00GB/s", + "reconnects": 0, + "status_2xx": 244356288, + "status_3xx": 0, + "status_4xx": 0, + "status_5xx": 0 + }, { "framework": "traefik", "language": "Go", diff --git a/site/data/pipelined-512.json b/site/data/pipelined-512.json index c6cafa09..ecddb95b 100644 --- a/site/data/pipelined-512.json +++ b/site/data/pipelined-512.json @@ -1236,6 +1236,25 @@ "status_4xx": 0, "status_5xx": 2 }, + { + "framework": "tokio-uring", + "language": "Rust", + "rps": 46374713, + "avg_latency": "175us", + "p99_latency": "389us", + "cpu": "6594.7%", + "memory": "45MiB", + "connections": 512, + "threads": 64, + "duration": "5s", + "pipeline": 16, + "bandwidth": "2.85GB/s", + "reconnects": 0, + "status_2xx": 231873568, + "status_3xx": 0, + "status_4xx": 0, + "status_5xx": 0 + }, { "framework": "traefik", "language": "Go", diff --git a/site/static/logs/baseline/4096/tokio-uring.log b/site/static/logs/baseline/4096/tokio-uring.log new file mode 100644 index 00000000..e69de29b diff --git a/site/static/logs/baseline/512/tokio-uring.log b/site/static/logs/baseline/512/tokio-uring.log new file mode 100644 index 00000000..e69de29b diff --git a/site/static/logs/limited-conn/4096/tokio-uring.log b/site/static/logs/limited-conn/4096/tokio-uring.log new file mode 100644 index 00000000..e69de29b diff --git a/site/static/logs/limited-conn/512/tokio-uring.log b/site/static/logs/limited-conn/512/tokio-uring.log new file mode 100644 index 00000000..e69de29b diff --git a/site/static/logs/pipelined/4096/tokio-uring.log b/site/static/logs/pipelined/4096/tokio-uring.log new file mode 100644 index 00000000..e69de29b diff --git a/site/static/logs/pipelined/512/tokio-uring.log b/site/static/logs/pipelined/512/tokio-uring.log new file mode 100644 index 00000000..e69de29b