Skip to content

效能評測

以下為 TCPDF-Next 與三套主流 PHP PDF 函式庫:TCPDFDomPDFmPDF 的實際效能評測比較。所有測試皆在相同硬體、受控的 Docker 環境下執行。結果取 20 次迭代的中位數,以消除離群值干擾。

測試環境

參數數值
CPUIntel Core i9-13900K (x86-64)
RAM64 GB DDR5(Docker 限制為 16 GB)
Docker4 CPUs、16 GB RAM、Debian bookworm
PHP8.5.3(CLI、OPcache 啟用、JIT 啟用)
TCPDF-Next1.7.0
TCPDF6.10.1
DomPDFv3.1.4
mPDFv8.2.7
Artisan (Chrome)Headless Chromium via CDP
RoadRunnerspiral/roadrunner-http ^3.6(HTTP 吞吐量測試)
暖機3 次迭代(丟棄)
測量20 次迭代(取中位數)
計時hrtime(true) 奈秒精度掛鐘時間

互動式比較

▼ Lower is better
TCPDF-Next
0.68 ms
TCPDF
2.55 ms3.7x
DomPDF
4.16 ms6.1x
mPDF
6.71 ms9.9x

PHP 8.5.3 + OPcache + JIT · Docker 4 CPUs / 16 GB · i9-13900K · Median of 20 runs

產生速度

每個情境在 3 次暖機迭代後執行 20 次,以下報告中位數產生時間。

簡單文件(1 頁)

一頁 A4 文件,包含標題與基本格式化文字,使用內建 Helvetica 字型。無圖片、無表格。

函式庫時間(ms)
TCPDF-Next0.68
TCPDF2.55
DomPDF4.16
mPDF6.71

TCPDF-Next 在最簡單的情境下於 1 ms 內完成 — 比 TCPDF 快 3.8x、比 DomPDF 快 6.1x、比 mPDF 快 9.9x。

發票(2 頁)

兩頁發票,包含 25 列表格明細項目、小計、頁首、頁尾及一張標誌圖片。

函式庫時間(ms)
TCPDF1.96
TCPDF-Next2.01
mPDF15.86
DomPDF17.33

TCPDF-Next 與 TCPDF 在發票情境中幾乎不分上下(~1.0x)。兩者皆大幅領先 mPDF(慢 7.9x)和 DomPDF(慢 8.6x)。

100 頁報告

100 頁文件,內容為密集的混合內容:標題、段落及結構化資料。

函式庫時間(ms)
TCPDF-Next34.29
TCPDF105.39
mPDF1,106.59*
DomPDF2,129.12

TCPDF-Next 在 34.29 ms 內完成 100 頁報告 — 比 TCPDF 快 3.1x、比 mPDF 快 32.3x、比 DomPDF 快 62.1x。

JIT 相容性說明

*mPDF 的 100 頁報告結果是在停用 JIT(opcache.jit=0)的情況下測量的,因為 mPDF 的程式碼路徑會觸發 PHP JIT 區段錯誤(SIGSEGV,退出碼 139)。OPcache 位元組碼快取仍保持啟用。這是已知的一類 PHP JIT 錯誤,會影響某些複雜迴圈模式。mPDF 其餘所有情境皆在啟用 JIT 的情況下執行。

TrueType 文件(1 頁)

一頁 A4 文件,使用 DejaVu Sans(~700 KB TrueType 字型)。此情境揭露真實的字型檔案解析成本 — 不同於 Helvetica(內建 Base14 字型,完全無需檔案 I/O)。

函式庫時間(ms)
TCPDF-Next4.08
TCPDF12.11
mPDF16.51
DomPDF24.14

TCPDF-Next 解析與嵌入 TrueType 字型的速度比 TCPDF 快 3.0x、比 mPDF 快 4.0x、比 DomPDF 快 5.9x。

相對速度(所有情境)

所有數值相對於 TCPDF-Next(1.0x 基準)。數值越低越快。

