Modern QA2026Метрики производительности LLM
Join

Course05 Performance & Chaos Engineering

Cutting-edge · Chapter 05

Метрики производительности LLM

Updated Jul 2026

Почему производительность LLM отличается

Функции на базе LLM вводят характеристики производительности, которые традиционное нагрузочное тестирование не учитывает. В отличие от REST API, который возвращает полный ответ за один обмен данными, эндпоинты LLM имеют переменное время ответа, ограничения пропускной способности на основе токенов, задержку холодного старта и лимиты запросов со стороны провайдера. QA-архитектор, применяющий традиционные перцентили задержки к эндпоинтам LLM, упустит метрики, которые действительно важны для пользователей.

Ключевые метрики производительности LLM

Основные метрики

Метрика Описание Типичный диапазон Почему это важно
Time to First Token (TTFT) Задержка до генерации первого токена 200 мс - 5 с Воспринимаемая отзывчивость — пользователи замечают, когда начинается стриминг
Tokens per Second (TPS) Пропускная способность генерации после первого токена 30-100 ток/с Пользовательский опыт при стриминге ответов
Общее время генерации Сквозное время, включая все токены 1-60 с Планирование таймаутов запросов и определение SLO
Задержка холодного старта Первый запрос после периода простоя (serverless) 5-30 с Планирование serverless-деплоя
Запас по лимиту запросов Расстояние до лимита провайдера (токенов/мин или запросов/мин) Зависит от тарифа Ёмкость обработки пиковых нагрузок
Утилизация контекстного окна Соотношение токенов промпта и завершения 10-100% Корреляция стоимости и задержки

Вторичные метрики

Метрика Описание Почему это важно
Соблюдение бюджета токенов Процент ответов в пределах max_tokens Предотвращает неконтролируемый рост затрат
Частота повторных попыток Процент запросов, требующих повтора (429, 500, таймаут) Надёжность сервиса
Частота переключения на резервного провайдера Как часто система переключается на вторичного провайдера Стабильность основного провайдера
Попадание в кеш Процент запросов, обслуженных из семантического кеша Оптимизация затрат и задержки
Частота обрыва стриминга Процент потоков, отключённых до завершения Надёжность сети

Понимание TTFT и общего времени генерации

Request sent                      Response complete
    |                                    |
    |--[TTFT]-->|                        |
    |           |---[Token streaming]--->|
    |           |                        |
    |           First token              Last token
    |                                    |
    |--------[Total Generation Time]---->|

TTFT определяет воспринимаемую отзывчивость. Пользователь, смотрящий на пустой экран 3 секунды, воспринимает систему как медленную, даже если полный ответ приходит за 5 секунд. При стриминге TTFT в 500 мс с последующими 4,5 секундами потоковой выдачи токенов воспринимается гораздо быстрее, чем 5-секундное ожидание полного ответа.

Практическое правило:

  • TTFT < 1 с: пользователи воспринимают ответ как «мгновенный»
  • TTFT 1-3 с: приемлемо с индикатором загрузки
  • TTFT > 3 с: пользователи начинают терять внимание

Измерение метрик LLM

Измерение без стриминга

Для эндпоинтов без стриминга вы можете напрямую измерить только общее время генерации. TTFT приходится оценивать:

# llm_metrics_collector.py
import time
import json
from dataclasses import dataclass

@dataclass
class LLMMetrics:
    ttft_ms: float
    total_generation_ms: float
    tokens_per_second: float
    prompt_tokens: int
    completion_tokens: int
    total_tokens: int
    model: str

def measure_non_streaming(client, prompt: str, model: str = "gpt-4o") -> LLMMetrics:
    """Measure LLM performance for a non-streaming request."""
    start = time.perf_counter()

    response = client.chat.completions.create(
        model=model,
        messages=[{"role": "user", "content": prompt}],
        max_tokens=256,
        stream=False,
    )

    total_ms = (time.perf_counter() - start) * 1000
    completion_tokens = response.usage.completion_tokens
    tps = completion_tokens / (total_ms / 1000) if total_ms > 0 else 0

    return LLMMetrics(
        ttft_ms=total_ms * 0.15,  # heuristic: ~15% of total time is prefill
        total_generation_ms=total_ms,
        tokens_per_second=tps,
        prompt_tokens=response.usage.prompt_tokens,
        completion_tokens=completion_tokens,
        total_tokens=response.usage.total_tokens,
        model=model,
    )

Измерение со стримингом (точный TTFT)

Для точного TTFT необходимо использовать потоковый API:

import time

