Benchmarks
2026-10-05 — M2 runtime benchmarks (Docker Desktop smoke run)
Not a performance claim. Docker Desktop on Windows (WSL2 VM, Ryzen 7 7800X3D), host shared with ~25 other running containers. Run-to-run variation on this host is ±15–20 % (e.g. scb static-small measured 71.7k and 58.6k req/s in two runs of the same build); compare servers only within one run.
Command (bench/README.md):
docker run --rm --cpuset-cpus 0-11 --cap-add SYS_NICE -e DURATION=10 \
-v scb-bench-www:/bench/www -v "$PWD/bench/results:/results" scb-bench- Server on CPUs 0-3 (4 workers/threads), application servers and the bench upstream on 4-5, oha 1.16.0 on 6-11; 64 connections; 3 s warm-up, 10 s measured.
- Same application servers for every web server: PHP-FPM 8.2 (
pm = static, 4 children, workers asnobody), Node.js 18 (one process), uvicorn 0.37 (2 workers), all on Unix sockets. scb starts and supervises its own copies; nginx 1.22 and Caddy 2.6 are put in front of identical externally started ones. cpu_scounts only the web server’s own processes (nginx: master + workers; scb:scb-webdwithout the application processes it supervises).
Results
server scenario rps p50_ms p95_ms p99_ms cpu_s peak_rss_mib
scb static-small 58607 0.97 2.16 3.5 29.15 13
scb static-1m 9501 6.4 11.72 16.59 35.09 14
scb respond 84335 0.58 1.88 3.25 21.14 14
scb proxy 42407 1.37 2.69 4.11 28.97 19
scb tls-h2-static-small 45750 1.27 2.62 4.1 30.61 20
scb php 14933 3.76 7.55 10.53 21.91 21
scb node 23986 2.41 5.15 7.09 18.79 22
scb python 11919 5.18 9.55 11.88 15.02 22
nginx static-small 98381 0.62 1.04 1.53 36.69 56
nginx static-1m 23076 2.27 6.9 10.19 35.48 56
nginx respond 241763 0.22 0.55 1.1 34.72 56
nginx proxy 62945 0.76 2.43 4.73 27.61 56
nginx tls-h2-static-small 82288 0.65 1.96 2.98 36.24 70
nginx php 21224 2.85 4.28 5.98 19.33 71
nginx node 24004 2.5 4.95 6.54 12.67 71
nginx python 10972 5.4 9.66 12.89 9.16 70
caddy static-small 31382 1.27 6.65 11.87 31.4 48
caddy static-1m 12819 3.33 15.29 23.81 30.2 48
caddy respond 52092 0.75 3.92 7.83 31.28 48
caddy proxy 23465 2.06 7.22 10.83 32.11 51
caddy tls-h2-static-small 22652 2.18 7.35 11.72 33.25 53
caddy php 12868 4.65 7.57 10.12 30.19 58
caddy node 25267 2.07 6.07 9.11 29.86 58
caddy python 11712 5.08 10.13 15.55 21.37 58All runs: 100 % success, 0 non-2xx responses.
Findings
- Node and Python are application-bound for all three servers: scb ≈ nginx ≈ Caddy (node 24.0k / 24.0k / 25.3k; python 11.9k / 11.0k / 11.7k). scb uses more CPU of its own than nginx for the same throughput (node 18.8 vs 12.7 CPU-s).
- PHP: scb 0.70× nginx (14.9k vs 21.2k), above Caddy (12.9k). At concurrency 1 and 4 scb’s latency equals nginx’s (0.38 ms vs 0.50 ms at c=1); the gap appears when 64 connections queue for 4 FPM workers. scb spends ~1.9× nginx’s CPU per PHP request; the profile shows no single hotspot. Recorded as an M2 follow-up (FastCGI connection reuse, params building).
- Static and proxy results reproduce
2026-10-05-static-profile.mdwithin host noise. - Harness bug found and fixed during this run: PHP-FPM workers run as
nobodyand could not be moved to the application CPUs withoutCAP_SYS_NICE, so in an earlier run they competed with scb for the server CPUs (PHP 12.2k). The harness now warns when re-pinning fails and the documented command adds--cap-add SYS_NICE.