Бенчмарки производительности
Реальные бенчмарки производительности, сравнивающие TCPDF-Next с тремя устоявшимися PHP-библиотеками для работы с PDF: TCPDF, DomPDF и mPDF. Все тесты выполнены на идентичном оборудовании в контролируемых условиях Docker. Результаты представляют медианы 20 итераций для устранения статистических выбросов.
Тестовое окружение
| Параметр | Значение |
|---|---|
| CPU | Intel Core i9-13900K (x86-64) |
| RAM | 64 ГБ DDR5 (Docker ограничен 16 ГБ) |
| Docker | 4 CPU, 16 ГБ RAM, Debian bookworm |
| PHP | 8.5.3 (CLI, OPcache включён, JIT включён) |
| TCPDF-Next | 1.7.0 |
| TCPDF | 6.10.1 |
| DomPDF | v3.1.4 |
| mPDF | v8.2.7 |
| Artisan (Chrome) | Headless Chromium через CDP |
| RoadRunner | spiral/roadrunner-http ^3.6 (тесты HTTP-пропускной способности) |
| Прогрев | 3 итерации (отброшены) |
| Измерения | 20 итераций (представлена медиана) |
| Хронометраж | hrtime(true) наносекундная точность |
Интерактивное сравнение
PHP 8.5.3 + OPcache + JIT · Docker 4 CPUs / 16 GB · i9-13900K · Median of 20 runs
Скорость генерации
Каждый сценарий выполнялся 20 раз после 3 прогревочных итераций. Представлено медианное время генерации.
Простой документ (1 страница)
Одна страница A4 с заголовком и базовым форматированным текстом на встроенном шрифте Helvetica. Без изображений, без таблиц.
| Библиотека | Время (мс) |
|---|---|
| TCPDF-Next | 0,68 |
| TCPDF | 2,55 |
| DomPDF | 4,16 |
| mPDF | 6,71 |
TCPDF-Next завершает простейший сценарий менее чем за 1 мс — в 3,8x быстрее TCPDF, в 6,1x быстрее DomPDF и в 9,9x быстрее mPDF.
Счёт (2 страницы)
Двухстраничный счёт с 25-строчной таблицей позиций, промежуточными итогами, колонтитулами и изображением логотипа.
| Библиотека | Время (мс) |
|---|---|
| TCPDF | 1,96 |
| TCPDF-Next | 2,01 |
| mPDF | 15,86 |
| DomPDF | 17,33 |
TCPDF-Next и TCPDF фактически равны в сценарии счёта (~1,0x). Оба значительно превосходят mPDF (в 7,9x медленнее) и DomPDF (в 8,6x медленнее).
100-страничный отчёт
100-страничный документ с плотным смешанным контентом: заголовки, абзацы и структурированные данные.
| Библиотека | Время (мс) |
|---|---|
| TCPDF-Next | 34,29 |
| TCPDF | 105,39 |
| mPDF | 1 106,59* |
| DomPDF | 2 129,12 |
TCPDF-Next завершает 100-страничный отчёт за 34,29 мс — в 3,1x быстрее TCPDF, в 32,3x быстрее mPDF и в 62,1x быстрее DomPDF.
Примечание по совместимости с JIT
*Результат mPDF для 100-страничного отчёта получен с выключенным JIT (opcache.jit=0) из-за segfault PHP JIT (SIGSEGV, exit code 139) в пути выполнения кода mPDF. Кеширование байт-кода OPcache оставалось активным. Это известный класс ошибок PHP JIT, затрагивающих определённые сложные циклические паттерны. Все остальные сценарии mPDF выполнялись с включённым JIT.
TrueType-документ (1 страница)
Одна страница A4 с использованием DejaVu Sans (~700 КБ TrueType-шрифт). Этот сценарий выявляет реальные затраты на парсинг шрифтовых файлов — в отличие от Helvetica (встроенный шрифт Base14, не требующий файлового ввода-вывода).
| Библиотека | Время (мс) |
|---|---|
| TCPDF-Next | 4,08 |
| TCPDF | 12,11 |
| mPDF | 16,51 |
| DomPDF | 24,14 |
TCPDF-Next парсит и встраивает TrueType-шрифт в 3,0x быстрее TCPDF, в 4,0x быстрее mPDF и в 5,9x быстрее DomPDF.
Относительная скорость (все сценарии)
Все значения относительно TCPDF-Next (базовый уровень 1,0x). Меньше = быстрее.
| Сценарий | TCPDF-Next | TCPDF | DomPDF | mPDF |
|---|---|---|---|---|
| Простой документ | 1,0x | 3,8x | 6,1x | 9,9x |
| Счёт | 1,0x | ~1,0x | 8,6x | 7,9x |
| 100-стр. отчёт | 1,0x | 3,1x | 62,1x | 32,3x |
| TrueType-документ | 1,0x | 3,0x | 5,9x | 4,0x |
HTML в PDF
Подходы к обработке HTML
Различные библиотеки используют принципиально разные подходы к конвертации HTML в PDF. Понимание этих различий важно для интерпретации результатов бенчмарков:
Прямая трансляция (TCPDF-Next, TCPDF) — Встроенный парсер HTML токенизирует HTML-теги и отображает их непосредственно на команды отрисовки PDF (Cell, MultiCell, Image) за один потоковый проход. Этот подход крайне быстр, но поддерживает только базовые HTML-теги и inline CSS — без Flexbox, Grid и сложных CSS-селекторов.
Движок CSS-макетов (DomPDF, mPDF) — Эти библиотеки разработаны с HTML как основным интерфейсом. DomPDF строит полное DOM-дерево, применяет каскад CSS (специфичность, наследование) и вычисляет блочную модель макета перед рендерингом в PDF. mPDF аналогично обрабатывает HTML через свой движок CSS-макетов. Обе поддерживают больше CSS-возможностей, чем парсеры прямой трансляции (float, позиционированные элементы, стилизованные таблицы), но не достигают полной поддержки CSS3 уровня браузера.
Полноценный рендеринг браузера (Artisan / Chrome) — TCPDF-Next Artisan делегирует рендеринг headless Chromium через Chrome DevTools Protocol (CDP). Это обеспечивает пиксельно-точную поддержку CSS3: Flexbox, Grid, Web Fonts, media queries, CSS-переменные — результат идентичен тому, что производит браузер Chrome.
Бенчмарк сравнивает нативный подход каждой библиотеки: TCPDF-Next и TCPDF используют встроенный парсер прямой трансляции; DomPDF и mPDF используют свои движки CSS-рендеринга (их основной API); Artisan использует Chrome.
Результаты
| Библиотека | Подход | Время (мс) |
|---|---|---|
| TCPDF-Next | Прямая трансляция | 1,51 |
| TCPDF | Прямая трансляция | 6,60 |
| DomPDF | Движок CSS-макетов | 13,69 |
| mPDF | Движок CSS-макетов | 29,63 |
| Artisan (Chrome) | Полный рендеринг браузера | 66,70 |
Относительное время (HTML в PDF)
| Библиотека | Относительно |
|---|---|
| TCPDF-Next | 1,0x |
| TCPDF | 4,4x |
| DomPDF | 9,0x |
| mPDF | 19,6x |
| Artisan (Chrome) | 44,1x |
Парсер прямой трансляции TCPDF-Next обеспечивает производительность менее 2 мс — в 4,4x быстрее парсера TCPDF на основе regex, в 9,0x быстрее движка CSS-макетов DomPDF и в 19,6x быстрее mPDF. Artisan (Chrome) в 44,1x медленнее, но обеспечивает полную точность CSS3, которую не может обеспечить ни одна другая библиотека.
Artisan Chrome — разбивка по фазам
Декомпозиция пайплайна Artisan (Chrome) на две фазы:
- Chrome CDP Render — headless Chrome конвертирует HTML в PDF-байты через
printToPDF - PDF Import + Embed — TCPDF-Next парсит PDF Chrome, извлекает страницу как Form XObject и встраивает в целевой документ
| Фаза | Медиана (мс) | Среднее (мс) | Мин (мс) | Макс (мс) | Стд. откл. |
|---|---|---|---|---|---|
| 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 |
| Итого | 83,35 | 83,29 | 68,20 | 97,56 | 4,70 |
Распределение времени: Chrome CDP = 97,4% | PDF Import = 2,3%
printToPDF Chrome доминирует в пайплайне — 97,4% общего времени. Фаза PDF Import (PdfReader + PageImporter + встраивание XObject) добавляет лишь ~2 мс — пренебрежимые накладные расходы.
Стандартное vs пофазное измерение
Стандартный тест Artisan (66,70 мс) использует интегрированный метод writeHtmlChrome() с keep-alive BrowserPool. Пофазный тест (83,35 мс итого) инструментирует каждую фазу отдельно, добавляя накладные расходы измерения. Оба используют тот же Chrome-инстанс с keep-alive — стоимость холодного старта ~250 мс при первоначальном запуске Chromium исключена как разовая стоимость, амортизируемая на тысячи запросов.
Когда использовать какой подход
Для простого HTML (таблицы, базовое форматирование) используйте встроенный HTML-парсер TCPDF-Next (1,51 мс). Для сложных CSS3-макетов, требующих пиксельной точности (Flexbox, Grid, Web Fonts), используйте Artisan — накладные расходы ~67 мс дают полную мощь движка рендеринга Chrome.
Жизненный цикл воркеров (DocumentFactory vs Standalone)
TCPDF-Next предоставляет паттерн DocumentFactory, разработанный для долгоживущих PHP-воркеров (RoadRunner, Swoole, Laravel Octane). Фабрика предварительно инициализирует и блокирует общие реестры (FontRegistry, ImageRegistry) при загрузке. Каждый HTTP-запрос создаёт лёгкий, одноразовый Document из фабрики — устраняя накладные расходы на инициализацию каждого запроса.
Этот раздел сравнивает DocumentFactory (общие, заблокированные реестры) с createStandalone() (свежие реестры при каждом вызове).
Встроенные шрифты (Helvetica)
| Режим | Медиана (мс) | Пиковая память (МБ) | Размер файла (КБ) |
|---|---|---|---|
| DocumentFactory | 0,60 | 4,0 | 3,3 |
| createStandalone() | 0,70 | 4,0 | 3,3 |
Результат: ~эквивалентно (соотношение 0,86x). Со встроенными шрифтами (Helvetica) оба режима работают идентично, поскольку нет парсинга шрифтовых файлов для кеширования. Реальное преимущество DocumentFactory проявляется с TrueType-шрифтами.
TrueType-шрифты (DejaVu Sans)
Это ключевой тест ценности DocumentFactory. В отличие от теста Helvetica выше (встроенный шрифт, нулевой парсинг), этот тест использует DejaVu Sans (~700 КБ TrueType-шрифт). DocumentFactory предварительно регистрирует и кеширует распарсенные данные шрифта при загрузке — последующие запросы пропускают весь файловый ввод-вывод шрифтов. createStandalone() должен парсить файл .ttf при каждом запросе.
| Режим | Медиана (мс) | Пиковая память (МБ) | Размер файла (КБ) |
|---|---|---|---|
| Factory (TTF кешировано) | 2,60 | 6,0 | 24,5 |
| Standalone (TTF парсинг) | 4,09 | 6,0 | 24,3 |
Ускорение Factory: 1,6x — Кешированный парсинг шрифтов устраняет ~1,5 мс на запрос. В воркере RoadRunner/Swoole, обрабатывающем 1 000 запросов/минуту, это экономит ~25 секунд CPU-времени в минуту.
Пиковое использование памяти
Все значения в МБ (медиана). Бенчмарк каждой библиотеки выполняется в собственном подпроцессе PHP — загружается только необходимый автозагрузчик, поэтому memory_get_peak_usage() отражает фактическую стоимость памяти только этой библиотеки.
Стандартные сценарии
| Сценарий | TCPDF-Next | TCPDF | DomPDF | mPDF |
|---|---|---|---|---|
| Простой документ | 4,0 | 12,0 | 6,0 | 14,0 |
| Счёт | 4,0 | 12,0 | 12,0 | 14,0 |
| 100-стр. отчёт | 4,0 | 12,0 | 66,0 | 27,9* |
| TrueType-документ | 6,0 | 14,0 | 20,0 | 16,0 |
TCPDF-Next поддерживает стабильный отпечаток 4 МБ от 1-страничных до 100-страничных документов, демонстрируя эффективное управление памятью через уплотнённые объекты страниц и общие ссылки на ресурсы.
Память HTML в PDF
| Библиотека | Пиковая память (МБ) |
|---|---|
| TCPDF-Next | 4,0 |
| Artisan (Chrome) | 4,0 |
| DomPDF | 10,0 |
| TCPDF | 12,0 |
| mPDF | 18,0 |
Artisan (Chrome) измеряет только память на стороне PHP — процесс headless Chrome имеет собственное пространство памяти, управляемое ОС.
Размер выходного файла
Все значения в КБ (медиана).
Стандартные сценарии
| Сценарий | TCPDF-Next | TCPDF | DomPDF | mPDF |
|---|---|---|---|---|
| Простой документ | 3,3 | 7,1 | 1,7 | 28,0 |
| Счёт | 5,0 | 9,2 | 4,0 | 30,2 |
| 100-стр. отчёт | 96,4 | 100,8 | 128,7 | 181,1* |
| TrueType-документ | 24,7 | 101,3 | 16,1 | 42,4 |
DomPDF создаёт наименьшие файлы для простых документов (1,7 КБ) за счёт агрессивной оптимизации потоков контента. TCPDF-Next создаёт компактный вывод через потоки перекрёстных ссылок и объектные потоки PDF 2.0. TCPDF встраивает значительно больший подмножество TrueType-шрифта (101,3 КБ vs 24,7 КБ).
Размер файла HTML в PDF
| Библиотека | Размер файла (КБ) |
|---|---|
| DomPDF | 5,3 |
| TCPDF-Next | 6,6 |
| TCPDF | 12,6 |
| Artisan (Chrome) | 36,9 |
| mPDF | 46,0 |
Вывод Artisan (Chrome) больше (36,9 КБ), потому что printToPDF Chrome генерирует полный самостоятельный PDF со встроенными ресурсами.
Пропускная способность
Тесты пропускной способности выполняются непрерывно в течение 30 секунд с использованием сценария простого документа. Значения отражают устойчивую производительность генерации под нагрузкой.
Стандартная (док/сек)
| Режим | TCPDF-Next | TCPDF | DomPDF | mPDF |
|---|---|---|---|---|
| Один поток | 2 605 | 1 169 | 233 | 130 |
| 4 воркера | 9 221 | 4 163 | 841 | 487 |
Документов в минуту
| Режим | TCPDF-Next | TCPDF | DomPDF | mPDF |
|---|---|---|---|---|
| Один поток | 156 284 | 70 134 | 13 956 | 7 800 |
| 4 воркера | 553 280 | 249 752 | 50 484 | 29 194 |
С 4 воркерами TCPDF-Next поддерживает свыше 9 200 документов в секунду — более 553 000 документов в минуту. Это в 2,2x превышает пропускную способность TCPDF, в 11,0x DomPDF и в 19,0x mPDF.
Пропускная способность жизненного цикла воркеров
Сравнение DocumentFactory и createStandalone() при использовании встроенного шрифта Helvetica.
| Режим | DocumentFactory | createStandalone() |
|---|---|---|
| Один поток | 2 490 | 2 515 |
| 4 воркера | 9 074 | 9 191 |
Со встроенными шрифтами DocumentFactory и createStandalone() обеспечивают эквивалентную пропускную способность — нет парсинга шрифтов для кеширования.
Пропускная способность TrueType-документов
Пропускная способность с TrueType-шрифтами (DejaVu Sans) — выявляет реальные накладные расходы парсинга шрифтов для каждой библиотеки.
| Библиотека | Один поток (док/сек) |
|---|---|
| TCPDF-Next | 242 |
| TCPDF | 81 |
| mPDF | 50 |
| DomPDF | 30 |
Пропускная способность TCPDF-Next с TrueType в 3,0x превышает TCPDF, в 4,8x mPDF и в 8,0x DomPDF — отражая эффективный subsetting и кеширование шрифтов.
Пропускная способность TTF жизненного цикла воркеров
DocumentFactory (кешированные данные TrueType-шрифта) vs createStandalone() (парсинг TTF при каждом запросе).
| Режим | Factory (TTF кешировано) | Standalone (TTF парсинг) |
|---|---|---|
| Один поток | 364 | 243 |
| 4 воркера | 1 327 | 871 |
Преимущество пропускной способности Factory: 1,5x (один поток). Кешированные данные TrueType-шрифта устраняют накладные расходы парсинга .ttf при каждом запросе. С 4 воркерами фабрика достигает 1 327 док/сек — улучшение на 52,4% по сравнению со standalone.
HTTP-пропускная способность RoadRunner
Реальный бенчмарк HTTP-сервера с использованием RoadRunner с паттерном воркера DocumentFactory. Измерено через ab (Apache Bench).
| Конфигурация | Док/сек | Средняя задержка (мс) | p50 (мс) | p99 (мс) | Ошибки |
|---|---|---|---|---|---|
| 1 воркер / 1 параллельный | 1 320 | 0,76 | 1 | 1 | 0 |
| 4 воркера / 4 параллельных | 4 812 | 0,83 | 1 | 1 | 0 |
Накладные расходы HTTP vs чистый pcntl_fork:
- Один поток: 49,3% накладных расходов (1 320 vs 2 605 док/сек)
- Несколько воркеров: 47,8% накладных расходов (4 812 vs 9 221 док/сек)
~48% накладных расходов отражают стоимость полного HTTP-стека (TCP accept, парсинг HTTP, запись ответа). Даже с этими накладными расходами TCPDF-Next за RoadRunner обеспечивает 4 812 реальных HTTP PDF-ответов в секунду с субмиллисекундной задержкой и нулём ошибок.
Методология
- Окружение: Все библиотеки работают внутри одного Docker-контейнера (PHP 8.5.3, Debian bookworm) с идентичной конфигурацией.
- Ограничения ресурсов: Контейнер ограничен 4 CPU и 16 ГБ RAM через ограничения ресурсов Docker.
- Среда выполнения: OPcache и JIT включены для всех библиотек. Предупреждения об устаревании подавлены глобально для честного хронометража.
- Изоляция подпроцессов: Каждая пара библиотека/сценарий выполняется в отдельном PHP-процессе (
exec()) для точного измерения памяти — классы автозагрузчика других библиотек не загрязняютmemory_get_peak_usage(). - Паритет API: TCPDF-Next и TCPDF используют нативный API
Cell/MultiCellдля не-HTML сценариев. DomPDF и mPDF используют эквивалентную HTML-разметку (их нативный интерфейс). - Тест TrueType-шрифтов использует DejaVu Sans (~700 КБ
.ttf) для выявления реальных затрат на парсинг шрифтов; тесты Helvetica (Base14) показывают базовый уровень без накладных расходов. - Artisan (Chrome) использует headless Chromium через CDP для пиксельно-точного рендеринга CSS3 (JavaScript отключён через CSP).
- Artisan пофазный декомпозирует рендеринг Chrome: Фаза 1 (Chrome CDP
printToPDF) vs Фаза 2 (PdfReader + PageImporter + встраивание). - Жизненный цикл воркеров сравнивает
DocumentFactory(общие FontRegistry + блокировка, ImageRegistry) vscreateStandalone()(свежие реестры при каждом вызове). - Жизненный цикл воркеров TTF демонстрирует ключевую ценность
DocumentFactory: кешированные данные TrueType-шрифта для тысяч запросов воркера. - RoadRunner HTTP использует roadrunner-server/roadrunner с паттерном воркера
DocumentFactory, измерено через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). Накладные расходы холодного старта (~250 мс на первоначальный запуск Chromium) исключены — это разовая стоимость, амортизируемая на тысячи запросов.
- Разрыв задержка vs пропускная способность: Измерения задержки одного запуска включают
gc_collect_cycles(),memory_reset_peak_usage()и накладные расходыhrtime()(~0,3 мс). Тесты пропускной способности выполняются в плотном цикле без накладных расходов измерения, поэтому их время на документ (1000/док_в_сек) ниже медианы одного запуска. Показатели пропускной способности точнее отражают производственную производительность. - Helvetica vs TrueType: Helvetica — встроенный PDF-шрифт (Base14), не требующий файлового ввода-вывода. Сценарий TrueType использует DejaVu Sans, что требует парсинга файлов
.ttf(~700 КБ). Преимущество кешированного FontRegistry уDocumentFactoryпроявляется только с TrueType-шрифтами. - Совместимость с JIT (*): Значения, помеченные *, измерены с выключенным JIT (
opcache.jit=0) из-за segfault PHP JIT (SIGSEGV, exit code 139) в пути выполнения кода этой библиотеки. Кеширование байт-кода OPcache остаётся активным. Это известный класс ошибок PHP JIT, затрагивающих определённые сложные циклические паттерны.
Воспроизведение бенчмарков
Набор бенчмарков включён в репозиторий. Для воспроизведения результатов:
cd benchmark
docker compose up --buildРезультаты выводятся в stdout по завершении теста. Настройка Docker обеспечивает идентичное окружение независимо от хостовой ОС.
Дополнительные материалы
- Миграция с TCPDF — Пошаговое руководство по миграции
- Оптимизация производительности — Потоковый режим, оптимизация памяти и стратегии кеширования