Нагруженная выдача модели: когда модель стала сервисом
В ноутбуке модель выглядит просто: вызвал функцию, получил ответ. В продукте всё сложнее: запросы приходят неравномерно, пользователи не хотят ждать, GPU дорогие, часть запросов длиннее других, а при перегрузе сервис не должен развалиться. Поэтому выдача модели как сервиса - это не только “ускорить модель”, а правильно организовать очередь, маршрутизацию, приоритеты, батчинг, отмену запросов и безопасное обновление версий модели.
Главная идея блока: хороший ML-сервис оптимизируют не под одну красивую цифру, а под баланс. Можно сильнее загрузить GPU, но ухудшить ожидание пользователя. Можно объединять больше запросов в батч, но увеличить задержку. Можно включить повторы при ошибках, но создать вторую волну нагрузки. Поэтому сначала спрашиваем: кому отвечаем, за какое время, сколько стоит ошибка и что делаем, если система перегружена.
Задержка: важен хвост, а не только среднее
Среднее время ответа часто обманывает. Представь, что 90 запросов отвечают за 100 мс, а 10 запросов ждут по 5 секунд. Среднее ещё может выглядеть терпимо, но реальные пользователи регулярно видят зависания и таймауты. Поэтому в нагруженных сервисах смотрят не только среднее, но и “хвосты” распределения: p95 и p99, то есть насколько медленны самые неприятные 5% или 1% запросов.
| Метрика | Простыми словами | Ловушка |
|---|---|---|
| p50 задержка | Медиана: половина запросов быстрее, половина медленнее. | Может быть хорошей, даже если часть пользователей регулярно страдает. |
| p95 / p99 задержка | Самые медленные 5% или 1% запросов. | Именно здесь видны очереди, перегруз, длинные запросы и неудачные реплики. |
| Пропускная способность | Сколько запросов или токенов система обрабатывает в секунду. | Большая пропускная способность не гарантирует быстрый ответ конкретному пользователю. |
| Глубина очереди | Сколько работы уже накопилось перед обработчиками. | Если очередь растёт, масштабирование может сработать слишком поздно. |
| Таймауты и отмены | Сколько запросов не дождалось результата. | Повторные запросы без ограничений могут усилить перегруз. |
| Цена запроса | Сколько стоит один ответ при текущей загрузке и железе. | Самый дешёвый режим может быть плохим для интерактивного продукта. |
Как живёт один запрос
Запрос обычно не идёт сразу в модель. Сначала входной слой принимает запрос и проверяет лимиты. Потом маршрутизатор решает, на какую модель или копию модели отправить работу. Очередь временно хранит запросы. Планировщик решает, какие запросы объединить в батч и какой машине отдать. Исполнитель держит модель в памяти и считает ответ. Для LLM или генеративных задач результат может идти постепенно: токен за токеном или превью за превью. Если пользователь ушёл или дедлайн истёк, запрос надо уметь отменить, иначе GPU продолжит делать бесполезную работу.
- Admission control, или входной лимит. Если система уже перегружена, лучше честно отказать, дать упрощённый ответ или попросить повторить позже, чем принять всё и уронить p99 всем пользователям.
- Очередь. Очередь полезна только пока ожидание меньше пользовательского дедлайна. После этого она превращается в фабрику таймаутов.
- Планировщик. Короткие интерактивные запросы, длинные фоновые задачи и платящие пользователи не должны бесконтрольно конкурировать за одну и ту же GPU.
- Потоковая выдача. Для LLM первый токен часто важнее полного времени до конца ответа: пользователь видит, что система начала работать.
- Отмена. Если пользователь закрыл страницу, нужно освобождать место в очереди, память под контекст и слот в батче.
Батчинг: полезный, но опасный компромисс
Батчинг означает: не считать каждый запрос отдельно, а объединять несколько запросов и прогонять их вместе. Это часто лучше загружает GPU. Но батч не бесплатный: иногда надо ждать, пока батч соберётся; запросы внутри батча могут быть разной длины; длинная генерация может задержать короткую. Поэтому батчинг почти всегда меняет баланс между “дёшево и много” и “быстро для конкретного пользователя”.
| Подход | Идея | Где хорош | Где боль |
|---|---|---|---|
| Статический батч | Собрали пачку, прогнали, потом следующую. | Офлайн-скоринг и одинаковые входы. | Плох для живого продукта: ждём пачку и простаиваем на разной длине запросов. |
| Динамический батч | Ждём маленькое окно времени, чтобы объединить близкие запросы. | Обычная выдача модели как сервиса, когда можно подождать несколько миллисекунд. | Большое окно улучшает загрузку GPU, но ухудшает хвосты задержки. |
| Непрерывный батчинг для LLM | В генерации новые запросы заходят в работу, когда старые последовательности завершились. | LLM-сервисы с разной длиной промптов и ответов. | Нужно следить за памятью под контекст, справедливостью и длинными запросами. |
Не путай загрузку железа и хороший сервис
Что делать при перегрузе
Перегруз должен быть заранее продуманным сценарием, а не пожаром. Хороший сервис знает, кого обслуживать первым, где ограничивать очередь, когда включать более дешёвую модель, когда возвращать ответ из кеша, когда переводить задачу в фоновый режим и когда честно отказать. Важно не только добавить машины, а не дать системе самой усилить проблему.
- Обратное давление. Верхний слой должен понимать, что нижний слой занят. Иначе запросы будут бесконечно копиться.
- Приоритеты. Интерактивный пользователь, платный пользователь и ночная фоновая обработка не должны иметь одинаковое право на ресурс.
- Мягкая деградация. Можно временно взять меньшую модель, сократить контекст, снизить разрешение, вернуть кешированный ответ или поставить задачу в фон.
- Масштабирование. Сигналом должны быть не только CPU/GPU, но и очередь, хвосты задержки, таймауты и время прогрева новых копий модели.
- Безопасная выкатка. Новая версия модели или новая настройка батчинга может улучшить среднюю скорость, но ухудшить p99. Поэтому нужны canary-проверка на малой доле трафика, сравнение метрик и быстрый откат.
Кейс Meta простыми словами: проблема была не только в модели
Полезный пример - Meta Engineering post про ads inference. В рекламной системе один пользовательский запрос может запускать много обращений к разным моделям. Модели часто обновляются, копии моделей разложены по множеству машин, железо неоднородное, а ответ нужен быстро. Проблема была не в том, что “одна модель медленная”. Проблема была в том, что нагрузка распределялась неровно: часть машин или копий моделей становилась перегруженной, и именно они портили хвосты задержки.
Что они делали по сути: лучше решали, куда отправить запрос; лучше размещали копии моделей по машинам; считали нагрузку не только на сервер в целом, но и на конкретную модель; учитывали пропускную способность памяти как отдельный ресурс; аккуратнее переводили трафик между версиями моделей; заранее оценивали, сколько копий модели понадобится при будущем трафике. По их отчёту это позволило обслуживать больше работы теми же ресурсами, снизить таймауты и улучшить p99. Но главный урок не в процентах, а в логике: узкое место было в распределении нагрузки, обновлении моделей и модели ресурсов, а не только в скорости отдельных ядер вычисления или среды исполнения.
| Что было в кейсе | Перевод на простой язык | Общий урок |
|---|---|---|
| Балансировка маршрутизации | Запросы надо отправлять туда, где реально есть свободная ёмкость. | Не достаточно иметь много серверов; важно правильно распределять работу между ними. |
| Балансировка размещения моделей | Копии популярных моделей должны лежать там, где они не создают горячие точки. | Размещение модели по машинам влияет на задержку так же, как код модели. |
| Счётчики нагрузки по каждой модели | Сервер может выглядеть “не очень загруженным”, но конкретная модель на нём уже перегружена. | Метрика должна соответствовать реальной единице работы, иначе балансировщик слепой. |
| Память как ресурс | Иногда упираемся не в число ядер, а в скорость чтения/записи памяти. | CPU/GPU utilization не всегда объясняет тормоза; надо профилировать настоящий bottleneck. |
| Бюджет на смену версии | Во время обновления модели часть трафика временно переезжает и может разбалансировать систему. | Rollout модели - часть serving architecture, а не формальная DevOps-операция. |
| Прогноз числа копий | Если трафик предсказуем, лучше заранее подготовить нужное число реплик. | Реактивное масштабирование может опоздать к пику нагрузки. |
Суть и вывод
Как это обсуждать на собеседовании
На собеседовании по нагруженному ML-сервиса обычно проверяют не знание всех фреймворков, а умение рассуждать под ограничениями. Хороший ответ начинается с уточнений: это интерактивный продукт или фоновая обработка? Как быстро пользователь ждёт ответ? Что хуже: таймаут, устаревший ответ, дорогой запасной режим или немного худшее качество? Есть ли платные пользователи? Бывают ли пики нагрузки? Разные ли модели делят одну и ту же GPU?
- “Как обслужить долгую генерацию?” Сделай задачу асинхронной: статус задачи, очередь с лимитом, частичное превью, отмена, таймаут, повтор только по правилам и запасной режим.
- “Почему батчинг ухудшил UX?” Запросы начали ждать сбор батча, короткие ответы оказались за длинными, p99 вырос, а средняя пропускная способность при этом могла улучшиться.
- “GPU недогружена, но очередь растёт. Что проверять?” Передачу данных, CPU-предобработку, планировщик, размер батча, пропускную способность памяти, прогрев worker-ов, загрузку модели, блокировки и давление на downstream-сервисы.
- “Как приоритизировать платных пользователей?” Разделить очереди и квоты, зарезервировать часть ёмкости, задать отдельные SLO и заранее описать поведение при перегрузе.
- “Какие дашборды нужны?” Очередь, p50/p95/p99, таймауты и отмены, запросы или токены в секунду, память и загрузка GPU, размеры батчей, цена запроса и сигналы регресса качества.
Какая интерактивная иллюстрация здесь нужна
Материалы
Официальные docs
Serving docs and production metrics reference for LLM workloads.
Architecture reference for router/model-server separation and serving behavior.
Official Triton docs for composing preprocessing/model/postprocessing into server-side ensembles.
Production cases
Production case for utilization, p99 latency and timeout-error trade-offs at Meta scale.
Concrete runtime optimization migration story for high-volume NLP inference.
Recent serving-layer optimization story with reported latency/throughput context.