Performance Benchmarks
Real-world performance benchmarks comparing TCPDF-Next against three established PHP PDF libraries: TCPDF, DomPDF, and mPDF. All tests were executed on identical hardware under controlled Docker conditions. Results are medians of 20 iterations to eliminate outlier noise.
Test Environment
| Parameter | Value |
|---|---|
| CPU | Intel Core i9-13900K (x86-64) |
| RAM | 64 GB DDR5 (Docker limited to 16 GB) |
| Docker | 4 CPUs, 16 GB RAM, Debian bookworm |
| PHP | 8.5.3 (CLI, OPcache enabled, JIT enabled) |
| 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 (HTTP throughput tests) |
| Warmup | 3 iterations (discarded) |
| Measured | 20 iterations (median reported) |
| Timing | hrtime(true) nanosecond-precision wall clock |
Interactive Comparison
PHP 8.5.3 + OPcache + JIT · Docker 4 CPUs / 16 GB · i9-13900K · Median of 20 runs
Generation Speed
Each scenario was run 20 times after 3 warmup iterations. The median generation time is reported.
Simple Document (1 Page)
A single A4 page with a heading and basic formatted text using built-in Helvetica font. No images, no tables.
| Library | Time (ms) |
|---|---|
| TCPDF-Next | 0.68 |
| TCPDF | 2.55 |
| DomPDF | 4.16 |
| mPDF | 6.71 |
TCPDF-Next completes the simplest scenario in under 1 ms — 3.8x faster than TCPDF, 6.1x faster than DomPDF, and 9.9x faster than mPDF.
Invoice (2 Pages)
A two-page invoice with 25-row tabular line items, subtotals, headers, footers, and a logo image.
| Library | Time (ms) |
|---|---|
| TCPDF | 1.96 |
| TCPDF-Next | 2.01 |
| mPDF | 15.86 |
| DomPDF | 17.33 |
TCPDF-Next and TCPDF are virtually tied in the Invoice scenario (~1.0x). Both significantly outperform mPDF (7.9x slower) and DomPDF (8.6x slower).
100-Page Report
A 100-page document with dense mixed content: headings, paragraphs, and structured data.
| Library | Time (ms) |
|---|---|
| TCPDF-Next | 34.29 |
| TCPDF | 105.39 |
| mPDF | 1,106.59* |
| DomPDF | 2,129.12 |
TCPDF-Next completes a 100-page report in 34.29 ms — 3.1x faster than TCPDF, 32.3x faster than mPDF, and 62.1x faster than DomPDF.
JIT Compatibility Note
*mPDF's 100-Page Report result was measured with JIT disabled (opcache.jit=0) due to a PHP JIT segfault (SIGSEGV, exit code 139) in mPDF's code path. OPcache bytecode caching remained active. This is a known class of PHP JIT bugs affecting certain complex loop patterns. All other mPDF scenarios ran with JIT enabled.
TrueType Document (1 Page)
A single A4 page using DejaVu Sans (~700 KB TrueType font). This scenario exposes real font-file parsing costs — unlike Helvetica (built-in Base14 font requiring zero file I/O).
| Library | Time (ms) |
|---|---|
| TCPDF-Next | 4.08 |
| TCPDF | 12.11 |
| mPDF | 16.51 |
| DomPDF | 24.14 |
TCPDF-Next parses and embeds the TrueType font 3.0x faster than TCPDF, 4.0x faster than mPDF, and 5.9x faster than DomPDF.
Relative Speed (All Scenarios)
All values relative to TCPDF-Next (1.0x baseline). Lower = faster.
| Scenario | TCPDF-Next | TCPDF | DomPDF | mPDF |
|---|---|---|---|---|
| Simple Document | 1.0x | 3.8x | 6.1x | 9.9x |
| Invoice | 1.0x | ~1.0x | 8.6x | 7.9x |
| 100-Page Report | 1.0x | 3.1x | 62.1x | 32.3x |
| TrueType Document | 1.0x | 3.0x | 5.9x | 4.0x |
HTML to PDF
HTML Processing Approaches
Different libraries take fundamentally different approaches to converting HTML to PDF. Understanding these differences is essential for interpreting the benchmark results:
Direct Translation (TCPDF-Next, TCPDF) — The built-in HTML parser tokenizes HTML tags and maps them directly to PDF drawing commands (Cell, MultiCell, Image) in a single streaming pass. This approach is extremely fast but supports only basic HTML tags and inline CSS — no Flexbox, no Grid, no complex CSS selectors.
CSS Layout Engine (DomPDF, mPDF) — These libraries are designed with HTML as their primary interface. DomPDF builds a full DOM tree, applies the CSS cascade (specificity, inheritance), and calculates box-model layout before rendering to PDF. mPDF's WriteHTML() similarly processes HTML through its own CSS layout engine. Both support more CSS features than direct-translation parsers (floats, positioned elements, styled tables) but still fall short of full browser-level CSS3.
Full Browser Rendering (Artisan / Chrome) — TCPDF-NextArtisan delegates rendering to headless Chromium via the Chrome DevTools Protocol (CDP). This provides pixel-perfect CSS3 support: Flexbox, Grid, Web Fonts, media queries, CSS variables — output identical to what a Chrome browser would produce.
The benchmark compares each library's native approach: TCPDF-Next and TCPDF use their built-in direct-translation parser; DomPDF and mPDF use their CSS rendering engines (their primary API); Artisan uses Chrome.
Results
| Library | Approach | Time (ms) |
|---|---|---|
| TCPDF-Next | Direct translation | 1.51 |
| TCPDF | Direct translation | 6.60 |
| DomPDF | CSS layout engine | 13.69 |
| mPDF | CSS layout engine | 29.63 |
| Artisan (Chrome) | Full browser rendering | 66.70 |
Relative Time (HTML to PDF)
| Library | Relative |
|---|---|
| TCPDF-Next | 1.0x |
| TCPDF | 4.4x |
| DomPDF | 9.0x |
| mPDF | 19.6x |
| Artisan (Chrome) | 44.1x |
TCPDF-Next's direct-translation parser delivers sub-2 ms performance — 4.4x faster than TCPDF's regex-based parser, 9.0x faster than DomPDF's CSS layout engine, and 19.6x faster than mPDF. Artisan (Chrome) is 44.1x slower but provides full CSS3 fidelity that no other library can match.
Artisan Chrome — Phased Breakdown
Decomposes the Artisan (Chrome) pipeline into two phases:
- Chrome CDP Render — headless Chrome converts HTML to PDF bytes via
printToPDF - PDF Import + Embed — TCPDF-Next parses Chrome's PDF, extracts the page as a Form XObject, and embeds it into the target document
| Phase | Median (ms) | Mean (ms) | Min (ms) | Max (ms) | Stddev |
|---|---|---|---|---|---|
| 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 |
Time distribution: Chrome CDP = 97.4% | PDF Import = 2.3%
Chrome's printToPDF dominates the pipeline at 97.4% of total time. The PDF Import phase (PdfReader + PageImporter + XObject embedding) adds only ~2 ms — negligible overhead.
Standard vs Phased Measurement
The standard Artisan test (66.70 ms) uses the integrated writeHtmlChrome() method with BrowserPool keep-alive. The phased test (83.35 ms total) separately instruments each phase, adding measurement overhead. Both use the same keep-alive Chrome instance — the ~250 ms cold-start cost for initial Chromium launch is excluded as it is a one-time cost amortized over thousands of requests.
When to Use Which Approach
For simple HTML (tables, basic formatting), use TCPDF-Next's built-in HTML parser (1.51 ms). For complex CSS3 layouts requiring pixel-perfect fidelity (Flexbox, Grid, Web Fonts), use Artisan — the ~67 ms overhead buys the full power of Chrome's rendering engine.
Worker Lifecycle (DocumentFactory vs Standalone)
TCPDF-Next provides a DocumentFactory pattern designed for long-running PHP workers (RoadRunner, Swoole, Laravel Octane). The factory pre-initializes and locks shared registries (FontRegistry, ImageRegistry) at boot time. Each HTTP request creates a lightweight, disposable Document from the factory — eliminating per-request initialization overhead.
This section compares DocumentFactory (shared, locked registries) against createStandalone() (fresh registries per call).
Built-in Fonts (Helvetica)
| Mode | Median (ms) | Peak Memory (MB) | File Size (KB) |
|---|---|---|---|
| DocumentFactory | 0.60 | 4.0 | 3.3 |
| createStandalone() | 0.70 | 4.0 | 3.3 |
Result: ~equivalent (ratio 0.86x). With built-in fonts (Helvetica), both modes perform identically because there is no font file parsing to cache. The real advantage of DocumentFactory appears with TrueType fonts.
TrueType Fonts (DejaVu Sans)
This is the key test for DocumentFactory's value proposition. Unlike the Helvetica test above (built-in font, zero parsing), this test uses DejaVu Sans (~700 KB TrueType font). DocumentFactory pre-registers and caches parsed font data at boot — subsequent requests skip all font file I/O. createStandalone() must parse the .ttf file on every single request.
| Mode | Median (ms) | Peak Memory (MB) | File Size (KB) |
|---|---|---|---|
| Factory (TTF cached) | 2.60 | 6.0 | 24.5 |
| Standalone (TTF parse) | 4.09 | 6.0 | 24.3 |
Factory speedup: 1.6x — Cached font parsing eliminates ~1.5 ms per request. In a RoadRunner/Swoole worker handling 1,000 requests/minute, this saves ~25 seconds of CPU time per minute.
Peak Memory Usage
All values in MB (median). Each library's benchmark runs in its own PHP subprocess — only the required autoloader is loaded, so memory_get_peak_usage() reflects the actual memory cost of that library alone.
Standard Scenarios
| Scenario | TCPDF-Next | TCPDF | DomPDF | mPDF |
|---|---|---|---|---|
| Simple Document | 4.0 | 12.0 | 6.0 | 14.0 |
| Invoice | 4.0 | 12.0 | 12.0 | 14.0 |
| 100-Page Report | 4.0 | 12.0 | 66.0 | 27.9* |
| TrueType Document | 6.0 | 14.0 | 20.0 | 16.0 |
TCPDF-Next maintains a consistent 4 MB footprint from 1-page to 100-page documents, demonstrating efficient memory management through compacted page objects and shared resource references.
HTML to PDF Memory
| Library | Peak Memory (MB) |
|---|---|
| TCPDF-Next | 4.0 |
| Artisan (Chrome) | 4.0 |
| DomPDF | 10.0 |
| TCPDF | 12.0 |
| mPDF | 18.0 |
Artisan (Chrome) measures only the PHP-side memory — the headless Chrome process has its own memory space managed by the OS.
Output File Size
All values in KB (median).
Standard Scenarios
| Scenario | TCPDF-Next | TCPDF | DomPDF | mPDF |
|---|---|---|---|---|
| Simple Document | 3.3 | 7.1 | 1.7 | 28.0 |
| Invoice | 5.0 | 9.2 | 4.0 | 30.2 |
| 100-Page Report | 96.4 | 100.8 | 128.7 | 181.1* |
| TrueType Document | 24.7 | 101.3 | 16.1 | 42.4 |
DomPDF produces the smallest files for simple documents (1.7 KB) through aggressive content-stream optimization. TCPDF-Next produces compact output through PDF 2.0 cross-reference streams and object streams. TCPDF embeds a significantly larger TrueType font subset (101.3 KB vs 24.7 KB).
HTML to PDF File Size
| Library | File Size (KB) |
|---|---|
| DomPDF | 5.3 |
| TCPDF-Next | 6.6 |
| TCPDF | 12.6 |
| Artisan (Chrome) | 36.9 |
| mPDF | 46.0 |
Artisan (Chrome) output is larger (36.9 KB) because Chrome's printToPDF generates a complete standalone PDF with embedded resources.
Throughput
Throughput tests run continuously for 30 seconds using the simple-document scenario. Values reflect sustained generation capacity under load.
Standard (docs/sec)
| Mode | TCPDF-Next | TCPDF | DomPDF | mPDF |
|---|---|---|---|---|
| Single Thread | 2,605 | 1,169 | 233 | 130 |
| 4 Workers | 9,221 | 4,163 | 841 | 487 |
Documents per Minute
| Mode | TCPDF-Next | TCPDF | DomPDF | mPDF |
|---|---|---|---|---|
| Single Thread | 156,284 | 70,134 | 13,956 | 7,800 |
| 4 Workers | 553,280 | 249,752 | 50,484 | 29,194 |
With 4 workers, TCPDF-Next sustains over 9,200 documents per second — more than 553,000 documents per minute. This is 2.2x the throughput of TCPDF, 11.0x DomPDF, and 19.0x mPDF.
Worker Lifecycle Throughput
Compares DocumentFactory vs createStandalone() throughput using built-in Helvetica font.
| Mode | DocumentFactory | createStandalone() |
|---|---|---|
| Single Thread | 2,490 | 2,515 |
| 4 Workers | 9,074 | 9,191 |
With built-in fonts, DocumentFactory and createStandalone() produce equivalent throughput — no font parsing to cache.
TrueType Document Throughput
Throughput with TrueType fonts (DejaVu Sans) — exposes real font-parsing overhead per library.
| Library | Single Thread (docs/sec) |
|---|---|
| TCPDF-Next | 242 |
| TCPDF | 81 |
| mPDF | 50 |
| DomPDF | 30 |
TCPDF-Next's TrueType throughput is 3.0x TCPDF, 4.8x mPDF, and 8.0x DomPDF — reflecting its efficient font subsetting and caching.
Worker Lifecycle TTF Throughput
DocumentFactory (cached TrueType font data) vs createStandalone() (parse TTF every request).
| Mode | Factory (TTF cached) | Standalone (TTF parse) |
|---|---|---|
| Single Thread | 364 | 243 |
| 4 Workers | 1,327 | 871 |
Factory throughput advantage: 1.5x (single thread). Cached TrueType font data eliminates per-request .ttf parsing overhead. With 4 workers, the factory achieves 1,327 docs/sec — a 52.4% improvement over standalone.
RoadRunner HTTP Throughput
Real HTTP server benchmark using RoadRunner with DocumentFactory worker pattern. Measured via ab (Apache Bench).
| Config | Docs/sec | Mean Latency (ms) | p50 (ms) | p99 (ms) | Failed |
|---|---|---|---|---|---|
| 1 worker / 1 concurrent | 1,320 | 0.76 | 1 | 1 | 0 |
| 4 workers / 4 concurrent | 4,812 | 0.83 | 1 | 1 | 0 |
HTTP overhead vs raw pcntl_fork:
- Single thread: 49.3% overhead (1,320 vs 2,605 docs/sec)
- Multi-worker: 47.8% overhead (4,812 vs 9,221 docs/sec)
The ~48% overhead reflects the cost of the full HTTP stack (TCP accept, HTTP parse, response write). Even with this overhead, TCPDF-Next behind RoadRunner delivers 4,812 real HTTP PDF responses per second with sub-millisecond latency and zero failed requests.
Methodology
- Environment: All libraries run inside the same Docker container (PHP 8.5.3, Debian bookworm) with identical configuration.
- Resource constraints: Container limited to 4 CPUs and 16 GB RAM via Docker resource constraints.
- Runtime: OPcache and JIT enabled for all libraries. Deprecation warnings suppressed globally for fair timing.
- Subprocess isolation: Each library/scenario pair runs in a separate PHP process (
exec()) for accurate memory measurement — autoloader classes from other libraries do not pollutememory_get_peak_usage(). - API parity: TCPDF-Next and TCPDF use native
Cell/MultiCellAPI for non-HTML scenarios. DomPDF and mPDF use equivalent HTML markup (their native interface). - TrueType font test uses DejaVu Sans (~700 KB
.ttf) to expose real font parsing costs; Helvetica (Base14) tests show zero-overhead baseline. - Artisan (Chrome) uses headless Chromium via CDP for pixel-perfect CSS3 rendering (JavaScript disabled via CSP).
- Artisan phased decomposes Chrome rendering: Phase 1 (Chrome CDP
printToPDF) vs Phase 2 (PdfReader + PageImporter + embed). - Worker lifecycle compares
DocumentFactory(shared FontRegistry + lock, ImageRegistry) vscreateStandalone()(fresh registries per call). - Worker lifecycle TTF demonstrates
DocumentFactory's key value: cached TrueType font data across thousands of worker requests. - RoadRunner HTTP uses roadrunner-server/roadrunner with
DocumentFactoryworker pattern, measured viaab(Apache Bench). - Warmup: 3 iterations are executed and discarded before measurement begins, ensuring OPcache and JIT are fully warmed.
- Iterations: 20 measured iterations per scenario. The median is reported to eliminate outlier noise.
- Throughput: Tests run continuously for 30 seconds.
- Timing:
hrtime(true)provides nanosecond-precision wall-clock measurement. - Memory:
memory_get_peak_usage(true)reports real (RSS) peak memory. - File size: Output is flushed to disk and measured with
filesize().
Notes on Data Interpretation
- Subprocess isolation for memory: Each library's latency benchmark runs in its own PHP subprocess. Only the required autoloader is loaded, so
memory_get_peak_usage()reflects the actual memory cost of that library alone — not cumulative autoloader pollution from other libraries. - Artisan (Chrome) uses BrowserPool keep-alive: Chrome process stays alive across iterations, matching production behavior (RoadRunner/Swoole/Octane). Cold-start overhead (~250 ms for initial Chromium launch) is excluded — it's a one-time cost amortized over thousands of requests.
- Latency vs Throughput gap: Single-run latency measurements include
gc_collect_cycles(),memory_reset_peak_usage(), andhrtime()overhead (~0.3 ms). Throughput tests run in a tight loop without measurement overhead, so their per-document time (1000/docs_per_sec) is lower than the single-run median. Throughput numbers more accurately reflect production performance. - Helvetica vs TrueType: Helvetica is a built-in PDF font (Base14) requiring zero file I/O. The TrueType scenario uses DejaVu Sans, which requires parsing
.ttffiles (~700 KB).DocumentFactory's cached FontRegistry advantage only manifests with TrueType fonts. - JIT compatibility (*): Values marked with * were measured with JIT disabled (
opcache.jit=0) due to a PHP JIT segfault (SIGSEGV, exit code 139) in that library's code path. OPcache bytecode caching remains active. This is a known class of PHP JIT bugs affecting certain complex loop patterns.
Reproducing the Benchmarks
The benchmark suite is included in the repository. To reproduce these results:
cd benchmark
docker compose up --buildResults are printed to stdout at the end of the run. The Docker setup ensures an identical environment regardless of host OS.
Further Reading
- Migration from TCPDF — Step-by-step migration guide
- Performance Tuning — Streaming mode, memory optimization, and caching strategies