Benchmarks
Pre-Adaptive-Runtime baseline (frozen 2026-10-06)
Naming: written before the rename to Scale Web Server (ScalWS, 2026-10-07). Names here are those of the artefacts at the time:
scb-webd=scalwsd,scbctl=scalwsctl,scb-controller=scalws-controller, cratesscb-*=scalws-*, metricsscb_*=scalws_*, paths/etc/scb=/etc/scalws.
The authoritative single-instance reference for the Adaptive Runtime work: regression gates compare against these numbers with Adaptive Runtime disabled.
| Commit | 71fc873 (tag pre-adaptive-runtime-2026-10-06) |
| Binary | scb-webd release, features http3, mimalloc; SHA-256 79399e0865066900b0b1398c3163c0b18ce25d86be79109ce9956df8a643b03d |
| Toolchain | rustc 1.99.0 (b940084d7 2026-09-28), cargo 1.99.0 |
| Host | VMware VM, AMD EPYC 7642, 8 vCPU, 31 GiB, Ubuntu 24.04, kernel 6.8.0-146 |
| Peers | nginx 1.24.0, oha 1.16.0, PHP-FPM 8.3 (4 static workers), Node 18, uvicorn (2 workers) |
| Method | MODE=release bench/multi.sh: 10 s warm-up, 30 s measurement, 5 runs, medians, server order rotated per run; server CPUs 0-1, applications 2-3, oha 4-7; 64 connections; NOFILE 65535 for every process; open file cache with a check per hit and kTLS on both servers; nginx sends the same forwarding headers |
| Results | server [private host path] (archived in the backup) |
| Tests | scripts/linux-test.sh on the same host: 301 passed, 0 failed, 1 ignored (the explicit measurement tool) |
Results (requests/s, median of 5)
scenario scb nginx scb/nginx CV% scb/nginx p99 ms scb/nginx CPU us/req scb/nginx RSS MiB scb/nginx
static-small 77.7k 71.9k 1.08× 9.6 / 9.8 1.52 / 1.54 25.7 / 27.8 33 / 46
static-1m 8.28k 8.61k 0.96× 5.2 / 8.0 12.04 / 11.81 241.4 / 232.1 33 / 42
respond 97.1k 114.7k 0.85× 6.1 / 11.4 1.15 / 0.92 20.6 / 17.4 33 / 42
proxy 46.7k 50.4k 0.93× 9.2 / 6.6 2.66 / 2.00 42.7 / 39.6 36 / 43
cache-hit 80.8k 64.6k 1.25× 7.1 / 6.0 1.35 / 1.61 24.7 / 31.0 36 / 43
tls-h2-static-small 64.7k 44.2k 1.47× 7.9 / 6.8 1.76 / 2.44 30.8 / 45.2 41 / 50
tls-static-1m 1.56k 1.53k 1.02× 4.7 / 4.9 79.1 / 65.3 1280.7 / 1305.1 42 / 50
php 28.2k 26.2k 1.07× 5.2 / 4.8 3.50 / 4.06 64.3 / 68.7 42 / 50
node 22.2k 28.1k 0.79× 4.2 / 4.1 5.62 / 4.77 61.5 / 45.7 42 / 50
python 12.2k 11.9k 1.02× 3.5 / 2.7 11.56 / 12.28 92.4 / 80.4 42 / 50nginx python has 4 valid runs: run 4 answered only 502 (the external uvicorn was not
ready); aggregate.py now excludes such runs and run.sh waits for the sockets.
cache-hit per run (scb/nginx): 1.18, 1.41, 1.25, 1.13, 1.24.
Known gaps and caveats
- Host variance. The same build drifts by up to ±30 % over minutes on this VM (the
CPU-only
pipeline_costtool moved between 2.0 and 3.7 µs/request with no change). Only interleaved A/B runs decide; absolute numbers from different times are not comparable. Cache-hit variance (A4) is this: per-request CPU varies run to run while the server uses exactly 2.0 CPUs; connection distribution and loop pinning were tested and ruled out. - respond 0.85×, proxy 0.93×. The difference is user-space CPU (kernel time is equal or lower than nginx’s; same syscalls and TCP segments). The proxy remainder is spread through hyper’s client; closing it would mean replacing hyper (out of scope).
- node 0.79×. Node.js saturates its CPU in both setups but spends more CPU per request behind scb (~45.6 µs) than behind nginx (~36 µs) — open investigation.
- static-1m 0.96×. About one more TCP segment per response than nginx.
Work in this phase (each change verified by an interleaved A/B)
| Change | Evidence |
|---|---|
Skip the cache stage for static files without default_ttl | static-1m 8.39k → 8.76k, CPU 238 → 228 µs/req |
x-cache lookup only after the cache stage ran | pipeline 2.85 → 2.77 µs (5/5 rounds) |
One clock read in finish | 5/5 rounds lower |
One Arc<Shared> per request | 5/5 rounds lower |
| Lock-free request-path histograms (identical encoding, tested) | 4/5 rounds lower, ~−3 % |
| Single-pass host validation (property-tested against the old one) | 12/15 rounds lower |
Reverted: unpinned event loops (SCB_NO_PIN) | no change in median or spread |