46 lines
2.6 KiB
Markdown
46 lines
2.6 KiB
Markdown
# v0.0.39 — оптимизация apply_replacements (однопроходная замена) — 2026-08-19
|
||
|
||
**Дата:** 2026-08-19
|
||
**Версия:** 0.0.38 → 0.0.39
|
||
**Файл:** `drhider/replacer.py`, `site/app.py` (версия)
|
||
|
||
---
|
||
|
||
## Суть
|
||
|
||
Обфускация PDF 19 МБ шла 132.7с, из них ИИ (LLM) всего 7с — остальное (~125с)
|
||
тратил основной код. Замер выявил узкое место: `apply_replacements`.
|
||
|
||
## Причина
|
||
|
||
`apply_replacements` для **каждой** сущности из mapping делала **2 полных
|
||
прохода `re.sub` по всему тексту** (основной + фоллбэк `_md_tolerant_pattern`).
|
||
Сложность O(N_сущностей × размер_текста × 2) — квадратичная по числу сущностей.
|
||
При 300+ сущностей и тексте в несколько МБ — десятки секунд.
|
||
|
||
Замер (0.9 МБ, 304 сущности): **17.3 с**.
|
||
|
||
## Решение
|
||
|
||
Однопроходная замена:
|
||
- Все ключи собираются в **один** regex-паттерн через альтернацию `(?:...|...)`
|
||
(по убыванию длины, с `re.escape`, границы `(?<!\w)...(?!\w)` для alnum-сущностей).
|
||
- Один `re.sub` с callback (lookup в mapping), O(text).
|
||
- Фоллбэк `_md_tolerant_pattern` применяется **только к ключам, не найденным**
|
||
основной заменой (отслеживание через `matched_keys`), а не ко всем.
|
||
|
||
## Результат замера
|
||
- 300 сущностей реально в тексте (1.3 МБ): **1.17 с** (было бы десятки сек).
|
||
- Реалистичный сценарий (10 сущностей): **0.29 с**.
|
||
- Согласованность токенов и mapping.csv сохранены (проверено через `obfuscate`).
|
||
|
||
## Проверка
|
||
- `test_replacer` 10/10, `test_builder` 20/20, `test_scanner` 20/20, `test_zip` 28/28.
|
||
- Пайплайн `obfuscate`: «ООО Ромашка»→«Вектор_0001» в двух файлах согласовано,
|
||
mapping.csv корректен.
|
||
|
||
## Примечание
|
||
- `scan_regex` (0.4с) и upload (сеть ~1.7 МБ/с) — не узкие места, не трогали.
|
||
- `pdf_to_markdown` (pdfplumber) локально не замерен (нет библиотеки); остаётся
|
||
потенциальным вторым узким местом для будущей оптимизации (pypdf/pdfminer).
|