Cameleeringsimoom

simoom / Compare / hyper and axum vs simoom

Same droplet. Measured.

hyper and axum vs simoom

hyper is Rust's default HTTP library, and axum is the framework most teams build on top of it, both on tokio. simoom takes a different road: no async runtime, no third-party crates, HTTP/3 and TLS in the box. Benchmark campaign 4 (2026-11-12) measured hyper, axum, axum with rustls, actix-web and simoom on the same droplets in the same rounds. Here is what came out, including where they are ahead.

15 of 21campaign 4 measures where simoom is ahead of hyperlevel on 5, behind on 1
simoom 24% moreStatus endpoint, 256 clients: requests/s with 99% within 1 ms, against hypersimoom 137,889 req/s, hyper 110,931 req/s; axum: simoom 36% more; actix-web: simoom 29% more
simoom 26% lowerCPU per request at a fixed 50,000 requests/s, against hypersimoom 24.1 µs, hyper 32.6 µs; axum: simoom 34% lower; actix-web: simoom 23% lower
simoom 31x less10,000 idle connections: memory per connection, against hypersimoom 573 B, hyper 17,790 B; axum: simoom 38x less; actix-web: simoom 21x less

hyper, axum and simoom at a glance

Wins and losses in one table. The measured rows are from campaign 4: hyper 1.12.0, axum 0.8.9 and axum with rustls, each on the same DigitalOcean c-4 droplet as simoom in the same rounds, with simoom's figure from those same runs.

