Concurrency sweep в Amazon SageMaker AI отвечает на конкретный вопрос: сколько одновременных запросов выдерживает ваш инференс-эндпоинт до того, как задержка начнёт расти быстрее пропускной способности. Метод подаёт на эндпоинт контролируемо растущий поток одновременных запросов и строит кривую производительности, из которой видно точку насыщения. Это число затем переводится в количество инстансов для продакшена.
Свой нагрузочный стенд собирать не нужно. Concurrency sweeps встроены в Amazon SageMaker AI Inference Recommendations, поэтому всё делается штатными средствами платформы, без самописных скриптов на Locust или k6. Рабочий процесс укладывается в четыре шага: развернуть модель через нативный vLLM-контейнер, задать профиль нагрузки, запустить sweep через API CreateAIBenchmarkJob и разобрать результаты.
Дальше: как читать точку насыщения, какие метрики важны (throughput, p99-задержка, TTFT, TPOT, разброс p50-p99), что показал пример на NVIDIA Nemotron-3 Nano 30B с точкой насыщения в 256 одновременных запросов на ml.g7e.2xlarge и как рецепт max-concurrency-under-sla автоматизирует подбор конкурентности под заданные SLA.
Что такое concurrency sweeps и какую проблему они решают
Concurrency sweep это систематический подход к бенчмаркингу: на инференс-эндпоинт Amazon SageMaker AI отправляются контролируемые, постепенно растущие уровни одновременного трафика, и на каждом уровне измеряются два показателя: пропускная способность в токенах в секунду и задержка каждого запроса. Из этих замеров складывается кривая, показывающая, где эндпоинт ещё справляется, а где начинает захлёбываться (разбор AWS).
Без системного подхода подбор размера флота превращается в цикл: развернул, прогнал нагрузку вручную, подкрутил конфигурацию, повторил, пока цифры не покажутся приемлемыми. Проблема в том, что «показалось» плохо переводится в бюджет. Избыток инстансов означает простаивающие GPU, за которые вы платите по часам. Недостаток означает очереди запросов, всплески задержки и деградацию сервиса для пользователей. В исходном материале AWS крайний случай избыточности описан прямо: выбрать пять инстансов ml.g7e.2xlarge там, где хватило бы одного, значит сжечь бюджет на простаивающем железе. Это иллюстрация, а не рекомендуемая конфигурация.
Точка насыщения: что это и почему она важна
Точка насыщения это уровень конкурентности, на котором добавление одновременного трафика перестаёт улучшать пропускную способность и начинает ухудшать задержку. До неё эндпоинт перерабатывает запросы в более высокий throughput. После неё запросы выстраиваются в очередь, throughput стоит на месте, а задержка растёт.
Один sweep даёт три ориентира для планирования продакшена:
- Идеальный баланс: конкурентность, при которой пропускная способность максимальна, а задержка ещё укладывается в допустимые границы.
- Точка разрыва: уровень, где задержка пересекает порог SLA.
- Коэффициент правильного размера: сколько инстансов нужно, чтобы покрыть пиковый трафик при известной ёмкости одного инстанса.
Первый ориентир задаёт конфигурацию обслуживания. Второй проводит красную линию, за которую нельзя заходить на пиках. Третий превращает замеры в число инстансов и в счёт за GPU.
Как работают concurrency sweeps в SageMaker AI Inference Recommendations
Механизм встроен в SageMaker AI Inference Recommendations. Отдельную нагрузочную инфраструктуру создавать и поддерживать не требуется: sweep запускается как задача бенчмаркинга на платформе. Рабочий процесс состоит из четырёх шагов:
- Развернуть модель на эндпоинте Amazon SageMaker AI с использованием нативного vLLM-контейнера.
- Настроить профиль нагрузки: число входных и выходных токенов, режим стриминга.
- Запустить sweep через API CreateAIBenchmarkJob.
- Проанализировать результаты и определить оптимальную конкурентность и размер флота.
Перед стартом нужны аккаунт AWS с доступом к Amazon SageMaker AI и AWS Identity and Access Management. Для первого прогона разумно взять модель поменьше и один инстанс: цель первого sweep - получить рабочую кривую, а не сразу закрыть вопрос с продакшен-конфигурацией.
Настройка профиля нагрузки: входные и выходные токены, стриминг
Профиль нагрузки определяет, насколько результаты sweep будут похожи на реальность. Указываются типичное число входных и выходных токенов для вашего сценария и режим стриминга. От этих параметров зависят и форма кривой, и итоговая точка насыщения.
Чат-бот с системным промптом и короткими ответами на 150-300 выходных токенов и генератор длинных документов на 2-4 тысячи выходных токенов нагружают эндпоинт по-разному, особенно на этапе декодирования. Если измерить систему на коротком профиле и запустить её на длинной генерации, точка насыщения окажется ниже замеренной. Обратная ошибка тоже встречается: замер на длинных ответах даёт пессимистичную оценку для сценария с короткими репликами. Профиль стоит брать из реальных логов запросов, а не из ощущений.
Запуск sweep через API CreateAIBenchmarkJob
Сам sweep инициируется программным вызовом CreateAIBenchmarkJob с заданными уровнями конкурентности. В материалах AWS приведена прогрессия 64, затем 256, затем 1024 одновременных запроса. Шаг между уровнями должен быть достаточно крупным, чтобы разница в поведении была заметной, и достаточно мелким, чтобы не перескочить точку насыщения. Запускать job можно многократно, меняя конфигурацию сервинга или профиль нагрузки, и сравнивать кривые между собой.
Какие метрики смотреть и как анализировать результаты
На каждом уровне конкурентности sweep фиксирует два базовых показателя: пропускную способность в токенах в секунду и задержку каждого запроса. Для инженерных решений этого мало, поэтому в анализ добавляются p99-задержка, TTFT, TPOT и разброс между p50 и p99 (описание метрик у AWS).
Throughput и задержка: как найти баланс
Кривая throughput сначала растёт почти линейно, затем выходит на плато. Кривая задержки на малой конкурентности почти не меняется, а после определённого уровня уходит вверх круто. Точка насыщения лежит на перегибе между этими двумя участками.
Условный пример: если при 256 одновременных запросах throughput максимален и p99 держится около 2 секунд, а при 512 запросах throughput не растёт, зато p99 уходит к 5 секундам, рабочая точка - 256 или чуть ниже. Числа здесь иллюстративные, подставляйте свои замеры. Резонно оставлять небольшой запас ниже точки насыщения: реальный трафик неравномерен, а синтетический профиль не повторяет его в точности.
TTFT и TPOT: что они говорят о пользовательском опыте
TTFT (time to first token) это время до первого токена. Для чатов и стриминговых интерфейсов это главная метрика восприятия: пользователь видит реакцию только после первого токена. TPOT (time per output token) определяет, с какой скоростью дальше появляется текст.
Сочетания метрик указывают на разные узкие места. Высокий TTFT при низком TPOT намекает на очередь на входе: запрос долго ждёт, прежде чем начать обрабатываться. Высокий TPOT при низком TTFT говорит о нехватке вычислительных ресурсов на декодировании. Смотреть на них стоит вместе с конкурентностью: TTFT нередко начинает деградировать раньше, чем общая пропускная способность выходит на плато.
Отдельный показатель - разрыв между p50 и p99. Медиана описывает типичный запрос, p99 показывает хвост. Если p50 стабильна, а p99 уходит далеко вверх, часть запросов попадает в очередь и обслуживается неравномерно. Такой разрыв часто проявляется раньше падения throughput и служит сигналом, что конкурентность подобрана слишком агрессивно.
Практический пример: NVIDIA Nemotron-3 Nano 30B на ml.g7e.2xlarge
В разборе AWS разворачивается NVIDIA Nemotron-3 Nano 30B: MoE-модель с 3 млрд активных параметров. Инстанс - ml.g7e.2xlarge с GPU Blackwell. По результатам sweep точка насыщения пришлась на 256 одновременных запросов: дальнейший рост конкурентности не давал прироста пропускной способности и ухудшал задержку (пример на Nemotron-3 Nano 30B).
Как интерпретировать результат 256 запросов
256 одновременных запросов это ёмкость одного инстанса при конкретной модели и конкретном профиле нагрузки, а не универсальный максимум для ml.g7e.2xlarge. Если пиковый трафик составляет 1024 одновременных запроса, арифметика даёт минимум четыре инстанса. К этому числу стоит добавить запас на отказоустойчивость, неравномерность распределения между копиями и на то, что профиль реальной нагрузки может отличаться от тестового.
Меняете модель, версию контейнера, квантизацию или тип инстанса - sweep нужно прогонять заново. Точка насыщения зависит от всех этих факторов, и переносить 256 запросов на другую конфигурацию нельзя.
Автоматизация подбора конкурентности под SLA: рецепт max-concurrency-under-sla
Ручной разбор кривых плохо масштабируется: для каждой комбинации модели, инстанса и профиля нагрузки нужно заново искать, где throughput упирается в плато, а задержка пересекает порог. Рецепт max-concurrency-under-sla делает это сам: подбирает максимальную конкурентность, при которой выполняются заданные пороги SLA по end-to-end задержке, TTFT, TPOT и доле ошибок. Несколько порогов можно задавать одновременно, и найденная конфигурация обязана удовлетворять всем сразу, а не по отдельности.
Как задать пороги SLA и не ошибиться
Пороги зависят от приложения, универсальных чисел нет. Для интерактивного чата разумный ориентир - TTFT меньше секунды, иначе пользователь замечает паузу до начала ответа. Для пакетной генерации, где ответ забирают из очереди, требования к TTFT мягче, зато важнее end-to-end задержка и доля ошибок. Задавать только общую задержку рискованно: можно получить конфигурацию, которая формально укладывается в бюджет, но стартует с заметной паузой.
Слишком строгие пороги занижают конкурентность и заставляют платить за инстансы, которые простаивают большую часть времени. Слишком мягкие пороги пропускают деградацию пользовательского опыта. Рабочий приём: начать с консервативных значений, посмотреть, какую конкурентность подберёт рецепт, и ослаблять пороги по одному, отслеживая метрики после каждого изменения.
Ограничения и стоимость: что нужно помнить перед запуском sweep
Эндпоинты SageMaker AI тарифицируются по часам работы инстанса, поэтому каждый sweep на реальном железе стоит денег. GPU-инстансы вроде ml.g7e.2xlarge не та статья расходов, где стоит забыть про удаление ресурсов: забытый эндпоинт продолжает тарифицироваться, пока вы анализируете результаты.
Второе ограничение связано с природой метода: sweep даёт отправную точку, но не заменяет нагрузочное тестирование в продакшене с реальным распределением запросов. Синтетический профиль с фиксированным числом входных и выходных токенов не воспроизводит всё разнообразие живого трафика.
Третье: результаты привязаны к версии модели и контейнера. Обновили vLLM-контейнер или сменили квантизацию - замеры придётся повторить.
Как минимизировать расходы на тестирование
- Брать минимально достаточное число уровней конкурентности: обычно хватает трёх-четырёх точек, чтобы увидеть перегиб.
- Удалять эндпоинт сразу после сбора данных, не оставлять его «на потом».
- Планировать прогон так, чтобы за одну сессию проверить несколько конфигураций, а не разворачивать инстанс заново под каждую.
- Использовать рецепт max-concurrency-under-sla вместо ручного перебора: он отсекает заведомо неподходящие уровни сам.
Кому и когда стоит применять concurrency sweeps
Подход окупается там, где решения о размере флота принимаются регулярно и стоят дорого. Основные сценарии: переход генеративного AI-сервиса из прототипа в продакшен, смена модели или типа инстанса, сокращение затрат на уже работающем эндпоинте, планирование ёмкости под прогнозируемый рост трафика.
Если нагрузка небольшая, стабильная и SLA мягкие, ручной оценки с запасом может хватить: стоимость одного лишнего инстанса окажется ниже затрат на несколько прогонов sweep. Обратная ситуация - высокие требования к задержке при больших объёмах трафика: здесь ошибка в размере флота обходится дороже всего, и системный замер быстро себя окупает.
Практический старт: разверните модель через нативный vLLM-контейнер на одном инстансе, задайте профиль нагрузки из реальных логов, прогоните sweep по уровням 64, 256 и 1024 и посмотрите, где ломается кривая. Заодно почитайте руководство по работе с новым SageMaker Python SDK v3: там показано, как связать бенчмаркинг, подбор инстанса и деплой конфигурации в одном ноутбуке. После сбора данных удалите эндпоинт и перенесите цифры в план ёмкости.