Computer Vision Engineer: от детекции и Doc-AI до продакшнаВведение и быстрый стартМетрики CV простым языком

Метрики CV простым языком

Уроки курсаМетрики CV простым языком

Latency p50/p95 и throughput: что и как корректно мерить

Latency — время обработки одного запроса (inference одной картинки). Throughput — сколько запросов обработали за секунду. p50 — медиана, половина запросов быстрее. p95 — 95% запросов быстрее этого времени, остальные 5% медленнее. Смотрим на p95, а не на среднее — медленные запросы проседают пользовательский опыт. Замеры делаем после warmup, иначе первые итерации искажают картину.

Ключевые идеи

  • Latency: время одного inference, измеряем в миллисекундах (мс).
  • p50 (медиана): половина запросов быстрее. p95: 95% быстрее, отсекаем выбросы.
  • Throughput: запросов в секунду (QPS). Зависит от batch size и железа.
  • Warmup: первые несколько итераций медленные (загрузка модели, JIT), не считаем их.
  • Среднее время обманчиво — один медленный запрос поднимает среднее, но не заметен в p50.

Latency: p50 и p95

Latency — сколько миллисекунд от получения картинки до выдачи результата. Но одно число не показывает полную картину:

  • Среднее: сумма всех времён / количество. Проблема: один запрос 500 мс среди 99 по 50 мс даст среднее 54.5 мс — выглядит нормально, хотя 1% пользователей ждал полсекунды.
  • p50 (медиана): 50% запросов быстрее, 50% медленнее. Устойчиво к выбросам.
  • p95: 95% запросов быстрее. Показывает worst-case для большинства пользователей.
  • p99: ещё строже, но требует больше замеров для стабильности.

Пример: 100 запросов, времена от 45 до 120 мс. Сортируем по возрастанию:

  • p50 = 50-й запрос ≈ 52 мс
  • p95 = 95-й запрос ≈ 98 мс
  • Среднее ≈ 58 мс (выглядит нормально)
  • Максимум = 120 мс (5% пользователей).

Если p95 сильно отличается от p50 — есть проблемы (сборка мусора, конкуренция за GPU, холодный старт).

Throughput

Throughput — сколько изображений обработали за секунду. Формула:

Зависит от:

  • Batch size: обработка 16 картинок одновременно быстрее, чем 16 раз по одной.
  • GPU utilization: если GPU загружен на 50%, можно увеличить batch.
  • Параллелизм: несколько worker'ов обрабатывают запросы одновременно.

Пример: обработали 1000 картинок за 10 секунд → throughput = 100 QPS.

Пример замера

Код для измерения latency (p50/p95) и throughput:

import time
import numpy as np

def inference(image):
    """Заглушка: inference модели."""
    time.sleep(np.random.uniform(0.045, 0.055))  # 45-55 мс

def measure_performance(num_requests=1000, warmup=10):
    """Замеры latency и throughput."""
    latencies = []
    
    # Warmup: первые итерации не считаем
    print(f"Warmup: {warmup} запросов...")
    for _ in range(warmup):
        inference(None)
    
    # Основные замеры
    print(f"Измеряем {num_requests} запросов...")
    start_time = time.time()
    
    for _ in range(num_requests):
        start = time.time()
        inference(None)
        latency_ms = (time.time() - start) * 1000
        latencies.append(latency_ms)
    
    total_time = time.time() - start_time
    throughput = num_requests / total_time
    
    # Перцентили
    latencies_sorted = sorted(latencies)
    p50 = latencies_sorted[int(len(latencies) * 0.50)]
    p95 = latencies_sorted[int(len(latencies) * 0.95)]
    p99 = latencies_sorted[int(len(latencies) * 0.99)]
    
    print(f"p50 latency: {p50:.2f} ms")
    print(f"p95 latency: {p95:.2f} ms")
    print(f"p99 latency: {p99:.2f} ms")
    print(f"Throughput: {throughput:.2f} QPS")
    
    return latencies, throughput

# Запуск
measure_performance(num_requests=1000, warmup=10)

Вывод:

Warmup: 10 запросов...
Измеряем 1000 запросов...
p50 latency: 50.12 ms
p95 latency: 54.87 ms
p99 latency: 55.23 ms
Throughput: 19.95 QPS

Частые проблемы

  1. Нет warmup: первые 5-10 итераций медленные (загрузка модели в GPU, CUDA kernels, JIT-компиляция). Без warmup p95 завышен. Всегда делайте 10-20 warmup итераций.
  2. Мало запросов: 10 замеров — p95 нестабилен. Нужно минимум 100, лучше 1000 для надёжного p95.
  3. Фоновые процессы: другие приложения грузят GPU/CPU — латентность скачет. Закройте лишнее или используйте изолированное окружение.
  4. Batch size = 1 на GPU: неэффективно, GPU недогружен. Для throughput используйте batch 16-32, для latency — batch 1 (чтобы измерить реальное время одного запроса).
  5. Сравнение среднего вместо p95: среднее скрывает проблемы. Модель А: среднее 50 мс, p95 100 мс. Модель Б: среднее 55 мс, p95 60 мс. Модель Б лучше для продакшена.

Как мерить правильно

  1. Warmup: 10-20 итераций перед началом замеров.
  2. Достаточно запросов: минимум 100, лучше 1000 для стабильных перцентилей.
  3. Изолированное окружение: закройте другие приложения, используйте одну модель на GPU.
  4. Фиксированный batch: для latency batch=1, для throughput batch=16-32.
  5. Повторите несколько раз: один запуск может быть аномальным. Сделайте 3-5 прогонов, усредните результаты.

Связанные понятия

  • Batch inference: обработка нескольких картинок одновременно. Увеличивает throughput, но повышает latency одного запроса.
  • Model optimization: TensorRT, ONNX Runtime, quantization (INT8) ускоряют inference.
  • Profiling: инструменты (NVIDIA Nsight, PyTorch Profiler) показывают, где модель тормозит.

Итоги

  • Latency: время одного inference. Смотрим p50 (медиана) и p95 (worst-case для 95%).
  • Throughput: запросов в секунду. Зависит от batch size и параллелизма.
  • Среднее время обманчиво — используйте перцентили.
  • Warmup обязателен: первые итерации медленные, не считаем их.
  • Для стабильных замеров нужно ≥100 запросов в изолированном окружении.