中文翻译暂不可用,显示俄语原文。

Как вы измеряете TTFT (Time To First Token) и TPOT (Time Per Output Token)?

Краткий тезис

TTFT — время до первого токена, критично для ощущения отзывчивости; TPOT — среднее время на каждый последующий токен, определяет общую задержку генерации. Обе метрики измеряют на стороне инференса, разделяя фазы prefill и decode. Для RAG-систем TTFT становится особенно важным, так как включает время retrieval, а TPOT влияет на восприятие скорости ответа при стриминге. Измеряют с помощью p50/p95/p99 перцентилей, используя инструменты логирования (например, Langfuse, OpenTelemetry) и встроенные хуки в LLM-серверах (vLLM, TGI).


1. Термины: TTFT, TPOT, prefill и decode

  • TTFT (Time To First Token) — промежуток от момента получения системой полного запроса пользователя до момента выдачи первого сгенерированного токена. Включает фазу prefill (параллельная обработка промпта) и, возможно, время на retrieval в RAG.
  • TPOT (Time Per Output Token) — среднее время, затрачиваемое моделью на генерацию одного токена после первого. Относится к фазе decode (последовательная авторегрессия).
  • Prefill — этап, когда модель обрабатывает входной текст (промпт) целиком, вычисляя key-value cache для всех его токенов параллельно. Вычислительно интенсивен, зависит от длины промпта.
  • Decode — этап, когда модель генерирует токены один за другим, используя ранее вычисленный KV cache. Время decode почти не зависит от длины промпта, но сильно зависит от размера модели и количества генерируемых токенов.
ФазаХарактер вычисленийЗависимость от длины промптаВлияние на TTFT/TPOT
PrefillПараллельныйВысокая (≈ O(prompt_len))TTFT (вся фаза)
Decode (первый токен уже на выходе prefill)ПоследовательныйНизкаяTPOT (для каждого токена)

Фактически TTFT ≈ время prefill + latency сетевого вызова, а TPOT ≈ среднее время decode.


2. Почему TTFT и TPOT измеряют раздельно?

  • Разная природа задержек: prefillmemory-bound для коротких промптов и compute-bound для длинных; decode — всегда memory-bound из-за загрузки KV cache.
  • Влияние на UX: TTFT отвечает за первое впечатление («система начала отвечать?»), TPOT — за плавность стриминга (токены появляются с комфортной скоростью).
  • Оптимизации: методы вроде chunked prefill уменьшают TTFT, а speculative decodingTPOT. Раздельное измерение помогает выбирать правильный рычаг.

В RAG-системе TTFT дополнительно включает вызов retrieval (поиск в векторной БД, переранжирование). Поэтому измерять TTFT нужно как сквозное время от отправки запроса пользователем до первого токена ответа LLM, включая все этапы.


3. Как измерить TTFT

Базовый подход — отметка времени на каждом этапе пайплайна:

  1. Клиент отправляет запрос → фиксируем t_start.
  2. Система получает запрос, выполняет retrieval, формирует промпт, отдаёт LLM.
  3. Модель начинает генерацию → фиксируем t_first_token.
  4. TTFT = t_first_token - t_start.

Метрику обычно агрегируют по всем запросам, считая перцентили.

Пример на Python (псевдокод)

import time
import asyncio
from statistics import median, quantiles

ttft_values = []

async def handle_request(prompt: str):
    t_start = time.monotonic()
    # Здесь вызов RAG (retrieve + rerank) и формирование полного промпта
    # ...
    # Генерация первого токена (например, через streaming)
    async for token in llm.generate_stream(prompt):
        t_first = time.monotonic()
        ttft = t_first - t_start
        ttft_values.append(ttft)
        break  # после первого токена выходим
    # продолжаем генерацию остальных токенов для TPOT
    # ...

# После накопления данных
p50 = median(ttft_values)
p95 = quantiles(ttft_values, n=20)[18]  # для 95-го перцентиля
p99 = quantiles(ttft_values, n=100)[98]
print(f"TTFT p50: {p50*1000:.1f}ms, p95: {p95*1000:.1f}ms, p99: {p99*1000:.1f}ms")

Важно: для точности используйте time.monotonic() (не time.time()) во избежание скачков системного времени.


4. Как измерить TPOT

TPOT считаем после первого токена: замеряем полное время генерации, вычитаем TTFT, делим на количество последующих токенов.

Формула

TPOT = (total_time - TTFT) / (num_tokens - 1)

Где total_time — время от t_start до получения конца последовательности (EOS), num_tokens — общее число токенов в ответе (включая первый).

Пример измерения:

async def measure_latency(prompt: str):
    t_start = time.monotonic()
    tokens = []
    async for token in llm.generate_stream(prompt):
        tokens.append(token)
        if len(tokens) == 1:
            t_first = time.monotonic()
            ttft = t_first - t_start
    t_end = time.monotonic()
    total_time = t_end - t_start
    num_tokens = len(tokens)
    if num_tokens > 1:
        tpot = (total_time - ttft) / (num_tokens - 1)
    else:
        tpot = None  # только один токен (редкий случай)
    return ttft, tpot

Особенность: TPOT может неравномерно распределяться: первые несколько токенов могут быть медленнее из-за прогрева decode. Поэтому часто считают медианный TPOT на каждом шаге, а не средний.


5. Распределения: p50, p95, p99

Метрики latency имеют тяжёлый хвост — редкие, но очень долгие запросы. Использовать только среднее бессмысленно.

  • p50 — типичное время для большинства пользователей.
  • p95 — худший сценарий для 5% запросов; обычно целевая метрика оптимизации.
  • p99 — события-выбросы (например, при перегрузке сервера или длинном промпте).
