Токенизация съедает до 64% времени до первого токена. Не инференс, а токенизация
Все два года оптимизации инференса ушли на то, что происходит после появления токенов: KV-кеш, prefix caching, continuous batching, спекулятивное декодирование. Работа Чженъю Чжана и Чжичао Цао замерила, что происходит до, и получила цифру, от которой неуютно: при высоком попадании в кеш промптов токенизация занимает до 64% времени до первого токена.
TL;DR: У кодинг-агентов промпт-кеш попадает в 94,1% случаев, и на этом фоне токенизация вырастает с 10% до 64% времени до первого токена, потому что фронтенд каждый раз токенизирует весь транскрипт заново. TokTier делает токенизацию stateful: переиспользует прошлую последовательность токенов, переклеивая только окно вокруг дописанного куска. Инкрементальный ремонт занимает 0,5 до 1,1 мс на контекстах от 100 тысяч до 3 миллионов символов, до 437 раз быстрее токенизатора Hugging Face. С vLLM медианный TTFT падает на 16 до 34%. Код есть, пакета на PyPI по сути нет.
Почему токенизация вдруг стала узким местом
Из-за формы трафика кодинг-агентов. Агент после каждого результата тула отправляет весь длинный транскрипт заново, дописав к нему немного. Авторы разобрали 153 951 вызов из двух агентных экосистем: медианный вызов добавляет около 1,4 тысячи символов, и только от 1 до 3,6% вызовов начинают или пересобирают сессию, зато эти немногие несут контексты в миллионы символов.
Модельная часть с этим давно справляется: попадание в промпт-кеш на уровне флота 94,1%, то есть инференс почти не пересчитывает то, что уже видел. А фронтенд пересчитывает всё. И когда попадание в кеш приближается к 0,99, доля токенизации в TTFT растёт с 10% до 64% по компонентным замерам. Модельную работу убрали, работу фронтенда не тронули, и она стала видна.
Дальше арифметика становится злой. Один пользовательский ход у агента разворачивается в 3 до 5 вызовов модели по медиане, 27 до 34 на P90 и 87 до 103 на P99, а автономный трейс даёт 30 вызовов на попытку. Токенизация платится на каждом шаге, поэтому линейный по длине токенизатор накапливает квадратичную работу на сессии, чей контекст только растёт. В деньгах: полная ре-токенизация на современном Rust-токенизаторе стоит 13,4 мс процессорного времени на запрос, что при 0,5 запроса в секунду на GPU читается как 6,7 фронтенд-ядер на каждую тысячу GPU.
Почему это нельзя было просто закешировать раньше: дописанные символы способны сдвинуть границы токенов в хвосте предыдущей последовательности. Наивное склеивание даёт другие token ID, а другие token ID означают другой ответ модели.
Как TokTier переиспользует токены
Через проверяемую склейку, а не через доверие. Сервис ставится между роутером запросов и движком модели, принимает текст и идентификатор модели, возвращает token ID и держит состояние живых сессий.
Контракт у него один и жёсткий: выданные token ID всегда совпадают с полной референсной токенизацией того же текста. Не «почти», не «в большинстве случаев».
Для продолжения сессии сервис хранит прошлую последовательность токенов, токенизирует заново небольшое окно вокруг дописанного куска, сопоставляет свежие и закешированные записи и склеивает только если проверка нашла стабильную границу пре-токенизации внутри совпавшего участка. В формальной части статьи такая граница называется сертификатом. Проверка не прошла: окно расширяется, и в пределе делается полная референсная токенизация.
Для вызовов без переиспользуемого префикса, то есть для тех редких, но огромных, сделан второй путь: regex пре-токенизация в стиле GPT разложена на локальные правила, и она вместе с BPE считается на GPU. Плюс поверх всего живой трафик выборочно перепроверяет теневой верификатор.
Проверяли это неприлично тщательно: 17 семейств продакшен-токенизаторов, 1,5 на 10 в десятой степени проверок разбиений, корпус реального текста на 12,4 ТБ, больше 93 тысяч переигранных шагов агентов. Ноль расхождений.
Сколько это даёт на практике
Инкрементальный ремонт занимает от 0,5 до 1,1 мс на контекстах от 100 тысяч до 3 миллионов символов. Это до 437 раз быстрее токенизатора Hugging Face и в 2,1 раза быстрее сильнейшего кеш-бейзлайна (Gigatoken) в полностью прогретом состоянии на миллионе символов. GPU-путь кодирует запрос на миллион символов за 0,87 мс: до 491 раза ниже HF и в 23,4 раза ниже самого быстрого опубликованного CPU-метода на том же протоколе.
Но интереснее сквозные цифры, потому что микробенчмарки токенизатора мало кого волнуют. С vLLM в контуре медианный TTFT падает на 16 до 34%, а P99 на 23% под записанными бурстами. На контекстах 100 тысяч символов с дописками по 400 символов и потоком 15 запросов в секунду медиана TTFT улучшается на 26%. На gpt-oss-120B с контекстами 350 тысяч символов (95 тысяч токенов) медиана падает на 32%, причём собственная доля сервиса в TTFT составляет 0,64 мс.
Ёмкость меняется резче всего. При целевом P99 в 50 мс четыре ядра под ремонт плюс один GPU держат 1821 запрос в секунду. Stateless-фронтенд на 16 ядрах на том же условии насыщается на 40 запросах в секунду.
Стенд: двухсокетный AMD EPYC 9115, 32 физических ядра с выключенным SMT, четыре RTX PRO 6000 Blackwell по 96 ГБ, vLLM 0.25 с включённым prefix caching.
Кому это вообще нужно
Тем, кто сам хостит модели под агентную нагрузку. Если ты дёргаешь чужой API, эта работа объясняет, почему у провайдера может тормозить первый токен на длинной сессии, но починить ты ничего не можешь.
Целевой профиль конкретный: длинные растущие транскрипты, много коротких дописок, высокое попадание в промпт-кеш, серверная сторона под твоим контролем. Это ровно то, что происходит, когда ты поднимаешь vLLM под внутренний кодинг-агент или под агентный пайплайн с длинными сессиями. Тема стыкуется с тем, что мы разбирали на verbosity DeepSeek V4 Flash и на счёте за агентный кодинг у Databricks: дешёвый токен и быстрый инференс перестают спасать, когда узкое место переехало в другое место конвейера.
Подводные камни
Пакета на PyPI фактически нет. Модуль toktier там опубликован как pre-release-заглушка с описанием «предстоящий correctness-first тулкит токенизации». Работать надо с репозиторием, а не с pip install.
Все цифры сняты на одном стенде авторами. Независимых воспроизведений на момент выхода нет, а конфигурация специфическая: четыре Blackwell по 96 ГБ и 32 ядра. Кеш-хит 94,1% это тоже флотовый показатель крупного трафика, а не то, что ты увидишь на одном сервере с тремя пользователями.
Есть режимы, где становится хуже. На необычно больших дописках P99 самого сервиса ремонта держится на 13 до 17 мс независимо от нагрузки, что выше их же цели в 10 мс, потому что роутер такие запросы никуда не перенаправляет. В одном бурст-режиме P90 ухудшился на 28%, и авторы честно разбирают причины: контеншен GIL в генераторе нагрузки плюс более когерентная форма бурста, доехавшая до планировщика.
Выигрыш зависит от числа одновременных сессий. В закрытом контуре при 95 тысячах токенов на сессию медиана улучшается на 23% на двух последовательных сессиях со свежим KV-кешем и на 16% на трёх. Больше сессий, меньше запаса у кеша движка.
GPU уходит на токенизацию. Схема «четыре ядра плюс один GPU» означает, что один ускоритель ты снимаешь с инференса. На маленькой инсталляции это может съесть весь смысл затеи.
Альтернативы
Prefix caching в vLLM без всего остального. Уже включено у большинства и решает модельную часть. Именно после его включения фронтенд и становится доминирующей статьёй, так что это не альтернатива, а предпосылка.
Отдавать prompt_token_ids самому. Токенизировать на своей стороне и передавать готовые ID: интерфейс vLLM это умеет, и TokTier сам его использует. Дёшево, но всю логику безопасной склейки тогда пишешь ты.
Gigatoken. Открытый высокопроизводительный движок и сильнейший кеш-бейзлайн в статье. Стабильность границ у него заявлена как инженерное допущение, а не как проверяемый на каждом запросе сертификат. Быстро, но без гарантии совпадения с референсом.
Провайдерское кеширование промптов. Если ты на чужом API, это всё, что тебе доступно: экономит деньги и часть латентности, но фронтенд провайдера остаётся его проблемой.
Вердикт
Ставь, если хостишь модели под агентную нагрузку с длинными сессиями и уже видишь, что первый токен появляется медленнее, чем объясняет инференс. Прирост в 16 до 34% медианного TTFT и сорокакратный рост ёмкости фронтенда стоят возни, а гарантия побитового совпадения с референсной токенизацией снимает главный страх такой оптимизации.
Не трогай, если у тебя короткие запросы без переиспользуемого префикса или ты просто клиент чужого API: экономить нечего, а один GPU под токенизатор придётся отдать. И перед раскаткой воспроизведи замеры на своём профиле трафика, потому что кеш-хит 94% это чужая нагрузка, а не твоя.
Как попробовать
- Сначала измерь, есть ли у тебя проблема: сравни TTFT, который отдаёт vLLM в своих метриках, с временем, которое запрос проводит во фронтенде. Если разрыв заметный, читай дальше.
- Посмотри своё попадание в промпт-кеш. Ниже 0,9 эффект будет скромнее того, что в статье: там весь выигрыш растёт вместе с кеш-хитом.
- Забери код из github.com/asu-idi/toktier (
pip install toktierдаст заглушку, не рабочий пакет). - Проверь, что твой токенизатор попадает в 17 проверенных семейств, и включи теневую верификацию на выборке живого трафика. Расхождение token ID это не замедление, это другой ответ модели.
- Если решишь читать статью, начинай со второго раздела про характеризацию трафика: там самое полезное, а вся склейка с теоремами лежит в приложении A.