Перейти к основному содержимому

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

Planner architecture showing Metric Collector, Load Predictor, and Performance Interpolator feeding into the Scaling Algorithm and Connector Layer

Алгоритм масштабирования

Шаг 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 requests
  • next_isl: Average input sequence length
  • next_osl: Average output sequence length

Доступны четыре реализации предиктора:

ПредикторАлгоритмЛучше всего подходит для
Constantnext = currentСтабильных нагрузок, длинных интервалов
ARIMAAuto-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, чтобы опрашивать решения и сообщать о завершении.

Поток решения о масштабировании:

  1. Planner записывает (num_prefill, num_decode, decision_id) в runtime
  2. Внешняя система читает решение через client.wait()
  3. Внешняя система выполняет масштабирование
  4. Внешняя система сообщает о завершении через client.complete(decision)
  5. 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 для планируемой замены на проверку готовности.

После задержки:

  1. Инициализировать connector (K8s или Virtual в зависимости от --environment)
  2. Проверить структуру deployment
  3. Загрузить результаты профилирования
  4. Построить интерполяторы
  5. Инициализировать предиктор нагрузки
  6. Войти в основной цикл масштабирования

Соображения по производительности

  • Размер интервала корректировки: интервал должен быть достаточно длинным, чтобы операции масштабирования успевали завершиться. Если 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; вниз - только если оба сигнализируют о недогрузке.

Известные ограничения

  1. 30-секундная задержка запуска: жестко заданное ожидание регистрации компонентов. Его следует заменить проверкой готовности во время выполнения.
  2. Интервал корректировки против задержки масштабирования: если adjustment_interval < времени масштабирования, решения о масштабировании могут накапливаться. Planner пишет предупреждения, но не ставит их в очередь.
  3. Интерполяция по средним значениям: масштабирование на основе throughput использует средние ISL/OSL, которые могут плохо отражать бимодальные или heavy-tailed распределения.
  4. Ограничение одним DGD: каждый экземпляр Planner управляет ровно одним DGD. Координация нескольких моделей или нескольких DGD не поддерживается.

Будущая работа

  • Координация нескольких DGD для сценариев общего кластера
  • Интерполяция с учетом распределения (а не только среднего ISL/OSL)
  • Адаптивный интервал корректировки на основе наблюдаемой задержки масштабирования

Карта файлов

ФайлНазначение
planner_core.pyБазовый Planner, общий цикл масштабирования, ядро алгоритма
disagg_planner.pyOrchestrator режима disaggregated (prefill + decode)
agg_planner.pyOrchestrator режима 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