ПерцентильИнтерпретацияЦелевое значение для TTFTЦелевое значение для TPOT (модель 7B)
p50«нормальный» пользователь< 500ms< 30ms
p95порог терпения< 2s< 100ms
p99аномалии (алерт)< 5s< 200ms

В реальных RAG-системах TTFT p95 часто превышает 2 секунды из-за retrieval, поэтому отдельно мониторят TTFT без retrieval (чисто LLM) и TTFT с retrieval.


6. Инструменты для измерения

  • vLLM / TGI — встроенные метрики (например, time_to_first_token_ms в выхлопе).
  • Langfuse — logging latency с трейсингом шагов (retrievalgeneration).
  • OpenTelemetry — распределённая трассировка для microservices RAG.
  • Ваш код — кастомные декораторы и middleware (как в примере выше).
  • Prometheus + Grafana — для дашбордов p50/p95/p99 в реальном времени.

Пример конфигурации для vLLM (параметр --log-stats):

vllm serve meta-llama/Llama-2-7b-chat-hf --enable-lora --log-stats

Статистика выводится в stdout, её можно парсить и отправлять в систему мониторинга.


7. Оптимизация TTFT: chunked prefill и кэширование

  • Chunked prefill — разбивка длинного промпта на части и параллельный запуск prefill для каждой; позволяет уменьшить задержку до первого токена, но может увеличить TPOT. Применяется в TensorRT-LLM и vLLM (грядёт).
  • KV cache reuse — для повторяющихся промптов (системный контекст) кэш сохраняется между запросами, сокращая prefill. Актуально в Agentic RAG, где один и тот же system prompt используется многократно.
  • Сокращение длины промпта — более агрессивный chunking или ретривер с высоким hit rate (меньше документов в контексте).

8. Оптимизация TPOT: speculative decoding и flash attention

  • Speculative decoding — генерация «черновика» из нескольких токентов маленькой моделью, затем верификация большой моделью. Позволяет увеличить throughput и уменьшить воспринимаемый TPOT (параллелизация нескольких decode-шагов).
  • Flash Attention — эффективная реализация attention, уменьшающая время decode за счёт оптимизации memory access (IO-aware).
  • PagedAttention / vLLM — эффективное управление KV cache, снижающее latency decode (особенно при concurrent запросах).
  • Quantisation (FP16INT8/INT4) — уменьшает размер модели и bandwidth на decode, но может влиять на качество.

9. Компромиссы: TTFT vs TPOT и GPU utilization

Иногда оптимизация одной метрики ухудшает другую:

ОптимизацияВлияние на TTFTВлияние на TPOTКомментарий
Chunked prefillУменьшаетМожет растиPrefill конкурирует за ресурсы с decode
Увеличение batch sizeУвеличиваетУменьшаетMore queuing, better throughput per token
Speculative decodingНезначительноСильно уменьшаетПотенциально увеличивает TTFT из-за overhead
Более мощная модельУвеличиваетУвеличиваетTrade-off качество vs скорость

В Agentic RAG (многошаговые агенты, вызовы инструментов) каждый шаг имеет свой TTFT и TPOT. Общая воспринимаемая задержка складывается из последовательности TTFT+TPOT×num_tokens на шаг.


10. Влияние на RAG-системы

  • TTFT критически важен для интерактивных приложений (чат-боты, где пользователь ждёт первое слово).
  • TPOT важен для стриминга: если TPOT > 50-100ms, токены появляются рывками, ухудшая UX.
  • Горячий старт / кэширование: первый запрос к RAG (холодный старт) имеет большой TTFT из-за загрузки модели и построения retrieval индекса. Измеряйте отдельно cold TTFT и warm TTFT.
  • Пайплайны с горутинами: измеряйте сквозной TTFT от клиента до первого токена, включая сетевые задержки, ретраи retrieval и т.д.

11. Пет-проект для закрепления

Задача: Написать простой LLM-сервер на FastAPI с использованием transformers, который логирует TTFT и TPOT для каждого запроса, а затем выводит статистику p50/p95/p99.

Инструменты: Python 3.10+, FastAPI, transformers (или openai-клиент), numpy, matplotlib.

Шаги:

  1. Реализуйте эндпоинт /generate, который принимает {"prompt": "..."} и возвращает ответ через streaming (SSE).
  2. Внутри замеряйте:
    • t_start = time.monotonic() при получении запроса.
    • t_first после первого токена (флаг is_first_token).
    • t_end после отправки последнего токена.
    • Сохраняйте ttft, total_time, num_tokens.
  3. После 1000 запросов (можно имитировать набором промптов) постройте гистограммы TTFT и распределение TPOT.
  4. Добавьте логирование для каждого запроса в CSV.
  5. Экспериментируйте с разными hyperparameters (max_new_tokens, temperature), чтобы увидеть влияние на TPOT.

Ожидаемый результат: Дашборд (matplotlib/plotly) с перцентилями и гистограммами. Вы узнаете, как ведёт себя модель под нагрузкой, и на что влияет длина промпта.


12. Связь с другими вопросами

ВопросТема
7Как вы уменьшаете latency RAG-системы?
443Что такое TTLT (Time To Last Token) и как его считать?
444Какие стратегии batching применяются при инференсе?
446Что такое speculative decoding и как он уменьшает TPOT?
447Что такое chunked prefill и когда его применять?

Навигация