hyper / axumsimoom
What it ishyper: HTTP/1 and HTTP/2 library; axum: framework on tokio, tower and hyperHTTP/1.1, HTTP/2 and HTTP/3 server engine with its own TLS 1.3, WebSocket and a client
Runtimetokio (async/await)none: plain functions on one event loop per core
Third-party cratestokio, tower and their dependency trees0, not even for tests
HTTP/3not in hyper; the separate h3 crate describes itself as experimentalin-tree, a cargo feature; 54 of 58 QUIC Interop Runner pairs
TLSrustls or native-tls, chosen by youits own TLS 1.3; not independently audited yet
Middleware and ecosystemtower layers, axum extractors, many integrations, years of production usea router and optional helpers; version 0.1.0
Status endpoint, 256 clients (req/s, 99% within 1 ms)hyper 110,931; axum 101,477137,889 (24% more than hyper, 36% more than axum)
CPU per request at a fixed 50,000 req/shyper 32.6 µs; axum 35.6 µs23.6 µs
HTTPS API, 256 clients (req/s, 99% within 1 ms)axum + rustls 88,990134,004 (30% more)
New TLS 1.3 clients: full handshakes per secondaxum + rustls 11,6339,530 (axum + rustls 24% more)
Returning TLS 1.3 clients: resumed handshakes per secondaxum + rustls 11,46210,797 (axum + rustls 9% more)
HTTP/2, 64 connections x 100 streams (req/s, 99% within 5 ms)hyper 143,530; axum 124,765153,232
A new connection per request (req/s, 99% within 5 ms)hyper 16,75516,359 (level)
Memory per connection, 10,000 idle connectionshyper 17,790 B; axum 21,888 B573 B
Memory at rest (PSS)hyper 1,374 KB; axum 1,518 KB1,769 KB (hyper 26% less)
Start-up to first answerhyper 1.48 ms; axum 1.56 ms0.94 ms default build; 1.46 ms full server (level with hyper)
Program size, strippedhyper 2,069 KB; axum 2,246 KB; axum + rustls 5,400 KB735 KB default build; 1,550 KB full server
Traffic spike at 195,000 req/s offered, answers within 1 mshyper 2,179/s; axum 956/s2,743/s (all three far below actix-web's 59,015/s)

Campaign 4, 2026-11-12: every figure is the median of rounds, and simoom's figure comes from the same droplet and rounds as the server it is compared with, so it differs a little from row to row. Details in the benchmarks.

Every measure, head to head

measure (campaign 4)hyperaxumaxum + rustlsactix-web
Status endpoint, 64 clients: requests/s with 99% within 1 mssimoom 53% more84,364 req/s vs simoom 128,914 req/ssimoom 34% more96,227 req/s vs simoom 128,914 req/s–simoom 29% more99,838 req/s vs simoom 128,914 req/s
Status endpoint, 256 clients: requests/s with 99% within 1 mssimoom 24% more110,931 req/s vs simoom 137,889 req/ssimoom 36% more101,477 req/s vs simoom 137,890 req/s–simoom 29% more106,566 req/s vs simoom 137,890 req/s
Status endpoint, 1,024 clients: requests/s with 99% within 1 mssimoom 50% more102,154 req/s vs simoom 153,049 req/ssimoom 2.0x more76,953 req/s vs simoom 153,049 req/s–simoom 84% more83,023 req/s vs simoom 153,049 req/s
Latency at a fixed 50,000 requests/s (256 clients): p99simoom 7% lower418 µs vs simoom 387 µssimoom 14% lower404 µs vs simoom 346 µs–simoom 6% lower410 µs vs simoom 387 µs
CPU per request at a fixed 50,000 requests/ssimoom 26% lower32.6 µs vs simoom 24.1 µssimoom 34% lower35.6 µs vs simoom 23.6 µs–simoom 23% lower31.2 µs vs simoom 24.1 µs
JSON API, 1 KB in and out, 256 clients: requests/s with 99% within 1 mssimoom 19% more71,171 req/s vs simoom 84,755 req/ssimoom 27% more127,598 req/s vs simoom 161,438 req/s–simoom 56% more54,349 req/s vs simoom 84,755 req/s
HTTPS API, 256 clients: requests/s with 99% within 1 ms––simoom 30% more103,343 req/s vs simoom 134,004 req/s–
New TLS 1.3 clients: full handshakes per second––24% more than simoom11,633/s vs simoom 9,396/s–
Returning TLS 1.3 clients: resumed handshakes per second––9% more than simoom13,537/s vs simoom 12,388/s–
A new connection per request, 192 clients: requests/s with 99% within 5 mslevel16,378 req/s vs simoom 16,359 req/ssimoom 9% more15,009 req/s vs simoom 16,359 req/s–5% more than simoom17,137 req/s vs simoom 16,359 req/s
Service-to-service HTTP/2, 64 connections x 100 streams: requests/s with 99% within 5 mssimoom 7% more143,530 req/s vs simoom 153,232 req/ssimoom 23% more124,765 req/s vs simoom 153,232 req/s–simoom 2.1x more72,914 req/s vs simoom 153,232 req/s
64 KB downloads, 64 clients: requests/s with 99% within 5 mslevel3,776 req/s vs simoom 3,773 req/slevel3,779 req/s vs simoom 3,773 req/s–level3,780 req/s vs simoom 3,773 req/s
10,000 idle connections: memory per connectionsimoom 31x less17,790 B vs simoom 573 Bsimoom 38x less21,888 B vs simoom 573 B–simoom 21x less11,892 B vs simoom 573 B
100,000 idle connections: memory per connectionsimoom 30x less16,293 B vs simoom 535 Bsimoom 38x less20,356 B vs simoom 535 B–simoom 20x less10,649 B vs simoom 535 B
Traffic spike at 195,000 requests/s offered: answers within 1 ms per secondsimoom 26% more2,179 req/s vs simoom 2,743 req/ssimoom 2.9x more956 req/s vs simoom 2,743 req/s–22x more than simoom59,015 req/s vs simoom 2,743 req/s
Traffic spike: all answers per secondlevel157,030 req/s vs simoom 155,270 req/s3% more than simoom160,356 req/s vs simoom 155,270 req/s–level157,873 req/s vs simoom 155,270 req/s
Under 11,000 slow attackers: p99 of real requestslevel1,026 µs vs simoom 1,002 µssimoom 6% lower1,065 µs vs simoom 1,002 µs–simoom 4.4x lower4,428 µs vs simoom 1,002 µs
Start-up, process start to first answer: simoom's default build (HTTP/1.1, HTTP/2, WebSocket)simoom 57% faster1.48 ms vs simoom 0.94 mssimoom 66% faster1.56 ms vs simoom 0.94 mssimoom 2.0x faster1.91 ms vs simoom 0.94 mssimoom 93% faster1.81 ms vs simoom 0.94 ms
Start-up, process start to first answer: simoom's full server (also TLS 1.3 and HTTP/3)level1.48 ms vs simoom 1.46 mssimoom 7% faster1.56 ms vs simoom 1.46 mssimoom 31% faster1.91 ms vs simoom 1.46 mssimoom 24% faster1.81 ms vs simoom 1.46 ms
Program size, stripped: simoom's default build (HTTP/1.1, HTTP/2, WebSocket)simoom 2.8x smaller2,069 KB vs simoom 735 KBsimoom 3.1x smaller2,246 KB vs simoom 735 KBsimoom 7.3x smaller5,400 KB vs simoom 735 KBsimoom 3.3x smaller2,450 KB vs simoom 735 KB
Program size, stripped: simoom's full server (also TLS 1.3 and HTTP/3)simoom 25% smaller2,069 KB vs simoom 1,550 KBsimoom 31% smaller2,246 KB vs simoom 1,550 KBsimoom 3.5x smaller5,400 KB vs simoom 1,550 KBsimoom 37% smaller2,450 KB vs simoom 1,550 KB
Memory at rest (PSS)26% less than simoom1,315 KB vs simoom 1,769 KB17% less than simoom1,466 KB vs simoom 1,769 KBsimoom 43% less3,110 KB vs simoom 1,769 KBsimoom 7% less1,885 KB vs simoom 1,761 KB
Peak memory while serving the status endpoint (PSS)simoom 5.8x less16.1 MB vs simoom 2,848 KBsimoom 5.3x less14.9 MB vs simoom 2,848 KB–simoom 2.4x less6,912 KB vs simoom 2,880 KB
Peak memory while serving the JSON API (PSS)simoom 6.0x less17.4 MB vs simoom 2,988 KBsimoom 5.5x less16.0 MB vs simoom 2,988 KB–simoom 3.7x less10.9 MB vs simoom 2,988 KB
Peak memory while serving HTTPS (PSS)––simoom 2.3x less16.4 MB vs simoom 7,385 KB–

Each cell: simoom against that server on the same droplet in the same round, median of rounds, then the server's value against simoom's in the same runs. Every measure and every other server: the benchmarks.

When to pick hyper or axum instead

For many Rust services, axum on tokio is the right answer, and that is fine.

Pick hyper or axum when

  • Your program is already async and runs on tokio, or uses libraries that need it (database drivers, gRPC, cloud SDKs).
  • You want tower middleware, axum extractors, OpenAPI generators and the rest of the ecosystem.
  • Your team already knows axum, and hiring and onboarding matter more than footprint.
  • You need a library with years of production use and many contributors. simoom is at 0.1.0.
  • You want independently audited TLS: pair hyper with rustls. In campaign 4, axum with rustls also completed 24% more full TLS 1.3 handshakes per second than simoom (11,633 against 9,530).
  • Memory at rest matters more than memory per connection: hyper's idle process used 1,374 KB against simoom's 1,769 KB.

Pick simoom when

  • You don't want an async runtime in a program that never needed one: a CLI, a desktop app, a daemon, a device.
  • Every third-party crate is a review: simoom has none, from the TLS handshake to QUIC.
  • Binary size and start-up count: about 215 KB added for HTTP/1.1; in campaign 4 the default build was 735 KB stripped against hyper's 2,069 KB, and answered its first request 0.94 ms after process start against hyper's 1.48 ms.
  • You want HTTP/3 without a separate experimental stack.
  • Throughput per core matters and you can live with a smaller API.

What the code looks like

No async, no runtime to start. A route is a function that takes the request and a reply.

use simoom::{Outcome, Reply, Request, Router, Server};

let router = Router::new()
    .get("/health", |_: &mut Request<'_>, r: Reply<'_>| r.body_static(b"ok"))
    .get("/users/:id", |req: &mut Request<'_>, r: Reply<'_>| -> Outcome {
        let id = req.param_str("id").unwrap_or("").to_string();
        r.header("content-type", "application/json").body(format!("{{\"id\":\"{id}\"}}"))
    });

let server = Server::builder().listen("0.0.0.0:8080").service(router).start().expect("start");

Slow work (a database query, a call to another service) goes to the bounded offload pool with simoom::blocking, or to your own threads with Reply::defer. See routing and slow work.

How it was measured

Setup

  • DigitalOcean c-4 droplets (4 dedicated vCPUs, Xeon Platinum 8280, Bangalore), Ubuntu 24.04.
  • One server droplet, two separate load droplets, open-loop load: requests arrive at a fixed rate whether or not the server keeps up.
  • hyper 1.12.0, axum 0.8.9, axum-server 0.7.3 with rustls 0.23.46 and actix-web 4.18.2, each from its pinned Cargo.lock; simoom from the measured commit.

Rules

  • Headline: the highest steady rate whose p99 stays at or under 1 ms (5 ms for HTTP/2 with 100 streams and for a new connection per request).
  • Campaign 4, 2026-11-12: simoom ran on the same droplet in the same round as each competitor; every figure is the median of rounds (three or one per measure; the benchmarks list the runs).
  • Only servers on the same droplet are compared; raw HDR logs and scripts are in bench/results/.

Method and every number: benchmark method, the losses.

Common questions

Is simoom faster than hyper?

In benchmark campaign 4 (2026-11-12), with both servers on the same 4-vCPU droplet in the same rounds, simoom answered 137,889 status requests per second with 99% within 1 ms at 256 clients against hyper's 110,931 (24% more), and used 23.6 µs of CPU per request at a fixed 50,000 req/s against hyper's 32.6 µs (26% less). Of 21 measures simoom was ahead of hyper on 15, level on 5 and behind on 1: hyper's process used less memory at rest (1,374 KB against 1,769 KB). Start-up of simoom's full server (with TLS 1.3 and HTTP/3) was level with hyper, 1.46 ms against 1.48 ms; simoom's default build started in 0.94 ms.

Can I use hyper without tokio?

hyper is built for async Rust and in practice runs on tokio. If the goal is no async runtime at all, simoom is built that way: a service is a plain function on an event loop thread, and slow work goes to a bounded offload pool or to your own threads.

hyper vs axum vs simoom: which should I use?

For a typical web API on tokio with middleware, extractors and a team that knows the ecosystem, axum is the safer choice. For a low-level HTTP library inside the tokio world, use hyper. For an embedded server with no async runtime, no third-party crates, HTTP/3 and a small binary, use simoom.

Does simoom work with tower middleware?

No. simoom has its own Service type, a Router and optional helpers for compression, CORS, cookies, multipart, conditional requests and authentication. It does not implement tower's traits, so tower layers and axum extractors do not plug into it.

Does hyper support HTTP/3?

Not by itself. HTTP/3 for the hyper ecosystem lives in the separate h3 crate, which describes itself as experimental. simoom ships HTTP/3, QUIC v1 and TLS 1.3 in-tree as cargo features.

Try it next to your hyper service

Add the crate, serve the same route on another port, and measure both on your own machine.

cargo add simoom