Benchmarks
2026-10-07 — Adaptive Runtime: scaling, autoscaling and stability
Host: development VM (8 vCPU EPYC 7642, Ubuntu 24.04, kernel 6.8), tcp_migrate_req=1.
Topology separate from the frozen 2-CPU baseline: instance i on CPU i (one event loop
each), upstream and PHP-FPM (4 workers) on CPU 3, oha on CPUs 4-7.
Regression gates (single instance, Adaptive Runtime off, vs. the frozen baseline)
Interleaved release A/B (5 × 30 s), baseline binary 79399e08… against the build of
each stage:
| Scenario | after D1 | after D3/D4 |
|---|---|---|
| respond | +2.0 % | −1.4 % |
| static-small | +1.2 % | +2.3 % |
| cache-hit | +0.8 % | +0.5 % |
| TLS + HTTP/2 | +3.5 % | +0.1 % |
| PHP | +4.8 % | +4.3 % |
All within ±5 %; p99 and CPU/request within limits.
D1 — N instances (reuseport) vs. one instance with the same CPUs
bench/instances.sh, 3 runs × 10 s, medians; eff = rps(N) / (rps(1) × N).
| Scenario (conns) | 1 inst | 2 inst | eff | vs 1×2 loops | 3 inst | eff | vs 1×3 loops |
|---|---|---|---|---|---|---|---|
| respond (64) | 51.4k | 92.5k | 0.90 | 1.04× | 166.8k | 1.08 | 1.14× |
| respond (256) | 52.4k | 102.6k | 0.98 | 1.08× | 159.0k | 1.01 | 0.97× |
| static-small (64) | 43.5k | 75.9k | 0.87 | 0.92× | 127.3k | 0.97 | 1.02× |
| static-small (256) | 43.2k | 82.2k | 0.95 | 1.00× | 144.2k | 1.11 | 1.08× |
| static-1m (64) | 4.22k | 8.96k | 1.06 | 1.07× | 13.05k | 1.03 | 1.06× |
| cache-hit (64) | 46.6k | 87.9k | 0.94 | 0.95× | 152.3k | 1.09 | 1.05× |
| TLS+H2 (256) | 34.0k | 65.4k | 0.96 | 0.92× | 115.8k | 1.13 | 1.08× |
| proxy (256) | 25.4k | 47.3k | 0.93 | 1.08× | 65.6k | 0.86 | 1.06× |
| php (64) | 13.3k | 15.4k | 0.58 | 0.95× | 15.8k | 0.40 | 1.04× |
- Web-tier paths scale close to linearly, but no better than one instance given the same CPUs (0.92–1.14×): multi-instance buys elasticity, not efficiency.
- RSS: 26–30 MiB per instance (3 instances 79 MiB vs. 33 MiB for one 3-loop instance).
- Connection share with 64 connections can be uneven (e.g. 25/26/13); with 256 it is even.
- PHP is bound by PHP-FPM on one CPU: efficiency 0.4–0.6 — exactly the case the classifier must not answer with more scb instances.
- Zero non-2xx responses at every point.
D5 — autoscaling under load (web-tier mode, min 1, max 3)
Concurrency sweep, 20 s per point, scripts/controller-d5.sh:
| Path | conns | req/s | p99 ms | errors | instances | RSS MiB |
|---|---|---|---|---|---|---|
| respond | 8 | 43.6k | 0.28 | 0 | 1 | 26 |
| respond | 32 | 49.3k | 1.04 | 0 | 2 | 54 |
| respond | 64 | 99.1k | 1.10 | 0 | 3 | 81 |
| respond | 256 | 154.6k | 3.00 | 0 | 3 | 84 |
| respond | 1024 | 167.0k | 11.24 | 0 | 3 | 102 |
| static-small | 64 | 127.3k | 0.95 | 0 | 3 | 80 |
| static-small | 1024 | 121.0k | 15.18 | 0 | 3 | 98 |
Bursts from idle to 1024 connections for 5 s (three in a row, 8 s apart): 37.2k → 81.5k → 143.2k req/s while the controller grew 1 → 2 → 3 instances; p99 46 → 19 → 14 ms; zero errors. The pool scaled back to one instance after each sweep.
Compared with a fixed single instance on one CPU (~44–52k req/s, D1), the autoscaled pool reached ~3× the throughput at the same p99 band once three instances were active — on three times the CPUs, as D1 predicts.