h2load-h3

h2load is part of the nghttp2 project. HttpArena uses a custom build that links against ngtcp2, nghttp3, and quictls to enable HTTP/3 (QUIC) support. This build is invoked as h2load-h3 and drives both baseline-h3 and static-h3 profiles.

Installation

The h2load binary shipped by Linux distributions does not include HTTP/3. HttpArena builds its own image:

docker build -f docker/h2load-h3.Dockerfile -t h2load-h3:local docker/

For native execution under benchmark.sh, the binary and runtime libraries are extracted from the image to /opt/h2load-h3/ and a wrapper is installed at /usr/local/bin/h2load-h3 that sets LD_LIBRARY_PATH and execs the binary.

How it’s used

h2load-h3 --alpn-list=h3 https://localhost:8443/baseline2?a=1&b=1 \
  -c 64 -m 64 -t 64 -D 5s

For multi-URI static file tests:

h2load-h3 --alpn-list=h3 -i requests/static-h2-uris.txt \
  -H "Accept-Encoding: br;q=1, gzip;q=0.8" \
  -c 64 -m 64 -t 64 -D 5s
FlagDescriptionValue
--alpn-list=h3Negotiate HTTP/3 over QUIC
-cNumber of QUIC connections64
-mMax concurrent streams per connection64
-tWorker threads64
-DDuration5s
-iURI list file (multi-URL rotation)

Why h2load-h3 instead of oha?

The previous HTTP/3 driver (oha) topped out at ~85k req/s and could not saturate any framework — its single-threaded quinn client became the bottleneck before the server. h2load-h3 uses ngtcp2’s multi-threaded worker model with sendmmsg / recvmmsg syscall batching, reaching ~580k req/s on the same hardware against the same server (Kestrel + msquic). Both ends are CPU-saturated at that point, which makes the result a fair measurement of the framework’s QUIC stack rather than the load generator’s.

Why HTTP/3 results are still lower than HTTP/2

HTTP/3 carries an inherent CPU cost vs HTTP/2 that loopback benchmarks make particularly visible:

  • Per-packet AEAD encryption with no kernel TLS offload
  • All packet processing in userspace via msquic / ngtcp2
  • More syscalls per request than TCP’s buffered read/write paths
  • Loopback at MTU 1500 — required for proper GSO/GRO behavior — gives no help from a real NIC

Expect a 4-6× gap between h3 and h2 numbers for the same framework. This reflects the state of the QUIC ecosystem in 2026, not a configuration issue.