Skip to content

Продуктивність

PDF Oxide v0.3.11 забезпечує 0.8мс середнє вилучення тексту в Python на корпусі з 3 830 PDF — у 5× швидше за PyMuPDF та у 15× швидше за pypdf, зі 100% рівнем успіху на валідних PDF.

Результати бенчмарків

Корпус: 3 830 PDF

Три незалежні публічні тестові набори, об’єднані разом:

Набір PDF Джерело
veraPDF 2 907 Корпус тестування відповідності PDF/A
Mozilla pdf.js 897 Тестовий набір рендерингу PDF для браузера
SafeDocs 26 Корпус некоректних PDF від DARPA SafeDocs

Чому цей корпус надійний

Це не вручну обрані PDF. Кожен набір — це усталений, рецензований тестовий корпус, що підтримується органами стандартизації або великими проєктами з відкритим кодом:

  • veraPDF — офіційний валідатор відповідності PDF/A, який використовується спільнотою стандартів PDF понад 10 років. Його 2 907 тестових файлів є атомарними — кожен тестує рівно одну характеристику специфікації PDF — охоплюючи всі версії PDF/A (1A/1B, 2A/2B/2U, 3A/3B/3U, 4/4E/4F), доступність PDF/UA (UA1, UA2) та обидва стандарти ISO 32000-1 (PDF 1.7) та ISO 32000-2 (PDF 2.0). Ліцензія CC BY 4.0.

  • Mozilla pdf.js забезпечує рендеринг PDF у Firefox, обробляючи мільярди PDF щорічно. Його 897 тестових файлів охоплюють реальні граничні випадки рендерингу: складні багатосторінкові документи, типи анотацій (стилі рамок, виділення, маркери, вкладені файли), віджети форм, незвичні кодування шрифтів, великі стрес-тестові документи та граничні випадки content stream. 7 файлів навмисно пошкоджені для тестування коректного відхилення помилок.

  • DARPA SafeDocs — програма дослідження безпеки, фінансована урядом США, зосереджена на стійкості парсерів. Її 26 файлів націлені на найскладніші граничні випадки: цикли content stream у шрифтах Type3, подвійні трейлери startxref, компактований синтаксис PDF, варіації діалектів, граничні випадки вбудованих зображень, рекурсивне вкладення шрифтів та зашифровані PDF з паролями Unicode (включаючи UTF-16 LE). Ці файли розроблені для того, щоб аварійно завершити, заблокувати або скомпрометувати вразливі парсери.

Що охоплює цей корпус

  • Усі основні версії PDF: PDF 1.0 до PDF 2.0
  • Шифрування та паролі: AES-256, RC4, паролі Unicode, кодування UTF-16 LE
  • Граничні випадки безпеки: Рекурсивні структури, цикли content stream, некоректні трейлери, пошкодження, згенеровані фазингом
  • Різноманітність шрифтів: TrueType, CIDFont, Type1, Type3, кодування CJK, вбудовані підмножини
  • Складність документів: Від фікстур з однією сторінкою до документів з понад 10 000 сторінок, вбудовані зображення, вкладені Form XObjects
  • Коректне відхилення: 7 навмисно зламаних файлів (відсутні заголовки PDF, невалідні потоки xref) — бібліотека, яка «парсить» їх, є менш безпечною, а не більш надійною

З 3 830 файлів 3 823 є валідними PDF. 7 невалідних файлів — це тестові фікстури для обробки помилок — PDF Oxide коректно відхиляє всі 7.

Порівняння бібліотек Python

Середній час вилучення тексту на PDF на повному корпусі з 3 830 PDF:

Бібліотека Середнє p99 Рівень успіху Ліцензія
PDF Oxide 0.8мс 9мс 100% MIT
PyMuPDF 4.6мс 28мс 99.3% AGPL-3.0
pypdfium2 4.1мс 42мс 99.2% Apache-2.0
pymupdf4llm 55.5мс 280мс 99.1% AGPL-3.0
pdftext 7.3мс 82мс 99.0% GPL-3.0
pdfminer 16.8мс 124мс 98.8% MIT
pdfplumber 23.2мс 189мс 98.8% MIT
markitdown 108.8мс 378мс 98.6% MIT
pypdf 12.1мс 97мс 98.4% BSD-3

PDF Oxide — найшвидша бібліотека PDF, доступна для Python. На відміну від PyMuPDF, використовує ліцензію MIT — жодних обмежень AGPL для комерційного використання.

Порівняння бібліотек Rust

Бібліотека Середнє p99 Рівень успіху Вилучення тексту
PDF Oxide 0.8мс 9мс 100% Вбудоване, рівень продакшену
oxidize_pdf 13.5мс 11мс 99.1% Базове
unpdf 2.8мс 10мс 95.1% Базове
pdf_extract 4.08мс 37мс 91.5% Базове
lopdf 0.3мс 2мс 80.2% Без вбудованого вилучення

