Framework Competition — Warm Start

RoadRunner/Octane-style resident worker: the framework boots once, then serves every request. Each number charges that boot per request, so a cell is the whole time one request keeps a worker busy — what a recycled pool or a queue behind one worker actually experiences. Full-stack request lifecycle benchmark (routing → controller → ORM query (SQLite) → template render → response).

PHP 8.3.33 · Linux 6.8.0-139-generic · warm mode · OPcache: yes · 1000 iterations per run over 30 runs · charts show the median as a faint bar + dot with the fastest → p95 range, lower is better

Boot model: boot included: the worker's recycle cost is added to every request

Framework startup — boot + teardown (cold + FPM rebuild + warm)
Framework startup — boot + teardown (cold + FPM rebuild + warm)
Total response times
Total response times
Peak memory footprint
Peak memory footprint

Feature benchmarks

Routing

GET / — dispatches a plain request through the router and returns a rendered template — no database access.

Routing
ORM / Active Record

GET /items — loads one page of the 1,000 seeded rows through each framework's ORM / Active Record layer: 20 items plus a COUNT for the pagination total.

ORM / Active Record
Query Builder

GET /items-qb — builds the same page of 20 items with each framework's query builder instead of its ORM, so the two data-access styles can be compared directly.

Query Builder
REST API (JSON)

GET /api/items — serves the same page of items as a JSON response rather than HTML, which adds serialization to the ORM work.

REST API (JSON)
AOP (Aspect-Oriented)

GET /features/aop — runs a request through an interceptor pipeline — logging, retry and middleware aspects wrapped around the handler. Only frameworks with an AOP layer take part.

AOP (Aspect-Oriented)
Cache

GET /features/cache — reads a COUNT(*) over the 1,000 rows through the framework's cache with a 10-second TTL, so a hit costs no database work and a miss runs the query.

Cache
Database Events

GET /features/db-events — inserts one event row per request and lets the framework's database events fire around that write.

Database Events
Event Dispatcher

GET /features/events — dispatches an in-process event to registered listeners.

Event Dispatcher
Validation

GET /features/validation — validates a payload with the framework's own validator.

Validation
Config

GET /features/config — resolves a value from the framework's config repository.

Config
Request-Scoped Services

GET /features/request-scoped — resolves a service scoped to the request from the container.

Request-Scoped Services
Rate Limiter

GET /features/rate-limit — checks a cache-backed rate limiter.

Rate Limiter

Latency by endpoint (ms, trimmed mean — lower is better) (sub-line: boot + handle + post-response cleanup, which sum to the total) — every row and chart point adds the worker's boot (warm recycle), so each number is the full time one request keeps that worker busy

