Benchmarks de Performance
Benchmarks de performance reais comparando TCPDF-Next contra três bibliotecas PHP PDF estabelecidas: TCPDF, DomPDF e mPDF. Todos os testes foram executados no mesmo hardware sob condições Docker controladas. Os resultados são medianas de 20 iterações para eliminar ruído de outliers.
Ambiente de Teste
| Parâmetro | Valor |
|---|---|
| CPU | Intel Core i9-13900K (x86-64) |
| RAM | 64 GB DDR5 (Docker limitado a 16 GB) |
| Docker | 4 CPUs, 16 GB RAM, Debian bookworm |
| PHP | 8.5.3 (CLI, OPcache habilitado, JIT habilitado) |
| TCPDF-Next | 1.7.0 |
| TCPDF | 6.10.1 |
| DomPDF | v3.1.4 |
| mPDF | v8.2.7 |
| Artisan (Chrome) | Headless Chromium via CDP |
| RoadRunner | spiral/roadrunner-http ^3.6 (testes de throughput HTTP) |
| Warmup | 3 iterações (descartadas) |
| Medições | 20 iterações (mediana reportada) |
| Cronometragem | hrtime(true) relógio de parede com precisão de nanossegundos |
Comparação Interativa
PHP 8.5.3 + OPcache + JIT · Docker 4 CPUs / 16 GB · i9-13900K · Median of 20 runs
Velocidade de Geração
Cada cenário foi executado 20 vezes após 3 iterações de warmup. O tempo mediano de geração é reportado.
Documento Simples (1 Página)
Uma única página A4 com um heading e texto formatado básico usando a fonte Helvetica embutida. Sem imagens, sem tabelas.
| Biblioteca | Tempo (ms) |
|---|---|
| TCPDF-Next | 0,68 |
| TCPDF | 2,55 |
| DomPDF | 4,16 |
| mPDF | 6,71 |
O TCPDF-Next completa o cenário mais simples em menos de 1 ms -- 3,8x mais rápido que TCPDF, 6,1x mais rápido que DomPDF e 9,9x mais rápido que mPDF.
Fatura (2 Páginas)
Uma fatura de duas páginas com 25 linhas tabulares de itens, subtotais, cabeçalhos, rodapés e uma imagem de logo.
| Biblioteca | Tempo (ms) |
|---|---|
| TCPDF | 1,96 |
| TCPDF-Next | 2,01 |
| mPDF | 15,86 |
| DomPDF | 17,33 |
TCPDF-Next e TCPDF estão virtualmente empatados no cenário de Fatura (~1,0x). Ambos superam significativamente mPDF (7,9x mais lento) e DomPDF (8,6x mais lento).
Relatório de 100 Páginas
Um documento de 100 páginas com conteúdo misto denso: headings, parágrafos e dados estruturados.
| Biblioteca | Tempo (ms) |
|---|---|
| TCPDF-Next | 34,29 |
| TCPDF | 105,39 |
| mPDF | 1.106,59* |
| DomPDF | 2.129,12 |
O TCPDF-Next completa um relatório de 100 páginas em 34,29 ms -- 3,1x mais rápido que TCPDF, 32,3x mais rápido que mPDF e 62,1x mais rápido que DomPDF.
Nota de Compatibilidade JIT
*O resultado do mPDF no Relatório de 100 Páginas foi medido com JIT desabilitado (opcache.jit=0) devido a um segfault do PHP JIT (SIGSEGV, código de saída 139) no caminho de código do mPDF. O cache de bytecode do OPcache permaneceu ativo. Esta é uma classe conhecida de bugs do PHP JIT que afeta certos padrões de loop complexos. Todos os outros cenários do mPDF foram executados com JIT habilitado.
Documento TrueType (1 Página)
Uma única página A4 usando DejaVu Sans (~700 KB fonte TrueType). Este cenário expõe os custos reais de parsing de arquivo de fonte -- diferente do Helvetica (fonte Base14 embutida que não requer I/O de arquivo).
| Biblioteca | Tempo (ms) |
|---|---|
| TCPDF-Next | 4,08 |
| TCPDF | 12,11 |
| mPDF | 16,51 |
| DomPDF | 24,14 |
O TCPDF-Next faz o parse e incorpora a fonte TrueType 3,0x mais rápido que TCPDF, 4,0x mais rápido que mPDF e 5,9x mais rápido que DomPDF.
Velocidade Relativa (Todos os Cenários)
Todos os valores relativos ao TCPDF-Next (baseline 1,0x). Menor = mais rápido.
| Cenário | TCPDF-Next | TCPDF | DomPDF | mPDF |
|---|---|---|---|---|
| Documento Simples | 1,0x | 3,8x | 6,1x | 9,9x |
| Fatura | 1,0x | ~1,0x | 8,6x | 7,9x |
| Relatório de 100 Páginas | 1,0x | 3,1x | 62,1x | 32,3x |
| Documento TrueType | 1,0x | 3,0x | 5,9x | 4,0x |
HTML para PDF
Abordagens de Processamento HTML
Diferentes bibliotecas adotam abordagens fundamentalmente diferentes para converter HTML em PDF. Entender essas diferenças é essencial para interpretar os resultados do benchmark:
Tradução Direta (TCPDF-Next, TCPDF) -- O parser HTML embutido tokeniza tags HTML e as mapeia diretamente para comandos de desenho PDF (Cell, MultiCell, Image) em um único passo de streaming. Esta abordagem é extremamente rápida, mas suporta apenas tags HTML básicas e CSS inline -- sem Flexbox, sem Grid, sem seletores CSS complexos.
Engine de Layout CSS (DomPDF, mPDF) -- Estas bibliotecas são projetadas com HTML como sua interface primária. O DomPDF constrói uma árvore DOM completa, aplica a cascata CSS (especificidade, herança) e calcula o layout do box-model antes de renderizar em PDF. O WriteHTML() do mPDF similarmente processa HTML através de sua própria engine de layout CSS. Ambos suportam mais recursos CSS do que parsers de tradução direta (floats, elementos posicionados, tabelas estilizadas), mas ainda ficam aquém do CSS3 completo a nível de navegador.
Renderização Completa do Navegador (Artisan / Chrome) -- O TCPDF-NextArtisan delega a renderização ao Chromium headless via Chrome DevTools Protocol (CDP). Isso fornece suporte CSS3 pixel-perfect: Flexbox, Grid, Web Fonts, media queries, variáveis CSS -- saída idêntica ao que um navegador Chrome produziria.
O benchmark compara a abordagem nativa de cada biblioteca: TCPDF-Next e TCPDF usam seu parser de tradução direta embutido; DomPDF e mPDF usam suas engines de renderização CSS (sua API primária); Artisan usa Chrome.
Resultados
| Biblioteca | Abordagem | Tempo (ms) |
|---|---|---|
| TCPDF-Next | Tradução direta | 1,51 |
| TCPDF | Tradução direta | 6,60 |
| DomPDF | Engine de layout CSS | 13,69 |
| mPDF | Engine de layout CSS | 29,63 |
| Artisan (Chrome) | Renderização completa do navegador | 66,70 |
Tempo Relativo (HTML para PDF)
| Biblioteca | Relativo |
|---|---|
| TCPDF-Next | 1,0x |
| TCPDF | 4,4x |
| DomPDF | 9,0x |
| mPDF | 19,6x |
| Artisan (Chrome) | 44,1x |
O parser de tradução direta do TCPDF-Next entrega performance abaixo de 2 ms -- 4,4x mais rápido que o parser baseado em regex do TCPDF, 9,0x mais rápido que a engine de layout CSS do DomPDF e 19,6x mais rápido que mPDF. O Artisan (Chrome) é 44,1x mais lento, mas fornece fidelidade CSS3 completa que nenhuma outra biblioteca consegue igualar.
Artisan Chrome -- Detalhamento por Fases
Decompõe o pipeline do Artisan (Chrome) em duas fases:
- Chrome CDP Render -- Chrome headless converte HTML para bytes PDF via
printToPDF - PDF Import + Embed -- TCPDF-Next faz parse do PDF do Chrome, extrai a página como Form XObject e incorpora no documento de destino
| Fase | Mediana (ms) | Média (ms) | Mín (ms) | Máx (ms) | Desvpad |
|---|---|---|---|---|---|
| Chrome CDP Render | 81,17 | 81,17 | 65,51 | 95,80 | 4,84 |
| PDF Import + Embed | 1,96 | 2,08 | 1,60 | 2,87 | 0,40 |
| Total | 83,35 | 83,29 | 68,20 | 97,56 | 4,70 |
Distribuição de tempo: Chrome CDP = 97,4% | PDF Import = 2,3%
O printToPDF do Chrome domina o pipeline com 97,4% do tempo total. A fase de PDF Import (PdfReader + PageImporter + embedding do XObject) adiciona apenas ~2 ms -- overhead desprezível.
Medição Padrão vs Por Fases
O teste padrão do Artisan (66,70 ms) usa o método integrado writeHtmlChrome() com BrowserPool keep-alive. O teste por fases (83,35 ms total) instrumenta separadamente cada fase, adicionando overhead de medição. Ambos usam a mesma instância Chrome keep-alive -- o custo de cold-start de ~250 ms para o lançamento inicial do Chromium é excluído, pois é um custo único amortizado em milhares de requisições.
Quando Usar Qual Abordagem
Para HTML simples (tabelas, formatação básica), use o parser HTML embutido do TCPDF-Next (1,51 ms). Para layouts CSS3 complexos que requerem fidelidade pixel-perfect (Flexbox, Grid, Web Fonts), use o Artisan -- o overhead de ~67 ms compra todo o poder da engine de renderização do Chrome.
Ciclo de Vida do Worker (DocumentFactory vs Standalone)
O TCPDF-Next fornece um padrão DocumentFactory projetado para workers PHP de longa duração (RoadRunner, Swoole, Laravel Octane). O factory pré-inicializa e bloqueia registries compartilhados (FontRegistry, ImageRegistry) no tempo de boot. Cada requisição HTTP cria um Document leve e descartável a partir do factory -- eliminando overhead de inicialização por requisição.
Esta seção compara DocumentFactory (registries compartilhados e bloqueados) contra createStandalone() (registries novos por chamada).
Fontes Embutidas (Helvetica)
| Modo | Mediana (ms) | Pico de Memória (MB) | Tamanho do Arquivo (KB) |
|---|---|---|---|
| DocumentFactory | 0,60 | 4,0 | 3,3 |
| createStandalone() | 0,70 | 4,0 | 3,3 |
Resultado: ~equivalente (proporção 0,86x). Com fontes embutidas (Helvetica), ambos os modos têm performance idêntica porque não há parsing de arquivo de fonte para cachear. A vantagem real do DocumentFactory aparece com fontes TrueType.
Fontes TrueType (DejaVu Sans)
Este é o teste-chave para a proposta de valor do DocumentFactory. Diferente do teste Helvetica acima (fonte embutida, zero parsing), este teste usa DejaVu Sans (~700 KB fonte TrueType). O DocumentFactory pré-registra e cacheia dados de fonte parseados no boot -- requisições subsequentes pulam todo I/O de arquivo de fonte. O createStandalone() precisa fazer parse do arquivo .ttf em toda requisição.
| Modo | Mediana (ms) | Pico de Memória (MB) | Tamanho do Arquivo (KB) |
|---|---|---|---|
| Factory (TTF cacheado) | 2,60 | 6,0 | 24,5 |
| Standalone (TTF parse) | 4,09 | 6,0 | 24,3 |
Speedup do Factory: 1,6x -- O parsing de fonte cacheado elimina ~1,5 ms por requisição. Em um worker RoadRunner/Swoole processando 1.000 requisições/minuto, isso economiza ~25 segundos de tempo de CPU por minuto.
Pico de Uso de Memória
Todos os valores em MB (mediana). O benchmark de cada biblioteca roda em seu próprio subprocesso PHP -- apenas o autoloader necessário é carregado, então memory_get_peak_usage() reflete o custo real de memória apenas daquela biblioteca.
Cenários Padrão
| Cenário | TCPDF-Next | TCPDF | DomPDF | mPDF |
|---|---|---|---|---|
| Documento Simples | 4,0 | 12,0 | 6,0 | 14,0 |
| Fatura | 4,0 | 12,0 | 12,0 | 14,0 |
| Relatório de 100 Páginas | 4,0 | 12,0 | 66,0 | 27,9* |
| Documento TrueType | 6,0 | 14,0 | 20,0 | 16,0 |
O TCPDF-Next mantém um footprint consistente de 4 MB de documentos de 1 página a 100 páginas, demonstrando gerenciamento eficiente de memória através de objetos de página compactados e referências de recursos compartilhados.
Memória HTML para PDF
| Biblioteca | Pico de Memória (MB) |
|---|---|
| TCPDF-Next | 4,0 |
| Artisan (Chrome) | 4,0 |
| DomPDF | 10,0 |
| TCPDF | 12,0 |
| mPDF | 18,0 |
O Artisan (Chrome) mede apenas a memória do lado PHP -- o processo Chrome headless tem seu próprio espaço de memória gerenciado pelo SO.
Tamanho do Arquivo de Saída
Todos os valores em KB (mediana).
Cenários Padrão
| Cenário | TCPDF-Next | TCPDF | DomPDF | mPDF |
|---|---|---|---|---|
| Documento Simples | 3,3 | 7,1 | 1,7 | 28,0 |
| Fatura | 5,0 | 9,2 | 4,0 | 30,2 |
| Relatório de 100 Páginas | 96,4 | 100,8 | 128,7 | 181,1* |
| Documento TrueType | 24,7 | 101,3 | 16,1 | 42,4 |
O DomPDF produz os menores arquivos para documentos simples (1,7 KB) através de otimização agressiva de content-stream. O TCPDF-Next produz saída compacta através de cross-reference streams e object streams do PDF 2.0. O TCPDF incorpora um subset de fonte TrueType significativamente maior (101,3 KB vs 24,7 KB).
Tamanho de Arquivo HTML para PDF
| Biblioteca | Tamanho do Arquivo (KB) |
|---|---|
| DomPDF | 5,3 |
| TCPDF-Next | 6,6 |
| TCPDF | 12,6 |
| Artisan (Chrome) | 36,9 |
| mPDF | 46,0 |
A saída do Artisan (Chrome) é maior (36,9 KB) porque o printToPDF do Chrome gera um PDF standalone completo com recursos incorporados.
Throughput
Testes de throughput rodam continuamente por 30 segundos usando o cenário de documento simples. Os valores refletem a capacidade de geração sustentada sob carga.
Padrão (docs/seg)
| Modo | TCPDF-Next | TCPDF | DomPDF | mPDF |
|---|---|---|---|---|
| Thread Única | 2.605 | 1.169 | 233 | 130 |
| 4 Workers | 9.221 | 4.163 | 841 | 487 |
Documentos por Minuto
| Modo | TCPDF-Next | TCPDF | DomPDF | mPDF |
|---|---|---|---|---|
| Thread Única | 156.284 | 70.134 | 13.956 | 7.800 |
| 4 Workers | 553.280 | 249.752 | 50.484 | 29.194 |
Com 4 workers, o TCPDF-Next sustenta mais de 9.200 documentos por segundo -- mais de 553.000 documentos por minuto. Isso é 2,2x o throughput do TCPDF, 11,0x do DomPDF e 19,0x do mPDF.
Throughput por Ciclo de Vida do Worker
Compara throughput de DocumentFactory vs createStandalone() usando fonte Helvetica embutida.
| Modo | DocumentFactory | createStandalone() |
|---|---|---|
| Thread Única | 2.490 | 2.515 |
| 4 Workers | 9.074 | 9.191 |
Com fontes embutidas, DocumentFactory e createStandalone() produzem throughput equivalente -- sem parsing de fonte para cachear.
Throughput de Documento TrueType
Throughput com fontes TrueType (DejaVu Sans) -- expõe o overhead real de parsing de fonte por biblioteca.
| Biblioteca | Thread Única (docs/seg) |
|---|---|
| TCPDF-Next | 242 |
| TCPDF | 81 |
| mPDF | 50 |
| DomPDF | 30 |
O throughput TrueType do TCPDF-Next é 3,0x o do TCPDF, 4,8x o do mPDF e 8,0x o do DomPDF -- refletindo seu subsetting e caching eficientes de fontes.
Throughput TTF por Ciclo de Vida do Worker
DocumentFactory (dados de fonte TrueType cacheados) vs createStandalone() (parse do TTF a cada requisição).
| Modo | Factory (TTF cacheado) | Standalone (TTF parse) |
|---|---|---|
| Thread Única | 364 | 243 |
| 4 Workers | 1.327 | 871 |
Vantagem de throughput do Factory: 1,5x (thread única). Dados de fonte TrueType cacheados eliminam o overhead de parsing do .ttf por requisição. Com 4 workers, o factory alcança 1.327 docs/seg -- uma melhoria de 52,4% em relação ao standalone.
Throughput HTTP RoadRunner
Benchmark real de servidor HTTP usando RoadRunner com padrão de worker DocumentFactory. Medido via ab (Apache Bench).
| Config | Docs/seg | Latência Média (ms) | p50 (ms) | p99 (ms) | Falhas |
|---|---|---|---|---|---|
| 1 worker / 1 concorrente | 1.320 | 0,76 | 1 | 1 | 0 |
| 4 workers / 4 concorrentes | 4.812 | 0,83 | 1 | 1 | 0 |
Overhead HTTP vs pcntl_fork bruto:
- Thread única: 49,3% de overhead (1.320 vs 2.605 docs/seg)
- Multi-worker: 47,8% de overhead (4.812 vs 9.221 docs/seg)
O overhead de ~48% reflete o custo da stack HTTP completa (TCP accept, HTTP parse, response write). Mesmo com esse overhead, o TCPDF-Next atrás do RoadRunner entrega 4.812 respostas HTTP de PDF reais por segundo com latência sub-milissegundo e zero requisições falhadas.
Metodologia
- Ambiente: Todas as bibliotecas rodam dentro do mesmo container Docker (PHP 8.5.3, Debian bookworm) com configuração idêntica.
- Restrições de recursos: Container limitado a 4 CPUs e 16 GB de RAM via restrições de recursos Docker.
- Runtime: OPcache e JIT habilitados para todas as bibliotecas. Avisos de deprecação suprimidos globalmente para cronometragem justa.
- Isolamento por subprocesso: Cada par biblioteca/cenário roda em um processo PHP separado (
exec()) para medição precisa de memória -- classes de autoloader de outras bibliotecas não poluemmemory_get_peak_usage(). - Paridade de API: TCPDF-Next e TCPDF usam API nativa
Cell/MultiCellpara cenários não-HTML. DomPDF e mPDF usam markup HTML equivalente (sua interface nativa). - Teste de fonte TrueType usa DejaVu Sans (~700 KB
.ttf) para expor custos reais de parsing de fonte; testes com Helvetica (Base14) mostram baseline de overhead zero. - Artisan (Chrome) usa Chromium headless via CDP para renderização CSS3 pixel-perfect (JavaScript desabilitado via CSP).
- Artisan por fases decompõe a renderização do Chrome: Fase 1 (Chrome CDP
printToPDF) vs Fase 2 (PdfReader + PageImporter + embed). - Ciclo de vida do worker compara
DocumentFactory(FontRegistry compartilhado + lock, ImageRegistry) vscreateStandalone()(registries novos por chamada). - Ciclo de vida do worker TTF demonstra o valor-chave do
DocumentFactory: dados de fonte TrueType cacheados entre milhares de requisições do worker. - RoadRunner HTTP usa roadrunner-server/roadrunner com padrão de worker
DocumentFactory, medido viaab(Apache Bench). - Warmup: 3 iterações são executadas e descartadas antes do início da medição, garantindo que OPcache e JIT estejam totalmente aquecidos.
- Iterações: 20 iterações medidas por cenário. A mediana é reportada para eliminar ruído de outliers.
- Throughput: Testes rodam continuamente por 30 segundos.
- Cronometragem:
hrtime(true)fornece medição de relógio de parede com precisão de nanossegundos. - Memória:
memory_get_peak_usage(true)reporta o pico de memória real (RSS). - Tamanho de arquivo: A saída é gravada em disco e medida com
filesize().
Notas sobre Interpretação dos Dados
- Isolamento por subprocesso para memória: O benchmark de latência de cada biblioteca roda em seu próprio subprocesso PHP. Apenas o autoloader necessário é carregado, então
memory_get_peak_usage()reflete o custo real de memória apenas daquela biblioteca -- não poluição cumulativa de autoloader de outras bibliotecas. - Artisan (Chrome) usa BrowserPool keep-alive: O processo Chrome permanece ativo entre iterações, correspondendo ao comportamento em produção (RoadRunner/Swoole/Octane). O overhead de cold-start (~250 ms para lançamento inicial do Chromium) é excluído -- é um custo único amortizado em milhares de requisições.
- Gap entre Latência e Throughput: Medições de latência de execução única incluem overhead de
gc_collect_cycles(),memory_reset_peak_usage()ehrtime()(~0,3 ms). Testes de throughput rodam em loop apertado sem overhead de medição, então seu tempo por documento (1000/docs_por_seg) é menor que a mediana de execução única. Números de throughput refletem mais precisamente a performance em produção. - Helvetica vs TrueType: Helvetica é uma fonte PDF embutida (Base14) que não requer I/O de arquivo. O cenário TrueType usa DejaVu Sans, que requer parsing de arquivos
.ttf(~700 KB). A vantagem do FontRegistry cacheado doDocumentFactorysó se manifesta com fontes TrueType. - Compatibilidade JIT (*): Valores marcados com * foram medidos com JIT desabilitado (
opcache.jit=0) devido a um segfault do PHP JIT (SIGSEGV, código de saída 139) no caminho de código daquela biblioteca. O cache de bytecode do OPcache permanece ativo. Esta é uma classe conhecida de bugs do PHP JIT que afeta certos padrões de loop complexos.
Reproduzindo os Benchmarks
A suite de benchmark está incluída no repositório. Para reproduzir estes resultados:
cd benchmark
docker compose up --buildOs resultados são impressos no stdout ao final da execução. A configuração Docker garante um ambiente idêntico independente do SO do host.
Leitura Adicional
- Migração do TCPDF -- Guia de migração passo a passo
- Otimização de Performance -- Modo streaming, otimização de memória e estratégias de cache