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, crates scb-* = scalws-*, metrics scb_* = 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.

Commit71fc873 (tag pre-adaptive-runtime-2026-10-06)
Binaryscb-webd release, features http3, mimalloc; SHA-256 79399e0865066900b0b1398c3163c0b18ce25d86be79109ce9956df8a643b03d
Toolchainrustc 1.99.0 (b940084d7 2026-09-28), cargo 1.99.0
HostVMware VM, AMD EPYC 7642, 8 vCPU, 31 GiB, Ubuntu 24.04, kernel 6.8.0-146
Peersnginx 1.24.0, oha 1.16.0, PHP-FPM 8.3 (4 static workers), Node 18, uvicorn (2 workers)
MethodMODE=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
Resultsserver [private host path] (archived in the backup)
Testsscripts/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 / 50

nginx 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_cost tool 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)

ChangeEvidence
Skip the cache stage for static files without default_ttlstatic-1m 8.39k → 8.76k, CPU 238 → 228 µs/req
x-cache lookup only after the cache stage ranpipeline 2.85 → 2.77 µs (5/5 rounds)
One clock read in finish5/5 rounds lower
One Arc<Shared> per request5/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