Benchmark realista comparando frameworks PHP executando a mesma API de votação, em Docker com recursos idênticos e limitados. Mede latência (p95/p99), throughput sustentado, taxa de erro e eficiência de recursos.
Status: todos os 5 frameworks implementados e validados (smoke test 0% falhas). A variante PivotPHP usa o core 5.0 (
pivotphp/core ^5.0, core-routing 3.0 — roteador por instância); os demais estão inalterados.
| Framework | Diretório | Porta | Status |
|---|---|---|---|
| PivotPHP | pivotphp/ |
8080 | ✅ |
| Slim 4 | slim/ |
8081 | ✅ |
| Mezzio | mezzio/ |
8082 | ✅ |
| Symfony (microkernel) | symfony/ |
8083 | ✅ |
| Webman (async) | webman/ |
8084 | ✅ |
Cada framework é autocontido: Dockerfile, docker/ (nginx/php configs), app/ ou src/
(código da API), database/ (schema + seed) e docker-compose.yml próprios. O ambiente de
cada framework é isolado (banco e cache próprios), e um framework é medido por vez.
Limites estritos de recursos são garantidos via cgroups e Docker Compose:
- Aplicação: 1.0 vCPU / 512 MB RAM
- PostgreSQL: 2.0 vCPU / 2048 MB RAM
- Redis: 0.5 vCPU / 256 MB RAM
A suíte divide os testes em três grupos de medição (ver Metodologia detalhada):
Estado atual: os grupos A e B estão implementados apenas na variante PivotPHP — ainda não há comparação entre frameworks para eles (pendente: SPEC-109). O grupo C (votação) é o único comparável entre os 5 frameworks.
GET /api/health— JSON simples ({"status":"ok"})GET /api/ping— Resposta em texto puro (pong)
GET /api/products— Listagem paginada (page,limit) e ordenada por idGET /api/products/{id}— Detalhe de produto por chave primáriaPOST /api/products— Cadastro com validação de payload e inserção relacionalPATCH /api/products/{id}— Atualização parcial com invalidação de cacheDELETE /api/products/{id}— Exclusão física com invalidação de cacheGET /api/dashboard— Agregação analítica (COUNT,AVG,SUM, agrupamento por categoria) com cache no Redis
GET /api/candidates— Lista candidatos (cache Redis → PostgreSQL)GET /api/candidates/{id}— Detalhe de candidatoPOST /api/votes— Voto concorrente (validação de CPF, transação ACID com detecção de unicidade409)GET /api/votes/results— Resultados consolidados por candidatoDELETE /api/votes— Limpeza determinística para testes
pivotphp-benchmarks/
├── pivotphp/ # framework autocontido (implementado)
│ ├── Dockerfile # Nginx + PHP-FPM (idêntico entre frameworks)
│ ├── docker/ # nginx.conf, php.ini, www.conf (pm.max_children fixo)
│ ├── app/ # public/index.php + src/ (Controllers, Support)
│ ├── database/ # schema.sql + seed.sql
│ ├── composer.json
│ └── docker-compose.yml # postgres + redis + app (isolado)
├── slim/ # Slim 4 (idêntica estrutura)
├── mezzio/ # Mezzio (idêntica estrutura)
├── symfony/ # Symfony microkernel (idêntica estrutura)
├── webman/ # Webman async (own server, sem Nginx/PHP-FPM)
├── k6/ # smoke.js, vote-load.js, vote-spike.js, vote-soak.js
├── scripts/run-benchmark.sh # orquestrador
└── results/ # saídas (ignoradas no git)
# Todos os frameworks, cenário vote-load
./scripts/run-benchmark.sh
# Só um framework
./scripts/run-benchmark.sh pivotphp
# Cenário específico
./scripts/run-benchmark.sh slim spikeOs resultados brutos ficam em results/<timestamp>/. Cada framework sobe/desce a própria
stack (isolada) e o k6 roda smoke.js (validação) + o cenário escolhido.
cd pivotphp
docker compose up --build -d postgres redis app
# Smoke manual
curl http://localhost:8080/api/health
curl http://localhost:8080/api/candidates
curl -X POST http://localhost:8080/api/votes -H 'Content-Type: application/json' \
-d '{"candidateId":1,"cpf":"11144477735"}'
curl http://localhost:8080/api/votes/results
# Smoke via k6
docker compose --profile loadtest run --rm k6 run /scripts/smoke.jsOs relatórios consolidados ficam em reports/ e os dados brutos (k6 + smoke) em
results/<timestamp>/ (ignorados no git).
Referência — core 5.0, vote-load × 5 repetições (2026-10-11, APIs numa VM dedicada e carga gerada em
outro computador, ordem rotacionada · relatório):
| Framework | req/s (média ± IC95) | p50 | p95 | Erros | CPU app |
|---|---|---|---|---|---|
| Mezzio | 186,85 ± 1,00 | 640 ms | 2,34 s | 0,014% | 80% |
| Slim 4 | 186,21 ± 2,14 | 646 ms | 2,36 s | 0,012% | 80% |
| Symfony | 186,16 ± 0,87 | 651 ms | 2,35 s | 0,012% | 79% |
| PivotPHP 5.0 | 185,09 ± 0,82 | 657 ms | 2,38 s | 0,014% | 80% |
| Webman (worker persistente — ranking separado) | 523,26 ± 3,39 | 8 ms | 0,28 s | 0,000% | 18% |
Grupo PHP-FPM praticamente equivalente (185–187 req/s). O PivotPHP fica 0,6–0,9% abaixo dos demais; a diferença é estatisticamente significativa só contra o Mezzio (−0,9%) e indica um custo de CPU por requisição ligeiramente maior. O
vote-loadsatura os frameworks PHP-FPM (p95 > 2,3 s, acima dos thresholds): os números indicam capacidade sob fila, não latência dentro do SLO. Limitações da rodada no relatório.
Rodadas anteriores: prévia local de 2026-10-10 (host em uso, k6 no mesmo host e ordem fixa — superada por esta).
Execução remota (APIs numa VM, k6 em outro computador): reports/remote-setup.md e
./scripts/run-benchmark-remote.sh --host usuario@ip-da-vm --repeats 5 --warmup 1.
Consolidar repetições: python3 scripts/consolidate-runs.py --scenario vote-load results/<run1> results/<run2> ...
| Serviço | CPU | Memória |
|---|---|---|
| App PHP | 1.0 | 512M |
| PostgreSQL | 2.0 | 2G |
| Redis | 0.5 | 256M |
deploy.resourcesvale em Swarm mode oudocker compose --compatibility. O FPM pool (pm.max_children) é idêntico entre frameworks.