情境TCPDF-NextTCPDFDomPDFmPDF
簡單文件1.0x3.8x6.1x9.9x
發票1.0x~1.0x8.6x7.9x
100 頁報告1.0x3.1x62.1x32.3x
TrueType 文件1.0x3.0x5.9x4.0x

HTML 轉 PDF

HTML 處理方式

不同函式庫在將 HTML 轉換為 PDF 時採用截然不同的方式。理解這些差異對於解讀評測結果至關重要:

直譯(TCPDF-Next、TCPDF) — 內建 HTML 解析器會將 HTML 標籤進行標記化,並在單次串流掃描中直接對應至 PDF 繪圖指令(CellMultiCellImage)。此方式速度極快,但僅支援基本 HTML 標籤與行內 CSS — 不支援 Flexbox、不支援 Grid、不支援複雜 CSS 選擇器。

CSS 排版引擎(DomPDF、mPDF) — 這些函式庫以 HTML 作為其主要介面而設計。DomPDF 會建構完整的 DOM 樹、套用 CSS 層疊(特異性、繼承),並在渲染為 PDF 之前計算盒模型排版。mPDF 的 WriteHTML() 也透過其自有的 CSS 排版引擎處理 HTML。兩者支援的 CSS 功能比直譯解析器更多(浮動元素、定位元素、樣式化表格),但仍無法達到完整的瀏覽器級 CSS3 支援。

完整瀏覽器渲染(Artisan / Chrome) — TCPDF-NextArtisan 透過 Chrome DevTools Protocol (CDP) 將渲染委派給無頭 Chromium。這提供了像素完美的 CSS3 支援:Flexbox、Grid、Web Fonts、媒體查詢、CSS 變數 — 輸出與 Chrome 瀏覽器所產生的完全一致。

本評測比較各函式庫的原生方式:TCPDF-Next 與 TCPDF 使用其內建直譯解析器;DomPDF 與 mPDF 使用其 CSS 渲染引擎(其主要 API);Artisan 使用 Chrome。

結果

函式庫方式時間(ms)
TCPDF-Next直譯1.51
TCPDF直譯6.60
DomPDFCSS 排版引擎13.69
mPDFCSS 排版引擎29.63
Artisan (Chrome)完整瀏覽器渲染66.70

相對時間(HTML 轉 PDF)

函式庫相對速度
TCPDF-Next1.0x
TCPDF4.4x
DomPDF9.0x
mPDF19.6x
Artisan (Chrome)44.1x

TCPDF-Next 的直譯解析器交出低於 2 ms 的效能 — 比 TCPDF 的正規表達式解析器快 4.4x、比 DomPDF 的 CSS 排版引擎快 9.0x、比 mPDF 快 19.6x。Artisan (Chrome) 慢 44.1x,但提供其他函式庫無法匹敵的完整 CSS3 保真度。

Artisan Chrome — 階段拆解

將 Artisan (Chrome) 管線拆解為兩個階段:

  1. Chrome CDP 渲染 — 無頭 Chrome 透過 printToPDF 將 HTML 轉換為 PDF 位元組
  2. PDF 匯入 + 嵌入 — TCPDF-Next 解析 Chrome 的 PDF、擷取頁面作為 Form XObject,並嵌入目標文件
階段中位數(ms)平均值(ms)最小值(ms)最大值(ms)標準差
Chrome CDP 渲染81.1781.1765.5195.804.84
PDF 匯入 + 嵌入1.962.081.602.870.40
合計83.3583.2968.2097.564.70

時間分布: Chrome CDP = 97.4% | PDF 匯入 = 2.3%

Chrome 的 printToPDF 佔據管線 97.4% 的總時間。PDF 匯入階段(PdfReader + PageImporter + XObject 嵌入)僅增加約 2 ms — 開銷可忽略不計。

標準測試與階段測試的差異

