Performance

One trick — copy-on-write — makes everything else possible.

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)
Workersreq/svs fpm
php-fpm4~350—
Swoole4~4001.1×
Qbix1002,2946.6×
I/O-bound (50ms database query)
Workersreq/svs fpm
php-fpm478—
Swoole (coroutines)4~300~4×
Qbix1001,06014×
I/O-bound (200ms — real database load)
Workersreq/svs fpm
php-fpm420—
Swoole (coroutines)4~200–500~10–25×
Qbix20048824×

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 typeDirty pagesPrivate memory
Bare fork (process setup)37148KB
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.

MachineWorkersRAM used200ms I/Ovs fpm
4GB, 2 cores40047MB~976 req/s12×
8GB, 4 cores80094MB~1,952 req/s12×
32GB, 8 cores2,000235MB~4,880 req/s15×
64GB, 16 cores4,000469MB~9,760 req/s15×

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.

Full methodology on GitHub →