RequestWorkloadAzeraLaravelSymfonySpiralCodeIgniterCakePHP
GET /no DB — routing + template only0.017 0.001+0.015+0.0012.73 2.52+0.21+0.000.620 0.533+0.085+0.0023.76 3.48+0.27+0.010.466 0.000+0.457+0.0090.136 0.006+0.129+0.001
GET /items20 of 1000 items (page 1, + COUNT)0.143 0.001+0.139+0.0033.25 2.51+0.74+0.001.07 0.54+0.53+0.004.03 3.48+0.53+0.020.761 0.000+0.749+0.0120.488 0.006+0.481+0.001
GET /items/11 item by id0.054 0.001+0.051+0.0022.90 2.51+0.39+0.000.701 0.534+0.165+0.0023.85 3.48+0.36+0.010.664 0.000+0.652+0.0120.313 0.006+0.306+0.001
POST /items1 row upserted (sentinel #999999)0.110 0.001+0.107+0.0022.93 2.52+0.41+0.000.789 0.533+0.253+0.0033.90 3.48+0.41+0.010.737 0.000+0.725+0.0120.423 0.006+0.416+0.001
GET /items-qb20 of 1000 items (page 1, + COUNT)0.095 0.001+0.093+0.0012.94 2.52+0.42+0.000.770 0.533+0.235+0.0023.86 3.49+0.36+0.010.747 0.000+0.735+0.0120.287 0.006+0.280+0.001
GET /items-qb/11 item by id0.051 0.001+0.049+0.0012.82 2.51+0.31+0.000.669 0.533+0.134+0.0023.81 3.48+0.32+0.010.656 0.000+0.644+0.0120.226 0.006+0.219+0.001
POST /items-qb1 row upserted (sentinel #999997)0.085 0.001+0.083+0.0012.91 2.51+0.40+0.000.807 0.533+0.271+0.0033.85 3.49+0.35+0.010.849 0.000+0.836+0.0130.295 0.006+0.288+0.001
GET /api/items20 of 1000 items as JSON0.041 0.001+0.039+0.0013.13 2.52+0.61+0.000.763 0.534+0.227+0.0023.90 3.49+0.40+0.010.610 0.000+0.600+0.0100.265 0.006+0.258+0.001
GET /api/items/11 item by id as JSON0.039 0.001+0.037+0.0012.91 2.51+0.40+0.000.678 0.533+0.143+0.0023.82 3.49+0.32+0.010.577 0.000+0.566+0.0110.233 0.006+0.226+0.001
POST /api/items1 row upserted (sentinel #999998)0.056 0.001+0.053+0.0022.86 2.51+0.35+0.000.772 0.534+0.236+0.0023.85 3.48+0.36+0.010.651 0.000+0.640+0.0110.320 0.006+0.313+0.001
GET /features/aopno DB — interceptor pipeline0.130 0.001+0.128+0.0012.84 2.52+0.32+0.000.759 0.534+0.223+0.0024.00 3.48+0.51+0.01——
GET /features/cacheCOUNT(*) of 1000 rows, cached 10s (miss = query)0.014 0.001+0.012+0.0012.76 2.52+0.24+0.000.618 0.533+0.083+0.0023.80 3.48+0.31+0.010.414 0.000+0.406+0.0080.106 0.006+0.099+0.001
GET /features/logno DB — buffered log handlers0.014 0.001+0.012+0.0012.73 2.51+0.22+0.000.614 0.533+0.079+0.0023.79 3.48+0.30+0.01——
GET /features/retryno DB — retry policy0.011 0.001+0.009+0.0012.74 2.51+0.23+0.000.620 0.534+0.084+0.0023.80 3.48+0.31+0.01——
GET /features/pipelineno DB — middleware pipeline0.015 0.001+0.013+0.0012.74 2.52+0.22+0.000.617 0.534+0.081+0.0023.80 3.48+0.31+0.01——
GET /features/db-events1 event row INSERTed per request0.049 0.001+0.047+0.0012.91 2.52+0.39+0.000.736 0.534+0.200+0.0023.97 3.48+0.48+0.010.742 0.000+0.730+0.0120.259 0.006+0.252+0.001
GET /features/eventsno DB — in-process listeners0.040 0.001+0.038+0.0012.81 2.51+0.30+0.000.649 0.533+0.114+0.0023.85 3.48+0.36+0.010.703 0.000+0.691+0.0120.151 0.006+0.144+0.001
GET /features/validationno DB — validator run0.020 0.001+0.018+0.0013.36 2.51+0.85+0.000.729 0.534+0.193+0.0023.83 3.49+0.33+0.010.684 0.000+0.671+0.0130.189 0.006+0.182+0.001
GET /features/configno DB — config lookup0.010 0.001+0.008+0.0012.74 2.52+0.22+0.000.616 0.533+0.081+0.0023.79 3.48+0.30+0.010.423 0.000+0.415+0.0080.095 0.006+0.088+0.001
GET /features/request-scopedno DB — scoped service resolve0.010 0.001+0.008+0.0012.73 2.51+0.22+0.000.614 0.533+0.079+0.0023.81 3.48+0.32+0.010.407 0.000+0.399+0.0080.094 0.006+0.087+0.001
GET /features/rate-limitno DB — cache-backed limiter0.011 0.001+0.009+0.0012.76 2.51+0.25+0.000.623 0.533+0.088+0.0023.82 3.48+0.33+0.010.414 0.000+0.406+0.0080.106 0.006+0.099+0.001