Benchmarks
2026-10-08 — Per-domain counters (ADR-0042): A/B gate
Host: development VM (8 vCPU EPYC 7642, Ubuntu 24.04). One binary (release,
--features http3), two configurations: metrics.hosts: false (off, via
bench/hosts-off-wrapper.sh, which adds the setting to the bench configuration and execs
the binary) and the default metrics.hosts: true (on). Variants alternate within
every run (bench/multi.sh), so host drift affects both alike.
Saturated throughput (MODE=release bench/multi.sh, 5 × 30 s, server on CPUs 0-3)
| Scenario | off req/s (median, CV) | on req/s (median, CV) | Δ req/s | off CPU µs/req | on CPU µs/req | RSS MiB off / on |
|---|---|---|---|---|---|---|
| respond | 101 592 (1.2 %) | 102 130 (1.2 %) | +0.5 % | 25.59 | 26.39 | 37 / 37 |
| static-small | 95 148 (3.8 %) | 95 920 (2.9 %) | +0.8 % | 39.53 | 39.20 | 37 / 37 |
| proxy | 76 709 (5.2 %) | 78 252 (4.3 %) | +2.0 % | 51.60 | 50.66 | 39 / 40 |
| PHP | 27 953 (11.7 %) | 29 176 (10.3 %) | +4.4 % | 89.65 | 85.68 | 45 / 44 |
No throughput regression in any scenario (gate: ≤ 1 %, 2 % hard limit). CPU per request from a saturated closed loop is noisy (signs differ between scenarios); it is measured at a fixed rate below.
CPU per request at a fixed rate (bench/cpu-fixed-rate.sh, 20 000 req/s, 6 alternating rounds, server on CPUs 0-1)
| Scenario | off µs/req (median, min–max) | on µs/req (median, min–max) | Δ |
|---|---|---|---|
| respond (HTTP/1.1) | 31.29 (29.08–32.08) | 30.88 (30.28–31.30) | −1.3 % |
| static-small (HTTP/1.1) | 38.22 (37.58–38.68) | 37.91 (37.35–38.83) | −0.8 % |
| TLS + HTTP/2 static | 31.43 (26.35–32.38) | 31.24 (30.95–33.43) | −0.6 % |
No increase (the differences are within the spread of the rounds). Gate passed:
metrics.hosts is on by default.
Allocations and memory
The request path gains no allocation: counters are preallocated per 64 domains and event loop on first use. RSS is unchanged within 1 MiB (table above).
Invalid runs (not used)
The first A/B attempt measured a leftover server that held port 8080 (the variants under
test exited with “address in use”, yet the harness saw answers). bench/run.sh now
checks that the server process it started is alive before measuring. The second attempt
used a build without the http3 feature, which the bench configuration needs; the new
check stopped it.