For clean Markdown content of this page, append .md to this URL. For the complete documentation index, see https://docs.nvidia.com/dynamo/llms.txt. For full content including API reference and SDK examples, see https://docs.nvidia.com/dynamo/llms-full.txt.
Проектирование Planner
Документация уровня Tier 3 для авторов и архитекторов. Для пользовательской документации см. docs/components/planner/.
Обзор
Planner - это контроллер автоскейлинга Dynamo. Он поддерживает два режима масштабирования: на основе throughput (с использованием данных профилирования и прогнозирования трафика) и на основе load (с использованием метрик движка в реальном времени и онлайн-регрессии). Этот документ описывает внутреннюю архитектуру, алгоритмы и компромиссы дизайна для обоих режимов.
Масштабирование на основе Throughput
Алгоритм масштабирования
Шаг 1: Сбор метрик
Каждые adjustment_interval секунд Planner запрашивает Prometheus и получает:
- Средние TTFT и ITL за интервал
- Общее количество запросов
- Среднюю длину входной последовательности (ISL) и выходной последовательности (OSL)
Запрос Prometheus обращается к конечной точке Frontend /metrics, которая публикует гистограммы и счетчики.
Шаг 2: Расчет поправочного коэффициента
Planner поддерживает поправочные коэффициенты, которые адаптируют прогнозы на основе профилирования к поведению в реальной среде:
prefill_correction = actual_ttft / expected_ttft
decode_correction = actual_itl / expected_itl
Эти коэффициенты учитывают трудномоделируемые факторы, такие как:
- Очередь запросов: всплесковый трафик приводит к более высокому TTFT, чем в профилированном steady-state
- Попадания в prefix cache: повторное использование KV снижает эффективное число токенов prefill и уменьшает фактический TTFT
- Chunked prefill в decode: небольшие prefill-задачи, обрабатываемые в decode engine, влияют на ITL
- Разброс метрик: средние значения ISL/OSL могут не отражать реальное распределение
Поправочные коэффициенты применяются как множители к следующему решению о масштабировании. Параметр --no-correction отключает это поведение для отладки или когда преобладают артефакты cold-start.
Шаг 3: Прогноз нагрузки
Planner прогнозирует три значения на следующий интервал:
next_num_req: Number of requestsnext_isl: Average input sequence lengthnext_osl: Average output sequence length
Доступны четыре реализации предиктора:
| Предиктор | Алгоритм | Лучше всего подходит для |
|---|---|---|
| Constant | next = current | Стабильных нагрузок, длинных интервалов |
| ARIMA | Auto-ARIMA с необязательным преобразованием log1p | Трендовых/сезонных паттернов |
| Kalman | Фильтр Калмана с локальным линейным трендом | Всплескового трафика |
| Prophet | Модель временных рядов Facebook Prophet | Сложной сезонности |
Все предикторы поддерживают warm-start из trace-файлов (--load-predictor-warmup-trace).
Шаг 4: Расчет числа реплик
Реплики prefill:
predicted_load = next_requests * next_isl / interval * min(1, prefill_correction)
prefill_replicas = ceil(predicted_load / interpolated_throughput / gpus_per_engine)
Поправочный коэффициент prefill линейно влияет на throughput, поскольку prefill выполняется в одном батче.
Реплики decode:
# Применить поправку к SLA-целевому значению ITL
corrected_itl = target_itl / decode_correction_factor
# Найти лучший throughput/GPU, который обеспечивает скорректированный ITL при прогнозируемой длине контекста
throughput_per_gpu = decode_interpolator.find_best_throughput_per_gpu(
itl=corrected_itl,
context_length=next_isl + next_osl / 2
)
# Вычислить требуемое число реплик
decode_replicas = ceil(next_num_req * next_osl / interval / throughput_per_gpu / gpus_per_engine)
Шаг 5: Выполнение масштабирования
Planner вызывает connector.set_component_replicas() с рассчитанными целями. По умолчанию масштабирование не блокирует поток: Planner продолжает мониторинг, пока реплики перестраиваются.
Дизайн Connector
Интерфейс
class PlannerConnector(ABC):
async def add_component(self, component_name)
async def remove_component(self, component_name)
# Расширенный интерфейс (не в ABC, но реализован обоими connector'ами):
async def set_component_replicas(self, targets, blocking)
async def validate_deployment(self, ...)
async def wait_for_deployment_ready(self)
KubernetesConnector
Непосредственно PATCH-ит ресурс DGD, чтобы обновить число реплик. Operator отслеживает изменения DGD и приводит развертывания компонентов к желаемому состоянию.
Решения по дизайну:
- Использует
DYN_PARENT_DGD_K8S_NAME, чтобы найти родительский DGD (внедряется operator'ом) - Разрешает службы по полю
subComponentType(prefill/decode) с fallback на устаревшие имена компонентов - Проверяет структуру deployment при запуске: убеждается, что службы prefill и decode существуют и что имена моделей совпадают
VirtualConnector
Для ненативных окружений (например, кастомных orchestrator'ов). Записывает решения о масштабировании в распределенный runtime через VirtualConnectorCoordinator (Rust binding). Внешние системы используют VirtualConnectorClient, чтобы опрашивать решения и сообщать о завершении.
Поток решения о масштабировании:
- Planner записывает
(num_prefill, num_decode, decision_id)в runtime - Внешняя система читает решение через
client.wait() - Внешняя система выполняет масштабирование
- Внешняя система сообщает о завершении через
client.complete(decision) - Planner видит
scaled_decision_id >= decision_idи продолжает работу
Тайм-аут: если подтверждение масштабирования не приходит в течение 1800 секунд (настраивается), Planner все равно переходит к новым решениям.
Интерполяция производительности
Planner использует данные профилирования до развертывания (файлы NPZ), чтобы отобразить (throughput, ISL/OSL, context_length) -> (TTFT, ITL). Эти данные поступают из процесса SLA-ориентированного профилирования (либо онлайн-профилирование GPU, либо оценка AI Configurator).
Поддерживаются два интерполятора:
- Prefill interpolator: отображает (throughput_per_gpu, ISL) -> TTFT
- Decode interpolator: отображает (throughput_per_gpu, context_length) -> ITL
Интерполяторы используют гранулярность sweep'а профилирования для определения точности. Более мелкая гранулярность означает больше выборок профилирования, но и более точную интерполяцию.
Инициализация
Сейчас Planner ждет 30 секунд (INIT_PLANNER_START_DELAY в components/src/dynamo/planner/__main__.py) как временное решение, пока другие компоненты (frontend, workers) регистрируются и стабилизируются; см. Known Limitations для планируемой замены на проверку готовности.
После задержки:
- Инициализировать connector (K8s или Virtual в зависимости от
--environment) - Проверить структуру deployment
- Загрузить результаты профилирования
- Построить интерполяторы
- Инициализировать предиктор нагрузки
- Войти в основной цикл масштабирования
Соображения по производительности
- Размер интервала корректировки: интервал должен быть достаточно длинным, чтобы операции масштабирования успевали завершиться. Если
adjustment_intervalкороче, чем время добавления/удаления worker'а (включая планирование pod'ов, загрузку модели и регистрацию), решения о масштабировании будут накладываться друг на друга. Значение по умолчанию 180s консервативно; для нагрузок с быстрой загрузкой моделей можно использовать более короткие интервалы. - Стабильность поправочного коэффициента: коэффициенты пересчитываются каждый интервал. Во время переходов трафика (например, ramp-up) они могут колебаться. Флаг
--no-correctionотключает коррекцию для сценариев, где преобладают искажающие cold-start артефакты. - Точность интерполяции против стоимости профилирования: более высокие
prefillInterpolationGranularityиdecodeInterpolationGranularityв sweep'е профилирования дают более точную интерполяцию, но линейно увеличивают время профилирования. Значения по умолчанию (16 для prefill, 6 для decode) балансируют точность и длительность профилирования. - Период разогрева предиктора: всем предикторам нужна история наблюдений, чтобы делать надежные прогнозы. ARIMA и Prophet требуют данных за несколько интервалов корректировки. Kalman начинает прогнозировать после
--kalman-min-pointsнаблюдений. Во время разогрева Planner использует constant predictor как fallback.
Масштабирование на основе Load
Режим на основе load использует ForwardPassMetrics (FPM) из event plane Dynamo, чтобы принимать решения о масштабировании с учетом SLA без необходимости в данных профилирования или KV Router.
Метрики
Каждый engine публикует ForwardPassMetrics на каждой итерации через ZMQ -> FpmEventRelay -> event plane. Planner подписывается через FpmEventSubscriber с автоматическим обнаружением engine'ов и отслеживанием жизненного цикла на основе MDC. Используются следующие ключевые поля:
- wall_time: время выполнения одной итерации (цель регрессии)
- scheduled_requests.sum_prefill_tokens: вход для prefill-регрессии
- scheduled_requests.sum_decode_kv_tokens: вход для decode-регрессии
- queued_requests: queued load prefill/decode для симуляции TTFT/ITL
- Пустые heartbeat'ы (wall_time=0) пропускаются
Диагностика
На каждом тике state machine масштабирования заполняет TickDiagnostics промежуточными данными решения - оцененными задержками, прогнозируемой нагрузкой, RPS по каждому engine и причинами решения - через внутренние поля _diag_*. Слой adapter читает это из PlannerEffects.diagnostics и:
- Устанавливает gauge-метрики Prometheus (например,
dynamo_planner_estimated_ttft_msи связанные оценки) - Записывает enum-метрики для причин решений load-scaling (
dynamo_planner_load_scaling_decision) - Передает данные в
DiagnosticsRecorder, который накапливает снимки по тикам и по расписанию выводит HTML-отчеты на основе Plotly
Глубины очередей FPM по каждому engine из _collect_fpm() экспортируются как размеченные gauge-метрики Prometheus.
Модели регрессии
Три специализированные модели регрессии (fpm_regression.py):
- PrefillRegressionModel: 1D-регрессия
sum_prefill_tokens -> wall_time. Оценивает TTFT, моделируя chunked prefill scheduling (чанкиmax_num_batched_tokens). - DecodeRegressionModel: 1D-регрессия
sum_decode_kv_tokens -> wall_time. Оценивает ITL для общей decode-нагрузки (scheduled + queued + средняя decode-длина). - AggRegressionModel: 2D-регрессия
(sum_prefill_tokens, sum_decode_kv_tokens) -> wall_time. Оценивает и TTFT (симулированный prefill с piggybacked decode), и ITL (decode со средней piggybacked prefill).
Решения о масштабировании
- Prefill/Decode: масштабировать вверх, если у ВСЕХ engine'ов оцененные TTFT/ITL > SLA; масштабировать вниз, если у ВСЕХ < SLA * sensitivity
- Agg: масштабировать вверх, если (у ВСЕХ TTFT > SLA) ИЛИ (у ВСЕХ ITL > SLA); масштабировать вниз, если (у ВСЕХ TTFT < SLA * sensitivity) И (у ВСЕХ ITL < SLA * sensitivity)
- Масштабирует только на +/-1 за интервал (неблокирующий режим с pending-desired guard: метрики продолжают наблюдаться, пока масштабирование идет, но новое действие масштабирования не выдается, пока не завершится предыдущее)
Сосуществование с масштабированием на основе Throughput
Когда включены оба режима, масштабирование на основе throughput (более длинный интервал) задает нижнюю границу числа реплик, а масштабирование на основе load (более короткий интервал) выполняет корректировки в реальном времени выше этого пола.
Режим Aggregated
В aggregated mode (--mode agg) engines обрабатывают и prefill, и decode через chunked prefill. Planner поддерживает обе модели регрессии - TTFT и ITL - но для обучения регрессии использует усредненные по времени метрики на worker'а (а не мгновенные), чтобы сгладить шум chunked prefill. Масштабируется вверх, если перегрузку сигнализирует либо prefill, либо decode; вниз - только если оба сигнализируют о недогрузке.
Известные ограничения
- 30-секундная задержка запуска: жестко заданное ожидание регистрации компонентов. Его следует заменить проверкой готовности во время выполнения.
- Интервал корректировки против задержки масштабирования: если
adjustment_interval< времени масштабирования, решения о масштабировании могут накапливаться. Planner пишет предупреждения, но не ставит их в очередь. - Интерполяция по средним значениям: масштабирование на основе throughput использует средние ISL/OSL, которые могут плохо отражать бимодальные или heavy-tailed распределения.
- Ограничение одним DGD: каждый экземпляр Planner управляет ровно одним DGD. Координация нескольких моделей или нескольких DGD не поддерживается.
Будущая работа
- Координация нескольких DGD для сценариев общего кластера
- Интерполяция с учетом распределения (а не только среднего ISL/OSL)
- Адаптивный интервал корректировки на основе наблюдаемой задержки масштабирования
Карта файлов
| Файл | Назначение |
|---|---|
planner_core.py | Базовый Planner, общий цикл масштабирования, ядро алгоритма |
disagg_planner.py | Orchestrator режима disaggregated (prefill + decode) |
agg_planner.py | Orchestrator режима aggregated (только на основе load) |
prefill_planner.py | Логика масштабирования только для prefill |
decode_planner.py | Логика масштабирования только для decode |
load_based_regression.py | Линейная регрессия со скользящим окном для масштабирования на основе load |
prometheus.py | Клиенты метрик Prometheus/router, классы данных |
perf_interpolation.py | Загрузка NPZ и интерполяция throughput/latency |
load_predictor.py | Предикторы ARIMA, Prophet, Kalman, Constant |
pre_swept_results_utils.py | Загрузчик предварительно рассчитанных данных профилирования H100/H200 |
kubernetes_connector.py | Интеграция с K8s API для масштабирования DGD |
kube.py | Низкоуровневая обертка над K8s client |
exceptions.py | Собственная иерархия исключений |
defaults.py | Конфигурации по умолчанию, отображения имен backend'ов |
planner_argparse.py | Определения аргументов CLI |