Gigatoken: токенизатор на Rust обрабатывает текст в 989 раз быстрее HuggingFace
Студент PhD из Стэнфорда выпустил Gigatoken — BPE-токенизатор на Rust, обрабатывающий 24,53 ГБ/с на 144-ядерном сервере (в 681–989 раз быстрее OpenAI tiktoken и HuggingFace tokenizers). Ускорение достигнуто ручной оптимизацией претокенизации с SIMD, кешированием повторяющихся слов и минимизацией взаимодействия с Python. Библиотека под MIT-лицензией, поддерживает 23 семейства токенизаторов (GPT, Llama, Qwen, DeepSeek и др.), доступна через pip install gigatoken.
Токенизация — первый шаг каждого запроса к LLM, и ускорение в 100–1000 раз напрямую снижает latency inference-пайплайнов и обучения на больших корпусах. Gigatoken показывает, что «мелкие» компоненты часто игнорируют, хотя там лежат серьёзные резервы производительности.
Почему токенизацию не профилировали
Токенизация — превращение текста в последовательность числовых токенов — редко попадает под лупу оптимизаторов языковых моделей. Марсель Рёд, аспирант Стэнфорда, решил исправить это упущение и выпустил Gigatoken — BPE-токенизатор на Rust с Python-биндингами (MIT-лицензия, v0.9.0).
На эталонном корпусе owt_train.txt (11,9 ГБ) токенизатор GPT-2 показал 24,53 ГБ/с на двухсокетной AMD EPYC 9565 (144 ядра). Для сравнения: OpenAI tiktoken выдал 36 МБ/с, HuggingFace tokenizers — 24,8 МБ/с на том же железе. Прирост — 681× и 989× соответственно.
На Apple M4 Max (16 ядер) та же задача выполняется со скоростью 8,79 ГБ/с (в 1268 раз быстрее HuggingFace, в 140 раз — tiktoken). На потребительском AMD Ryzen 7 9800X3D — 6,27 ГБ/с (106× и 68×). Ускорение не зависит от архитектуры CPU или словаря.
Откуда такая скорость
Выигрыш не в алгоритме BPE-слияния, а в двух местах, которые считались решёнными:
1. Претокенизация вручную. Стандартные реализации делегируют её regex-движкам. Gigatoken пишет state machine с нуля. Прогрессия на 100 МБ OpenWebText: - regex (fancy-regex): ~47 МиБ/с - базовый state machine: ~380 МиБ/с - winnow с SIMD-интринсиками NEON: 462 МиБ/с - прямой Iterator + таблица классов 256 байт + SWAR (SIMD Within A Register): 830 МиБ/с - dual-cursor ILP (эксплуатация instruction-level parallelism): 1049 МиБ/с
SWAR загружает 8 байт как u64 и проверяет все 8 символов branchless-арифметикой, без привязки к архитектуре. Два независимых курсора с безопасной точкой разделения позволяют out-of-order движку чередовать потоки на простаивающих execution портах. Итог: 22,3× быстрее regex.
2. Кеширование претокенов. Повторяющиеся слова не перекодируются, а извлекаются из кеша. Сложность — long-tail распределение, требующее тонкой балансировки размера кеша и минимизации синхронизации между потоками.
Режимы и совместимость
Gigatoken предлагает два режима: - Compatibility mode оборачивает HuggingFace/tiktoken и гарантирует побайтовую идентичность вывода, но платит накладными расходами Python (~200–300× прирост вместо 1000×). - Native API читает файлы напрямую через Rust — здесь публикуются headline-цифры.
Поддерживаются 23 семейства токенизаторов: GPT-2, GPT-OSS, Llama 3–4, Qwen 2–3.6, DeepSeek V3/R1/V4, GLM 4–5, Kimi K2, Nemotron 3, Phi-4, OLMo 2–3, ModernBERT, Gemma, Mistral.
Нюансы бенчмарков
Gigatoken кодирует целые файлы, автоматически находя границы документов и распараллеливаясь. HuggingFace и tiktoken тестировались на предразбитых фрагментах (100 МБ и 1 ГБ соответственно) без кеширования — базовые линии честные, но не идентичные по нагрузке.
SentencePiece-токенизаторы оптимизированы частично: Gemma 3 — 3,43 ГБ/с (9,6×), CodeLlama — 3,47 ГБ/с (10×). Прирост существенный, но на порядок ниже BPE.
Независимая воспроизводимость на KrabArena (Intel Xeon, 4 vCPU, 174 МБ OpenWebText): 277,8 МБ/с (26,2× против tiktoken, 83,4× против tokenizers). Все 35 356 документов прошли валидацию.
Что не сработало
Лог оптимизации честно перечисляет провалы: - hot/cold split (#[cold] + #[inline(never)]): регрессия до 580 МиБ/с — inline-барьер помешал LLVM объединить ASCII и Unicode ветви. - Двухпроходная классификация с SWAR-подсчётом переходов: алгоритмически корректна, но 354 МиБ/с — extra memory traffic перевесил экономию на ветвлениях. - Profile-guided optimization: нулевой эффект, т.к. внутренний цикл уже branchless, а граница слов зависит от данных.
Установка: pip install gigatoken. Репозиторий: 66,2% Rust, 33,3% Python. Интерактивный benchmark explorer позволяет переключать CPU и оценивать время на своём корпусе.
Ключевые выводы
- Токенизация — узкое место, которое игнорировали: прирост в 1000× доказывает, что оптимизация имеет смысл даже для «решённых» задач.
- Ручной state machine + SIMD Within A Register (SWAR) обходит regex в 22 раза без архитектурных интринсиков.
- Dual-cursor ILP использует out-of-order выполнение CPU, разрывая цепочку зависимостей (latency → throughput).
- Кеширование претокенов критично для реальных данных с long-tail распределением слов, но требует тонкой балансировки.
- Profile-guided optimization бесполезна, когда внутренний цикл уже branchless и data-dependent — важен микро-анализ, а не макро-профайлинг.
Автор: Анна Мельникова · Источник: marktechpost.com
**Честная работа, честные цифры.** Марсель Рёд сделал то, что мало кто делает: вскрыл слабое место, которое все считали незначительным, и выжал из него 1000-кратный прирост. Прелесть в том, что он не открыл новый алгоритм — он просто *правильно* реализовал старый. Лог оптимизации читается как детектив: dual-cursor ILP, SWAR вместо SIMD-интринсиков, честное признание провалов (привет, profile-guided optimization). Это не магия, а ремесло.
Но держите скепсис наготове. Бенчмарки не apples-to-apples: Gigatoken кодирует целые файлы, baseline'ы — предразбитые чанки без кеша. SentencePiece-токенизаторы оптимизированы в 10 раз слабее BPE. Compatibility mode теряет 70% прироста из-за Python-overhead. Если вы на HuggingFace-стеке и вам нужна побайтовая совместимость — реальный выигрыш ближе к 200×, не 1000×. Но даже 200× — это *очень много* для библиотеки, которую можно pip install. Если обрабатываете гигабайты текста — просто попробуйте. Если токенизируете по одному промпту — вам это не нужно.
Комментарии