Internals: benchmark reports

ScalWS 1.0.0 on bare metal: the installed package with three warm instances (2026-11-12)

The first measurement of a release as it is installed: the 1.0.0 .deb on the Xeon host, scalws-controller running three scalwsd instances of two CPUs each, started before the measurement (no autoscaling), with the service sandbox on (what a new install gets). nginx and OpenLiteSpeed run on the same six CPUs. A second part measures scaling of the instances themselves against rc.1-11.

Comparison with nginx and OpenLiteSpeed

Host: 2x Xeon E5-2650 v2 (no AVX2), 121 GB, Ubuntu 26.04, kernel 7.0.0-38, performance governor, turbo off, no VM or container running. Server on CPUs 0-5 (socket 0), the upstream, PHP-FPM, Node.js and uvicorn on 6, 7, 22, 23, oha 1.16.0 on 8-15 and 24-31 (socket 1), 64 connections. bench/multi.sh MODE=release: 10 s warm-up, 30 s measurement, 5 runs with rotating order; medians. Every measurement of every server answered 2xx only; no run was excluded.

  • ScalWS: scalws_1.0.0-1_amd64.deb (the published package) under its systemd unit (SERVERS=scalws-service of bench/run.sh): instances: {min: 3, max: 3, cpus_per_instance: 2, cpus: "0-5"}, sandbox: on, the bench sites of bench/configs/scalws.yaml with the applications as the external processes the other servers use (the controller requires external endpoints). “own HTTP/1” adds http1_server: experimental and upstream_client: experimental (ADR-0046, ADR-0045); “defaults” is the package as installed.
  • nginx 1.31.6 (built from the signed source tarball; every ratio is against it) and 1.28.3 (Ubuntu package), worker_processes 6; OpenLiteSpeed 1.9.3, 6 workers.
  • LiteSpeed Web Server Enterprise is not in this run: its trial licence has run out.
  • The “defaults” and the nginx and OpenLiteSpeed columns are one session (09:27 to 11:32 UTC); “own HTTP/1” was measured afterwards with the same harness (13:05 to 13:38 UTC). nginx 1.31 measured again in between (diagnosis below) was within 2 % of this table.

Requests per second:

scenarioScalWS own HTTP/1ScalWS defaultsnginx 1.28nginx 1.31OpenLiteSpeed 1.9.3own / nginx 1.31defaults / nginx 1.31
static-small130 100123 148124 795126 22294 6841.030.98
static-1m12 20212 17612 35212 36011 7720.990.99
respond232 080213 699228 343248 483n/a0.930.86
proxy105 03789 22096 424106 90760 0160.980.83
cache-hit190 043172 069110 477111 97952 8461.701.54
tls-h2-static-small96 63698 44075 23576 03377 5141.271.29
php28 97928 90327 16727 27722 0011.061.06
node7 7897 9678 2848 1058 0770.960.98
python5 6546 0836 1646 2635 5180.900.97

OpenLiteSpeed has no fixed-response directive: respond is not measured for it.

p99 latency (ms, median of 5 runs):

scenarioScalWS own HTTP/1ScalWS defaultsnginx 1.28nginx 1.31OpenLiteSpeed
static-small0.650.730.660.653.17
static-1m7.927.927.117.4824.01
respond0.390.410.410.41n/a
proxy0.900.970.880.904.03
cache-hit0.490.520.740.695.30
tls-h2-static-small1.541.581.181.161.72
php2.512.532.652.648.11
node15.6815.4715.0115.4615.10
python17.1815.2016.5815.4413.45

Server CPU per request (µs, median of 5 runs; ScalWS: the controller and its instances, the unit’s whole cgroup):

scenarioScalWS own HTTP/1ScalWS defaultsnginx 1.28nginx 1.31OpenLiteSpeed
static-small46.3149.1348.0747.5154.44
static-1m494.70495.10485.65485.32434.34
respond26.0328.2726.2624.08n/a
proxy57.5267.7362.1855.6283.93
cache-hit31.8035.1254.2853.56102.06
tls-h2-static-small57.9957.0866.4766.6275.83
php115.83119.4995.1693.07132.86
node82.1595.2480.6474.0594.94
python105.75124.47109.25101.39142.94

Peak RSS: ScalWS 109 to 120 MiB (controller and three instances together), nginx 103 to 131 MiB, OpenLiteSpeed 55 to 70 MiB.

Findings:

  • ScalWS leads on the cached response path (1.54 to 1.70x nginx 1.31), HTTPS/2 (1.27 to 1.29x) and PHP (1.06x), with the lowest CPU per request on the first two; on PHP it uses more CPU per request than nginx (116 against 93 µs) while serving more requests per second.
  • With the package defaults, respond (0.86) and proxy (0.83) are behind nginx; with ScalWS’s own HTTP/1 server and client they are at 0.93 and 0.98, and static-small at 1.03.
  • With the own client, Python (0.90) and Node.js (0.96) are slower than with the default client (0.97, 0.98): to be understood before the own paths become the defaults (1.1, P10).

Where the default gap comes from

Same host and harness, SCENARIOS="respond proxy static-small", 3 rotating rounds of 10 s

  • 30 s, medians (results/diag-100, diag-100b):
variantrespondproxystatic-small
nginx 1.31245 590107 338125 697
scalwsd alone (6 workers, no controller, no sandbox)216 70090 270127 947
controller, 1 instance x 6 CPUs, sandbox on211 60087 781121 424
controller, 3 x 2, sandbox on214 27689 261122 108
controller, 3 x 2, sandbox off212 16588 731124 782
controller, 3 x 2, sandbox on, own HTTP/1 (second session)233 581105 389129 684

The controller, the number of instances and the sandbox move these by 1 to 3 %; scalwsd alone with the default paths has the same gap. The gap is the default HTTP/1 server and client (hyper’s); the own implementations close most of it.

Instances: three against one (bench/instances.sh)

Instance i on CPU i, one event loop each, PHP-FPM and the upstream on CPU 3, oha on socket 1; 3 runs of 10 s warm-up + 20 s, medians; scalwsd 1.0.0 against rc.1-11 (the 1.0.0 binary of the package; rc.1-11 the published 1.0.0~rc.1-11 package). Zero non-2xx answers.

scenario (64 connections)3 instances, 1.0.0/ rc.1-113 instances / 1 process with 3 loops
static-small84 2311.391.03
respond108 3351.381.02
cache-hit87 3331.441.02
tls-h2-static-small74 0511.461.04
proxy43 5701.321.00
static-1m10 4291.051.01
php11 1941.001.00
  • Three instances serve three times one instance (efficiency 0.98 to 1.04), and slightly more than one process with three event loops on the same CPUs.
  • PHP is bound by the single CPU of PHP-FPM in this topology: equal for every count and build. On one instance 1.0.0 is 1.17x rc.1-11 on PHP.
  • 1.0.0 needs 25 to 30 % less CPU per request than rc.1-11 on the web paths (the profile-guided build of ADR-0047 and the 1.0.0 fixes).

Raw results, in the bench host’s results directory: results/xeon-1.0.0-cmp, xeon-1.0.0-exp, xeon-inst-1.0.0, xeon-inst-rc1-11. (scalwsd --version in the environment records shows an older build first on the host’s PATH; the service runs the package’s /usr/bin/scalwsd 1.0.0.)