標準 Artisan 測試(66.70 ms)使用整合式 writeHtmlChrome() 方法搭配 BrowserPool keep-alive。階段測試(合計 83.35 ms)分別量測各階段,增加了測量開銷。兩者使用相同的 keep-alive Chrome 實例 — 初始 Chromium 啟動的 ~250 ms 冷啟動成本已排除,因為這是一次性成本,可在數千次請求中攤銷。

何時使用哪種方式

對於簡單 HTML(表格、基本格式化),使用 TCPDF-Next 的內建 HTML 解析器(1.51 ms)。對於需要像素完美保真度的複雜 CSS3 排版(Flexbox、Grid、Web Fonts),使用 Artisan — ~67 ms 的額外開銷換來 Chrome 渲染引擎的完整能力。

Worker 生命週期(DocumentFactory vs Standalone)

TCPDF-Next 提供 DocumentFactory 模式,專為長時間執行的 PHP Worker(RoadRunner、Swoole、Laravel Octane)而設計。Factory 在啟動時預先初始化並鎖定共用的註冊表(FontRegistryImageRegistry)。每個 HTTP 請求從 Factory 建立一個輕量、可丟棄的 Document — 消除了每次請求的初始化開銷。

本節比較 DocumentFactory(共用、鎖定的註冊表)與 createStandalone()(每次呼叫建立全新註冊表)。

內建字型(Helvetica)

模式中位數(ms)峰值記憶體(MB)檔案大小(KB)
DocumentFactory0.604.03.3
createStandalone()0.704.03.3

結果:大致相同(比率 0.86x)。使用內建字型(Helvetica)時,兩種模式效能完全相同,因為沒有需要快取的字型檔案解析作業。DocumentFactory 的真正優勢在 TrueType 字型時才會顯現。

TrueType 字型(DejaVu Sans)

這是展現 DocumentFactory 價值的關鍵測試。不同於上方的 Helvetica 測試(內建字型、零解析),此測試使用 DejaVu Sans(~700 KB TrueType 字型)。DocumentFactory 在啟動時預先註冊並快取已解析的字型資料 — 後續請求跳過所有字型檔案 I/O。createStandalone() 則必須在每次請求時解析 .ttf 檔案。

模式中位數(ms)峰值記憶體(MB)檔案大小(KB)
Factory(TTF 已快取)2.606.024.5
Standalone(TTF 解析)4.096.024.3

Factory 加速比:1.6x — 快取字型解析每次請求省去約 1.5 ms。在 RoadRunner/Swoole Worker 每分鐘處理 1,000 個請求的情況下,這相當於每分鐘節省約 25 秒的 CPU 時間。

峰值記憶體使用量

所有數值單位為 MB(中位數)。每個函式庫的評測皆在獨立的 PHP 子程序中執行 — 僅載入所需的自動載入器,因此 memory_get_peak_usage() 反映的是該函式庫單獨的實際記憶體成本。

標準情境

情境TCPDF-NextTCPDFDomPDFmPDF
簡單文件4.012.06.014.0
發票4.012.012.014.0
100 頁報告4.012.066.027.9*
TrueType 文件6.014.020.016.0

TCPDF-Next 從 1 頁到 100 頁文件始終維持 4 MB 的穩定記憶體佔用量,展現了透過壓縮頁面物件與共用資源參考實現的高效記憶體管理。

HTML 轉 PDF 記憶體

函式庫峰值記憶體(MB)
TCPDF-Next4.0
Artisan (Chrome)4.0
DomPDF10.0
TCPDF12.0
mPDF18.0

Artisan (Chrome) 僅測量 PHP 端記憶體 — 無頭 Chrome 程序擁有由作業系統管理的獨立記憶體空間。

輸出檔案大小

所有數值單位為 KB(中位數)。

標準情境

情境TCPDF-NextTCPDFDomPDFmPDF
簡單文件3.37.11.728.0
發票5.09.24.030.2
100 頁報告96.4100.8128.7181.1*
TrueType 文件24.7101.316.142.4

