Автомасштабирование LLM-инференса: какие метрики работают, а какие врут
Настроили автомасштабирование по загрузке GPU, а пользователи жалуются на задержки? GPU загружен на 60%, а время ответа выросло до 15 секунд. Разбираем 8 метрик для LLM-инференса, настройку окон масштабирования и бюджет холодного старта — с цифрами из реального эксперимента.
Вы настроили автомасштабирование по загрузке GPU, но пользователи жалуются на задержки. Видеокарта загружена на 60%, а время до первого токена (TTFT) выросло с 200 миллисекунд до 15 секунд. Знакомая ситуация? Проблема в том, что классические метрики не видят реальной нагрузки при инференсе больших языковых моделей. Движок с непрерывным батчингом эффективно утилизирует GPU, маскируя растущую очередь запросов. Решение - перейти на инференс-специфичные метрики, где главный инструмент для автомасштабирования - inflight_requests (количество запросов в обработке).
Эта статья построена на данных эксперимента: одну и ту же нагрузку пропустили через три политики масштабирования. Сработала только одна - на основе inflight_requests. Политики по TTFT и загрузке GPU провалились: они не заметили перегрузки, пока пользователи ждали ответа по 15 секунд. Разберём восемь метрик для инференса, настроим окна масштабирования без осцилляций и посчитаем бюджет холодного старта - 86 секунд для базовой модели 9B и 145 секунд для кастомной с весами 18 ГБ.
Почему классические метрики подводят при масштабировании LLM
GPU загружен на 60%, очередь запросов растёт, а TTFT увеличивается с 200 миллисекунд до 15 секунд. Пользователи ждут ответа, автомасштабирование молчит. Причина - непрерывный батчинг. Движок инференса собирает запросы в батчи динамически: пока один запрос генерирует токены, другие добавляются в обработку без перерыва. GPU всегда занят, даже когда система перегружена.
Классические метрики - загрузка CPU, RAM, GPU utilization - измеряют потребление ресурсов, а не качество обслуживания. GPU utilization 60% не означает «есть запас». Это означает «движок успевает обрабатывать текущий батч, но очередь уже копится». Загрузка GPU остаётся стабильной, а задержка для пользователя растёт экспоненциально. Система мониторинга показывает зелёный статус, клиенты пишут в поддержку.
Та же проблема с TTFT и сквозной задержкой (e2e_latency). Эти метрики реагируют, когда пользователь уже страдает. Они фиксируют факт перегрузки, но не предупреждают о ней. Для автомасштабирования нужны опережающие индикаторы - метрики, которые сигнализируют о проблеме до того, как вырастут задержки. Подробнее об архитектуре инференса, где endpoint, deployment и config разделены для гибкого управления, читайте в разборе архитектуры Together AI.
8 метрик, которые действительно отражают нагрузку на LLM-инференс
Метрики для LLM-инференса делятся на три группы: ведущие (опережают проблемы), запаздывающие (констатируют факт) и метрики эффективности (отражают использование ресурсов). Для автомасштабирования критичны ведущие метрики - они дают время добавить реплику до того, как пользователь заметит задержку.
Ведущие метрики: видим проблему до того, как она ударит по пользователю
inflight_requests - количество запросов, которые находятся в обработке в данный момент. Это главная метрика для автомасштабирования LLM-инференса. Когда число активных запросов приближается к пределу пропускной способности реплики, система должна добавить новую. Рост inflight_requests предшествует ухудшению TTFT: очередь растёт сначала внутри движка, и только потом пользователь чувствует задержку.
Порог для масштабирования подбирается под модель и оборудование. Для модели 7B на одном GPU A100 порог может составлять 8-12 одновременных запросов. Для модели 70B на восьми GPU - 16-24 запроса. Точное значение определяется нагрузочным тестированием: подаёте трафик, фиксируете момент роста TTFT, смотрите на inflight_requests в этот момент. Это значение минус 20% - ваш порог для scale up.
Размер очереди (queue depth) - число запросов, ожидающих обработки. Пока очередь пуста, система справляется. Как только очередь начинает расти - это сигнал, что текущих реплик недостаточно. Размер очереди часто коррелирует с inflight_requests, но измеряется на уровне балансировщика, а не движка инференса. Полезно отслеживать обе метрики: inflight_requests показывает нагрузку внутри реплики, queue depth - нагрузку перед ней.
Запаздывающие метрики: когда пользователь уже чувствует боль
TTFT (Time To First Token) - время от отправки запроса до получения первого токена ответа. Для интерактивных приложений (чат-боты, автодополнение) критично держать TTFT ниже 200-500 миллисекунд. Рост TTFT до 2-5 секунд означает, что система перегружена. Проблема: TTFT растёт, когда очередь уже сформировалась. Масштабирование по TTFT всегда запаздывает - реплика добавляется, когда пользователи уже ждут.
e2e_latency (сквозная задержка) - полное время от запроса до последнего токена ответа. Зависит от длины ответа и количества токенов. Для summarization-задач допустима задержка в 5-10 секунд, для чат-ботов - 1-3 секунды. Как и TTFT, e2e_latency - запаздывающая метрика. Она подтверждает проблему, но не предотвращает её.
В эксперименте политика на основе TTFT не сработала: движок с непрерывным батчингом маскировал рост очереди, TTFT оставался в пределах 200 миллисекунд до момента, когда очередь резко переполнялась. Затем TTFT скакал до 15 секунд за один шаг. Система добавляла реплику, но пользователи уже получили плохой опыт.
throughput (пропускная способность) - количество токенов в секунду, которые генерирует система. Полезен для планирования мощностей, но как триггер масштабирования работает плохо. Throughput может оставаться стабильным при росте очереди: движок продолжает генерировать токены с той же скоростью, просто каждый запрос ждёт дольше.
Метрики эффективности: почему GPU utilization не спасёт
GPU utilization - процент загрузки видеокарты. Непрерывный батчинг поддерживает высокую утилизацию даже при перегрузке. GPU всегда занят обработкой токенов из текущего батча. Когда приходит новый запрос, он добавляется в батч, и GPU продолжает работать. Загрузка GPU не меняется, очередь растёт.
В эксперименте политика на основе GPU utilization с порогом 80% не увидела перегрузки. GPU был загружен на 55-65% всё время теста, включая моменты, когда TTFT превышал 10 секунд. Причина: модель упиралась в пропускную способность памяти (memory bandwidth), а не в вычислительные ядра GPU. Утилизация вычислительных ядер оставалась низкой, хотя память была насыщена.
batch size (размер батча) - количество запросов, обрабатываемых одновременно. Растущий batch size при стабильном throughput - признак перегрузки: движок пытается компенсировать нагрузку, увеличивая батч. Но batch size редко доступен как метрика снаружи движка и неудобен для настройки порогов автомасштабирования.
Если вы только начинаете разбираться в инфраструктуре для ИИ, рекомендуем исследование о том, почему 83% компаний используют GPU менее чем наполовину. Оно объясняет, как «вычислительный разрыв» съедает бюджеты.
Эксперимент: одна нагрузка, три политики - и только одна сработала
Условия эксперимента: одна реплика модели 7B, непрерывный батчинг, ступенчатое увеличение нагрузки от 5 до 50 одновременных пользователей. Три политики автомасштабирования с одинаковым целевым TTFT - 300 миллисекунд.
Политика на основе inflight_requests с порогом 10 запросов. При достижении порога система добавляла реплику. Результат: добавлено 4 реплики, TTFT держался в пределах 180-320 миллисекунд на всём тесте. Масштабирование произошло до того, как пользователи заметили задержку.
Политика на основе TTFT с порогом 500 миллисекунд. Система ждала ухудшения метрики. Результат: TTFT скакал до 15 секунд, добавлено 2 реплики с опозданием в 3-5 минут. Пользователи получили деградацию сервиса.
Политика на основе GPU utilization с порогом 80%. GPU был загружен на 55-65% весь тест. Порог не достигнут ни разу. Результат: 0 добавленных реплик, TTFT вырос до 18 секунд. Система не заметила перегрузки.
Вывод: inflight_requests - единственная метрика, которая предсказывает перегрузку до роста задержек. TTFT и GPU utilization реагируют постфактум или не реагируют вовсе. Причина - архитектура непрерывного батчинга, которая скрывает перегрузку от классических метрик.
Настройка окон масштабирования: как не попасть в ловушку осцилляций
Быстрое уменьшение масштаба создаёт осцилляции: нагрузка чуть спала - реплика удалилась, нагрузка вернулась - реплика запустилась заново. Каждый запуск - это холодный старт, который длится минуты. В это время оставшиеся реплики перегружены, задержки растут. Система входит в цикл: удалить-запустить-перегрузить-удалить.
Решение - асимметричные окна масштабирования. scale_up_window (окно увеличения) - интервал, в течение которого метрика должна превышать порог для добавления реплики. Короткое окно: 30-60 секунд. Перегрузка при инференсе наступает быстро, реагировать нужно сразу.
scale_down_window (окно уменьшения) - интервал, в течение которого метрика должна быть ниже порога для удаления реплики. Длинное окно: 5-10 минут. Нагрузка на LLM-инференс часто волнообразная: пользователи отправляют запрос, ждут ответ, читают его, затем отправляют следующий. Между волнами - несколько минут затишья. Если удалить реплику в это затишье, следующая волна встретит холодный старт.
Практические значения: scale_up_window - 60 секунд с порогом inflight_requests > 80% от максимума реплики. scale_down_window - 300 секунд (5 минут) с порогом inflight_requests < 30% от максимума реплики в течение всего окна. Для высоконагруженных систем с дорогими GPU окно уменьшения увеличивают до 10-15 минут, чтобы избежать лишних затрат на холодные старты.
Холодный старт: почему запуск новой реплики - это минуты, а не секунды
Холодный старт - время от решения добавить реплику до её готовности обрабатывать запросы. Для LLM-инференса это не секунды, а минуты. Данные из практики: базовая модель 9B (веса ~18 ГБ) запускается за 86 секунд. Кастомная модель с дообученными весами 18 ГБ - за 145 секунд. Модели 70B с весами 140 ГБ требуют 5-8 минут на холодный старт.
Что происходит за эти минуты: загрузка весов модели из хранилища в память GPU, инициализация движка инференса, прогрев кэша внимания (KV-cache), подключение к балансировщику. Всё это время реплика не обрабатывает запросы. Оставшиеся реплики принимают нагрузку на себя.
Если scale_down_window слишком короткое, система удаляет реплику при первом спаде нагрузки. Через 2 минуты нагрузка возвращается, запускается новый холодный старт - ещё 86-145 секунд простоя. За это время inflight_requests на оставшихся репликах превышает порог, TTFT растёт. Система добавляет вторую реплику - ещё один холодный старт. Получается каскад: вместо плавного масштабирования - постоянные перезапуски и деградация сервиса.
Бюджет холодного старта нужно закладывать в политику масштабирования. Для модели 9B с холодным стартом 86 секунд и scale_up_window 60 секунд общее время реакции на всплеск - около 2,5 минут. Это значит, что система должна выдерживать пиковую нагрузку на текущих репликах эти 2,5 минуты. Если нет - нужно держать горячий резерв: дополнительную реплику, которая всегда готова, но не получает трафик при нормальной нагрузке. Это плата за стабильный TTFT.
Сравнение стоимости разных архитектур для инференса, включая варианты на Intel Gaudi 2 и Nvidia H100 с двукратной экономией, вы найдёте в материале о корпоративном RAG без переплат.
Практические рекомендации: собираем всё вместе
Основная метрика для автомасштабирования LLM-инференса - inflight_requests. Она показывает рост очереди до появления задержек. Порог подбирается нагрузочным тестированием: фиксируете момент роста TTFT, берёте inflight_requests в этот момент, вычитаете 20%. Это ваш порог для scale up.
Дополнительно мониторите TTFT и e2e_latency - они подтверждают, что масштабирование сработало и пользователи довольны. GPU utilization отслеживайте для планирования мощностей, но не используйте как триггер масштабирования.
Окна масштабирования: scale_up_window - 30-60 секунд, scale_down_window - 5-10 минут. Точные значения зависят от паттернов нагрузки. Для систем с резкими всплесками (чат-боты в рабочее время) окно уменьшения увеличивают до 15 минут. Для систем с предсказуемой нагрузкой (ночная обработка документов) окна можно синхронизировать с расписанием.
Бюджет холодного старта замерьте для своей модели. Запустите реплику 10 раз, возьмите 95-й перцентиль времени запуска. Это значение закладывайте в расчёт пиковой ёмкости: текущие реплики должны выдерживать нагрузку в течение времени холодного старта плюс scale_up_window. Если не выдерживают - добавьте горячий резерв.
Адаптируйте пороги под сценарий использования. Для интерактивных приложений (чат, автодополнение) держите TTFT ниже 300 миллисекунд, порог inflight_requests - 70-80% от максимума реплики. Для пакетной обработки (summarization, классификация) допустима задержка 5-10 секунд, порог inflight_requests можно поднять до 90%.
Не используйте только GPU utilization. В эксперименте эта политика провалилась: GPU был загружен на 60%, а пользователи ждали ответа 15 секунд. Непрерывный батчинг скрывает перегрузку от этой метрики. Inflight_requests - единственная метрика, которая показала реальное положение дел и вовремя запустила масштабирование.