Skip to content

v0.2.0

Choose a tag to compare

@github-actions github-actions released this 29 Aug 13:15
· 85 commits to master since this release

All three protocol stacks are now written for this one job rather than taken
from a general-purpose crate, and shb leads wrk and h2load on every protocol.

Measured against nginx 1.31.4 on loopback, 10 s per run, 16 threads for every
tool. See the README for the full method and caveats.

Protocol Config shb wrk h2load
HTTP/1.1 1000 connections 993,170 856,476 796,238
HTTP/2 (h2c) 32 conns × 32 streams 932,839 885,527
HTTP/2 (h2c) 100 conns × 100 streams 1,255,321 1,205,942
HTTP/3 32 conns × 32 streams 2,054,015 1,466,884

What changed

Purpose-built protocol stacks. A load generator only needs to know where
one message ends and the next begins, plus the status code to tally. All three
stacks now do exactly that:

  • HTTP/1.1 responses are scanned rather than parsed: the status line,
    Content-Length, Transfer-Encoding and Connection are read, every other
    header is stepped over. Nothing is allocated per response.
  • HTTP/2 and HTTP/3 requests are one HPACK/QPACK block encoded once at
    start-up from static-table references, so the encoder never touches a
    dynamic table. The client advertises SETTINGS_HEADER_TABLE_SIZE: 0 and
    QPACK_MAX_TABLE_CAPACITY: 0, which stops the peer indexing too — responses
    then decode without a dynamic table, and only :status is read.

That last point is where the HTTP/3 gain comes from: a profile of a saturated
worker used to spend 47% of its time Huffman-decoding header values nothing
looked at.

Completion batching. One io_uring_enter now covers a batch of
completions instead of one each, bounded by min_wait_usec so a batch that
cannot be filled does not stall. Needs Linux 6.12; without it each wait
returns on the first completion, as before.

--disable-keepalive, which reconnects for every request (HTTP/1.1 only).

Fixes

  • MTU discovery was configured with an upper bound of 65527. That is QUIC's
    limit but not the kernel's — IPv4 cannot carry more than 65507 bytes of UDP
    payload — so the probe came back EMSGSIZE, which was treated as a dead
    connection and failed every request in flight. It cost about 4% of all
    HTTP/3 requests.

Notes

  • The comparison in previous READMEs was measured against a server that
    saturated before any of the clients did, which flattered every number.
    It is now nginx with headroom, and the results changed materially.
  • MSRV is 1.91.