DomPDF 在簡單文件中產生最小的檔案(1.7 KB),歸功於其積極的內容串流最佳化。TCPDF-Next 透過 PDF 2.0 交叉引用串流與物件串流產生緊湊的輸出。TCPDF 嵌入了顯著較大的 TrueType 字型子集(101.3 KB vs 24.7 KB)。

HTML 轉 PDF 檔案大小

函式庫檔案大小(KB)
DomPDF5.3
TCPDF-Next6.6
TCPDF12.6
Artisan (Chrome)36.9
mPDF46.0

Artisan (Chrome) 的輸出較大(36.9 KB),因為 Chrome 的 printToPDF 會產生包含嵌入資源的完整獨立 PDF。

吞吐量

吞吐量測試使用簡單文件情境持續執行 30 秒。數值反映持續負載下的產生能力。

標準(文件/秒)

模式TCPDF-NextTCPDFDomPDFmPDF
單執行緒2,6051,169233130
4 Workers9,2214,163841487

每分鐘文件數

模式TCPDF-NextTCPDFDomPDFmPDF
單執行緒156,28470,13413,9567,800
4 Workers553,280249,75250,48429,194

使用 4 Workers 時,TCPDF-Next 可維持每秒超過 9,200 份文件 — 即每分鐘超過 553,000 份文件。這是 TCPDF 吞吐量的 2.2x、DomPDF 的 11.0x、mPDF 的 19.0x。

Worker 生命週期吞吐量

比較使用內建 Helvetica 字型時 DocumentFactorycreateStandalone() 的吞吐量。

模式DocumentFactorycreateStandalone()
單執行緒2,4902,515
4 Workers9,0749,191

使用內建字型時,DocumentFactorycreateStandalone() 產生相同的吞吐量 — 沒有需要快取的字型解析作業。

TrueType 文件吞吐量

使用 TrueType 字型(DejaVu Sans)的吞吐量 — 揭露各函式庫每次的實際字型解析開銷。

函式庫單執行緒(文件/秒)
TCPDF-Next242
TCPDF81
mPDF50
DomPDF30

TCPDF-Next 的 TrueType 吞吐量是 TCPDF 的 3.0x、mPDF 的 4.8x、DomPDF 的 8.0x — 反映其高效的字型子集化與快取機制。

Worker 生命週期 TTF 吞吐量

DocumentFactory(已快取 TrueType 字型資料)vs createStandalone()(每次請求解析 TTF)。

模式Factory(TTF 已快取)Standalone(TTF 解析)
單執行緒364243
4 Workers1,327871

Factory 吞吐量優勢:1.5x(單執行緒)。快取的 TrueType 字型資料消除了每次請求的 .ttf 解析開銷。使用 4 Workers 時,Factory 達到 1,327 文件/秒 — 比 Standalone 提升 52.4%。

RoadRunner HTTP 吞吐量

使用 RoadRunner 搭配 DocumentFactory Worker 模式的實際 HTTP 伺服器評測。透過 ab(Apache Bench)測量。

設定文件/秒平均延遲(ms)p50(ms)p99(ms)失敗數
1 worker / 1 concurrent1,3200.76110
4 workers / 4 concurrent4,8120.83110

HTTP 開銷 vs 原始 pcntl_fork

  • 單執行緒:49.3% 開銷(1,320 vs 2,605 文件/秒)
  • 多 Worker:47.8% 開銷(4,812 vs 9,221 文件/秒)

約 48% 的開銷反映了完整 HTTP 堆疊的成本(TCP 接受、HTTP 解析、回應寫入)。即使加上這些開銷,TCPDF-Next 搭配 RoadRunner 仍能達到每秒 4,812 個真實 HTTP PDF 回應,延遲低於毫秒級且零失敗請求。