lopdf швидший на PDF, які може розпарсити, але не справляється з 20% корпусу. pdf_oxide — єдиний Rust crate, що поєднує 100% надійність із вбудованим вилученням тексту. Зверніть увагу, що lopdf не надає вилучення тексту — вам потрібно самостійно будувати декодування шрифтів та аналіз інтервалів.

Якість тексту

PDF Oxide досягає 99.5% паритету тексту порівняно з PyMuPDF та pypdfium2 на повному корпусі. Якість вимірювалася посимвольним порівнянням вилученого тексту по всіх 3 823 валідних PDF.

Розбивка за корпусами

Корпус PDF PDF Oxide середнє pypdfium2 середнє PyMuPDF середнє
veraPDF 2 907 0.7мс 3.6мс 4.1мс
Mozilla pdf.js 897 1.1мс 5.8мс 6.2мс
SafeDocs 26 0.9мс 4.0мс 4.3мс

Історія оптимізацій: v0.3.5 → v0.3.8

v0.3.8 усунула два критичних вузьких місця O(n), що знизило середнє з 23.3мс до 0.8мс (Python) на тому ж корпусі.

1. Масове кешування дерева сторінок

До: get_page() обходив дерево сторінок від кореня для кожної некешованої сторінки. Для послідовного вилучення всіх сторінок це було O(n) на сторінку та O(n²) загалом.

Після: При першому зверненні до сторінки все дерево сторінок обходиться один раз, і всі сторінки кешуються в HashMap<usize, Object>. Кожне наступне звернення — O(1).

Це виправлення, яке зменшило час обробки PDF з 10 000 сторінок з 55 секунд до 332 мілісекунд.

2. Кеш зміщень scan-for-object

До: Коли об’єкти були відсутні в таблиці xref, scan_for_object() читав весь PDF-файл для кожного відсутнього об’єкта. Теговані PDF з сотнями елементів дерева структури, яких не було в xref, спричиняли сотні повних читань файлу.

Після: Файл сканується один раз, і всі зміщення об’єктів кешуються в HashMap. Подальші пошуки — O(1).

3. Однопрохідне вилучення тексту

До: extract_spans() виконував два проходи по вмісту сторінки — спочатку для класифікації типу документа (академічний, газета, форма тощо), потім для вилучення тексту.

Після: Прохід класифікації повністю усунено. Адаптивні пороги з урахуванням шрифту тепер дають рівні або кращі результати за один прохід.

4. Попереднє виділення пам’яті для content stream

До: parse_content_stream() будував Vec операторів, починаючи зі стандартної ємності, що спричиняло повторні перерозподіли при великих content streams.

Після: Vec попередньо виділяється на основі розміру потоку (data.len() / 20), що оцінює приблизно один оператор на 20 байтів.

Методологія

Всі бенчмарки використовують однакову методологію:

  1. Кожна бібліотека обробляє всі 3 830 PDF з використанням multiprocessing Python (один PDF на процес)
  2. Тайм-аут 60 секунд на PDF — будь-який PDF, що перевищує цей час, вважається невдачею
  3. Вилучений текст зберігається на диск для кожної бібліотеки для порівняння якості
  4. Час вимірюється від відкриття файлу до фінального вилучення тексту
  5. Без прогрівних запусків, без кешування між файлами
  6. Один потік на PDF

Тестовий стенд запускає всі 18 бібліотек (3 Rust, 15 Python) на одній машині, одному корпусі, в однакових умовах.

Відтворення бенчмарків

Публічні тестові корпуси доступні безкоштовно:

Запустіть верифікацію:

cargo run --release --example verify_corpus -- \
    /path/to/veraPDF-corpus \
    /path/to/pdfjs-test \
    /path/to/safedocs \
    --csv results.csv

Характеристики продуктивності

Де PDF Oxide швидкий

  • Вилучення тексту: Головна ціль оптимізації. Суб-мілісекунда для типових документів.
  • Послідовне вилучення з багатьох сторінок: Кеш дерева сторінок робить вилучення всіх сторінок великого документа майже таким же швидким, як вилучення однієї.
  • Теговані PDF: Обхід дерева структури та роздільна здатність об’єктів тепер кешуються.
  • Некоректні PDF: Толерантний парсинг зі стратегіями fallback уникає дорогих повторних спроб.

Що масштабується лінійно

  • Кількість сторінок: Кожна сторінка обробляється незалежно. 100 сторінок займає приблизно у 100× більше, ніж одна сторінка.
  • Розмір content stream: Парсинг операторів є лінійним відносно довжини потоку.
  • Вилучення зображень: Пропорційне кількості та розміру зображень.

Коли очікувати повільніших результатів

  • Скановані PDF з OCR: Обробка OCR (якщо увімкнена) значно повільніша за вилучення тексту.
  • Рендеринг: Рендеринг сторінок у зображення залежить від складності вмісту та цільового DPI.
  • Сильно зашифровані PDF: Дешифрування AES-256 додає накладні витрати на кожен потік.
  • PDF з тисячами шрифтів: Парсинг шрифтів кешується на рівні документа, але початковий парсинг масштабується з кількістю шрифтів.

Наступні кроки