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