測試方法論

  • 環境: 所有函式庫在相同 Docker 容器(PHP 8.5.3、Debian bookworm)中以相同設定執行。
  • 資源限制: 容器透過 Docker 資源限制設定為 4 CPUs 與 16 GB RAM。
  • 執行環境: 所有函式庫皆啟用 OPcache 與 JIT。為確保公平計時,全域抑制棄用警告。
  • 子程序隔離: 每個函式庫/情境組合在獨立的 PHP 程序(exec())中執行,以精確測量記憶體 — 其他函式庫的自動載入器類別不會污染 memory_get_peak_usage()
  • API 對等: TCPDF-Next 與 TCPDF 在非 HTML 情境中使用原生 Cell/MultiCell API。DomPDF 與 mPDF 使用等效的 HTML 標記(其原生介面)。
  • TrueType 字型測試 使用 DejaVu Sans(~700 KB .ttf)以揭露實際字型解析成本;Helvetica(Base14)測試顯示零開銷基準。
  • Artisan (Chrome) 使用無頭 Chromium 透過 CDP 進行像素完美的 CSS3 渲染(透過 CSP 停用 JavaScript)。
  • Artisan 階段測試 拆解 Chrome 渲染:階段 1(Chrome CDP printToPDF)vs 階段 2(PdfReader + PageImporter + 嵌入)。
  • Worker 生命週期 比較 DocumentFactory(共用 FontRegistry + 鎖定、ImageRegistry)vs createStandalone()(每次呼叫建立全新註冊表)。
  • Worker 生命週期 TTF 展示 DocumentFactory 的核心價值:跨數千次 Worker 請求快取 TrueType 字型資料。
  • RoadRunner HTTP 使用 roadrunner-server/roadrunner 搭配 DocumentFactory Worker 模式,透過 ab(Apache Bench)測量。
  • 暖機: 在開始測量前執行並丟棄 3 次迭代,確保 OPcache 與 JIT 已完全暖機。
  • 迭代: 每個情境 20 次測量迭代。報告中位數以消除離群值干擾。
  • 吞吐量: 測試持續執行 30 秒。
  • 計時: hrtime(true) 提供奈秒精度掛鐘時間測量。
  • 記憶體: memory_get_peak_usage(true) 報告真實(RSS)峰值記憶體。
  • 檔案大小: 輸出寫入磁碟後以 filesize() 測量。

資料解讀注意事項

  • 記憶體的子程序隔離: 每個函式庫的延遲評測在其獨立的 PHP 子程序中執行。僅載入所需的自動載入器,因此 memory_get_peak_usage() 反映的是該函式庫單獨的實際記憶體成本 — 而非其他函式庫累積的自動載入器污染。
  • Artisan (Chrome) 使用 BrowserPool keep-alive: Chrome 程序在各迭代間保持存活,符合生產環境行為(RoadRunner/Swoole/Octane)。冷啟動開銷(初始 Chromium 啟動約 250 ms)已排除 — 這是一次性成本,可在數千次請求中攤銷。
  • 延遲與吞吐量的差距: 單次延遲測量包含 gc_collect_cycles()memory_reset_peak_usage()hrtime() 開銷(約 0.3 ms)。吞吐量測試在緊密迴圈中執行,不含測量開銷,因此其每文件時間(1000/文件每秒)低於單次中位數。吞吐量數據更準確地反映生產環境效能。
  • Helvetica vs TrueType: Helvetica 是內建 PDF 字型(Base14),完全無需檔案 I/O。TrueType 情境使用 DejaVu Sans,需要解析 .ttf 檔案(~700 KB)。DocumentFactory 的快取 FontRegistry 優勢僅在 TrueType 字型時才會顯現。
  • JIT 相容性(*): 標示 * 的數值是在停用 JIT(opcache.jit=0)的情況下測量的,因為該函式庫的程式碼路徑會觸發 PHP JIT 區段錯誤(SIGSEGV,退出碼 139)。OPcache 位元組碼快取仍保持啟用。這是已知的一類 PHP JIT 錯誤,會影響某些複雜迴圈模式。

重現評測

評測套件包含在儲存庫中。若要重現這些結果:

bash
cd benchmark
docker compose up --build

結果會在執行結束時輸出至 stdout。Docker 設定確保環境一致,不受主機作業系統影響。

延伸閱讀

以 LGPL-3.0-or-later 授權釋出。