def measure_streaming(client, prompt: str, model: str = "gpt-4o") -> LLMMetrics:
    """Measure LLM performance for a streaming request with accurate TTFT."""
    start = time.perf_counter()
    ttft = None
    token_count = 0
    full_response = ""

    stream = client.chat.completions.create(
        model=model,
        messages=[{"role": "user", "content": prompt}],
        max_tokens=256,
        stream=True,
        stream_options={"include_usage": True},
    )

    for chunk in stream:
        if chunk.choices and chunk.choices[0].delta.content:
            if ttft is None:
                ttft = (time.perf_counter() - start) * 1000
            full_response += chunk.choices[0].delta.content
            token_count += 1

        # Usage is in the final chunk when stream_options is set
        if hasattr(chunk, 'usage') and chunk.usage:
            prompt_tokens = chunk.usage.prompt_tokens
            completion_tokens = chunk.usage.completion_tokens

    total_ms = (time.perf_counter() - start) * 1000
    generation_time = total_ms - (ttft or 0)
    tps = completion_tokens / (generation_time / 1000) if generation_time > 0 else 0

    return LLMMetrics(
        ttft_ms=ttft or total_ms,
        total_generation_ms=total_ms,
        tokens_per_second=tps,
        prompt_tokens=prompt_tokens,
        completion_tokens=completion_tokens,
        total_tokens=prompt_tokens + completion_tokens,
        model=model,
    )

Экономика контекстного окна

Размер промпта напрямую влияет на задержку и стоимость. Понимание этой взаимосвязи критически важно для оптимизации производительности:

Использование контекста Типичное влияние на TTFT Влияние на стоимость Оптимизация
< 1K токенов Базовая линия Базовая линия Не требуется
1-4K токенов +100-300 мс 2-4x Суммаризация контекста
4-16K токенов +300 мс - 1 с 4-16x RAG с фильтрацией по релевантности
16-64K токенов +1-5 с 16-64x Агрессивная обрезка контекста
64-128K токенов +5-15 с 64-128x Перепроектирование подхода

Практические стратегии оптимизации:

  1. Кеширование промптов. Многие провайдеры кешируют префикс повторяющихся промптов, уменьшая TTFT для последующих запросов с одинаковым системным промптом.
  2. Обрезка контекста. Удаляйте нерелевантную историю разговора перед отправкой в модель.
  3. Семантическое кеширование. Кешируйте ответы для семантически похожих запросов, чтобы полностью избежать вызовов LLM.
  4. Маршрутизация моделей. Отправляйте простые запросы на меньшие, более быстрые модели. Резервируйте большие модели для сложных задач.

Установка SLO для функций LLM

SLO для LLM следует определять отдельно от традиционных SLO для API:

# llm-slo-definition.yaml
service: ai-chatbot
llm_slos:
  - name: streaming_responsiveness
    metric: time_to_first_token
    target: p95 < 2000ms
    window: 7d

  - name: generation_throughput
    metric: tokens_per_second
    target: avg > 40 tok/s
    window: 7d

  - name: completion_time
    metric: total_generation_time
    target: p95 < 10000ms
    window: 7d

  - name: availability
    metric: successful_requests / total_requests
    target: 99.5%
    window: 30d
    # Note: lower than typical API SLOs because LLM providers
    # have higher baseline error rates

  - name: rate_limit_headroom
    metric: requests_used / rate_limit
    target: peak < 80%
    window: 1d
    alert_at: 70%

Бенчмаркинг разных провайдеров

При оценке провайдеров LLM запускайте стандартизированные бенчмарки:

# llm_benchmark.py
BENCHMARK_PROMPTS = [
    {"name": "short_qa", "prompt": "What is 2+2?", "expected_tokens": 10},
    {"name": "medium_summary", "prompt": "Summarize the key differences between REST and GraphQL in 3 sentences.", "expected_tokens": 80},
    {"name": "long_generation", "prompt": "Write a Python function that implements binary search with detailed docstring.", "expected_tokens": 200},
]

def benchmark_provider(client, model: str, runs: int = 10) -> dict:
    """Benchmark a provider/model combination."""
    results = {}
    for prompt_config in BENCHMARK_PROMPTS:
        metrics = []
        for _ in range(runs):
            m = measure_streaming(client, prompt_config["prompt"], model)
            metrics.append(m)

        results[prompt_config["name"]] = {
            "ttft_p50": sorted([m.ttft_ms for m in metrics])[len(metrics)//2],
            "ttft_p95": sorted([m.ttft_ms for m in metrics])[int(len(metrics)*0.95)],
            "tps_avg": sum(m.tokens_per_second for m in metrics) / len(metrics),
            "total_p50": sorted([m.total_generation_ms for m in metrics])[len(metrics)//2],
        }
    return results

Эти данные определяют выбор провайдера, приоритизацию резервных вариантов и калибровку SLO. Запускайте бенчмарки еженедельно для отслеживания трендов производительности провайдеров.