The core insight
php-fpm re-bootstraps the framework on every request (10–50ms), uses ~42MB per worker, and leaks statics between requests. Qbix loads the framework once, forks workers via pcntl_fork(), and each worker shares the parent’s memory via copy-on-write. A WordPress-like request dirties only 30 pages = 120KB.
php-fpm (every request): Load PHP → autoloader → boot framework (10-50ms) → run code → respond → statics leak Cost: 42MB per worker. 4 workers on 200MB. 78 req/s at 50ms I/O. Qbix (once at startup): Load PHP → autoloader → load ALL classes → snapshot statics → fork workers (inherit everything via COW) Qbix (every request): Worker receives request → run code → restore snapshot (0.03ms) Cost: 120KB per worker. 400+ on 200MB. 1,060 req/s at 50ms I/O.
Benchmarks (single-core, PHP 8.3)
CPU-bound (WordPress-like workload)
| Workers | req/s | vs fpm | |
|---|---|---|---|
| php-fpm | 4 | ~350 | — |
| Swoole | 4 | ~400 | 1.1× |
| Qbix | 100 | 2,294 | 6.6× |
I/O-bound (50ms database query)
| Workers | req/s | vs fpm | |
|---|---|---|---|
| php-fpm | 4 | 78 | — |
| Swoole (coroutines) | 4 | ~300 | ~4× |
| Qbix | 100 | 1,060 | 14× |
I/O-bound (200ms — real database load)
| Workers | req/s | vs fpm | |
|---|---|---|---|
| php-fpm | 4 | 20 | — |
| Swoole (coroutines) | 4 | ~200–500 | ~10–25× |
| Qbix | 200 | 488 | 24× |
Swoole requires Runtime::enableCoroutine() and coroutine-aware drivers. Unmodified WordPress uses blocking I/O and hits fpm’s ceiling.
COW measurements
Measured with /proc/smaps_rollup, 255 classes loaded, Linux 4KB pages:
| Request type | Dirty pages | Private memory |
|---|---|---|
| Bare fork (process setup) | 37 | 148KB |
| Minimal API | +23 | +92KB |
| WordPress-like (sessions, headers, arrays) | +30 | +120KB |
| Heavy response (1K items) | +50–200 | +200KB–800KB |
The 30MB of loaded framework stays shared. Workers only pay for pages they write to.
Scaling
Workers blocked on I/O don’t use CPU — the kernel doesn’t schedule them until data arrives. With 200ms I/O and ~5ms CPU per request, each worker uses the CPU 2.5% of the time. Hundreds of sleeping workers on a single core is fine.
| Machine | Workers | RAM used | 200ms I/O | vs fpm |
|---|---|---|---|---|
| 4GB, 2 cores | 400 | 47MB | ~976 req/s | 12× |
| 8GB, 4 cores | 800 | 94MB | ~1,952 req/s | 12× |
| 32GB, 8 cores | 2,000 | 235MB | ~4,880 req/s | 15× |
| 64GB, 16 cores | 4,000 | 469MB | ~9,760 req/s | 15× |
State reset
Between each request, all state is restored in 0.03ms via cached ReflectionProperty handles. 28 PHP functions are shimmed via source transformation. See Compatibility for the full list.