Вы смотрите на сухую цифру — 45 tokens/s. Умом понимаете: быстро. Но чувствуете ли вы эту скорость? Ровно то же самое, что читать технические характеристики автомобиля без тест-драйва. Цифры обманывают. Особенно когда речь о реальном времени ожидания, а не пиковых метриках из бенчмарков.
Я долго искал способ перевести абстрактные токены в ощутимый опыт. Перепробовал десятки утилит, встроенные счётчики в llama.cpp, LM Studio, Ollama. Но все они показывали одно и то же — голые числа. Пока не написал крошечный скрипт, который делает одну простую вещь: показывает генерацию токенов как живую анимацию.
Идея: вместо абстрактного «45 t/s» вы видите, как токены буквально вылетают на экран один за другим. Чем быстрее — тем плотнее поток. Это меняет восприятие навсегда.
Почему 45 tokens/s — это не 45
В прошлой статье мы разбирали, как UI-бенчмарки рисуют красивые графики, но в реальной работе скорость падает в разы. Эффективные токены в секунду — то, что вы чувствуете спиной, — часто вдвое-втрое ниже заявленных пиковых значений. И вот тут визуализация становится спасительным кругом.
Скрипт, о котором речь, не считает prefill и не замеряет сетевые задержки. Он берёт сырой стрим токенов от любого LLM-бэкенда (через API, subprocess или stdout) и выводит их на экран с фиксацией времени появления каждого токена. В итоге вы видите не просто число, а динамику: первые токены появляются медленно (фаза «разогрева»), потом поток ускоряется, а к концу может замедляться из-за заполнения контекста.
Как выглядит визуализация
Представьте, что вы запускаете модель, и в консоли начинают всплывать слова с разной скоростью. Каждое новое слово — это токен. Скрипт окрашивает их в зависимости от интервала: зелёные — быстрые (менее 20 мс между токенами), жёлтые — средние, красные — тормоза (более 100 мс). Внизу бегущая строка показывает средний t/s за последнюю секунду. Это как спидометр, только для нейросети.
# Пример запуска (скрипт читает stdin)
curl -s http://localhost:8080/v1/chat/completions \
-d '{"model":"llama-4-8b","messages":[{"role":"user","content":"Расскажи про квантование"}],"stream":true}' \
| python3 visualize_tokens.py
Скрипт: код на 50 строк
Полная версия лежит в моём репозитории, а вот ядро — 30 строк на Python. Без зависимостей, только стандартная библиотека.
#!/usr/bin/env python3
"""Визуализация скорости генерации токенов в реальном времени."""
import sys
import time
from datetime import datetime
def colored(text, color):
colors = {'green': '\033[92m', 'yellow': '\033[93m', 'red': '\033[91m'}
return f"{colors.get(color, '')}{text}\033[0m"
def main():
last_time = None
window = []
for line in sys.stdin:
token = line.strip()
now = time.time()
if last_time:
interval = now - last_time
window.append(interval)
if len(window) > 50:
window.pop(0)
avg_interval = sum(window) / len(window)
tps = 1.0 / avg_interval if avg_interval > 0 else 0
# Цвет в зависимости от задержки
if interval < 0.02:
color = 'green'
elif interval < 0.1:
color = 'yellow'
else:
color = 'red'
sys.stdout.write(colored(token + ' ', color))
sys.stdout.flush()
# Вывод текущего t/s поверх
print(f"\r\033[K➡ {tps:.1f} t/s", end='', file=sys.stderr)
else:
sys.stdout.write(token + ' ')
sys.stdout.flush()
last_time = now
if __name__ == '__main__':
main()
Важно: скрипт измеряет чистое время между токенами. Если модель выдаёт сразу пачку токенов (например, через буферизацию), интервал может быть обманчиво маленьким. Для точности используйте поток с максимальным batching=1 или добавьте свою логику усреднения.
Сравнение с альтернативами
| Инструмент | Что показывает | Субъективность | Реальное время |
|---|---|---|---|
| Встроенные счётчики (llama.cpp, Ollama) | Среднее t/s за весь запрос | Нет | Пиковое |
| Бенчмарки (по типу pp/tg) | Только генерация, без prefill | Нет | Идеальные условия |
| Наш скрипт | Динамика t/s, цветовые интервалы | Да | Живой стрим |
Разница колоссальная. Когда вы видите, как токены вылетают красным на длинных промптах, вы сразу понимаете, что Q4_K_M на вашей видеокарте — не лучший выбор. Или что SGLang с его продвинутым планировщиком даёт ровный зелёный поток, в отличие от vLLM, где периодически возникают жёлтые всплески.
Два сценария, где визуализация спасает
1 Сравнение квантований
Вы мучаетесь выбором между Q4_K_M и Q5_K_M для своей 8B-модели. Цифры обычно отличаются на 10-15%. А субъективно? Запустите скрипт на обоих — и увидите, что Q5_K_M на коротких ответах (до 200 токенов) почти не заметен, а на длинных генерациях начинает «краснеть» чаще. Решение: берите Q4_K_M, если не пишете романы.
2 Проверка провайдера API
Вам обещают «молниеносный» API. Вы подключаете скрипт к стриму и видите: первые 5 токенов зелёные, потом 2 секунды тишины, потом снова зелёные. Это не генерация — это буферизация ответа на сервере. Честный провайдер отдаёт токены равномерно, а «молниеносность» часто достигается накоплением пачки и одномоментным выплёскиванием. Визуализация мгновенно подсвечивает такие фокусы.
Кому это реально нужно
- Инженерам, выбирающим модель для прода. Вы не можете позволить себе «быстро на бумаге, медленно в бою». Визуализация — ваш тест-драйв.
- Энтузиастам локальных LLM. Вы гоняетесь за каждой десятой t/s? Увидьте, насколько это заметно глазу. Часто лишняя оптимизация не стоит свеч.
- Тем, кто сравнивает разные рантаймы. Самописный vLLM может давать 90 t/s в бенчмарке, но с этим скриптом вы заметите микро-лаги, которых нет у llama.cpp.
В конечном счёте, скорость генерации — это не цифра, это ритм. Как пульс. И пока вы не увидите этот ритм своими глазами, вы будете принимать решения по фальшивым кардиограммам. Скрипт — дешёвый и сердитый способ наконец-то понять, что на самом деле происходит под капотом вашей модели.
А если вы всё ещё думаете, что 5 tokens/s — это медленно, запустите визуализацию на Claude Code с его ультра-длинным контекстом. Вдруг окажется, что субъективно это быстрее, чем «быстрый» Llama-4 Scout с его иллюзией миллиона токенов?