# vLLM disaggregated prefill: раздельные узлы и проверка задержки

Источник: https://modelium.tech/zhurnal/vllm-disaggregated-prefill/

Disaggregated prefill в vLLM переносит обработку входного контекста и генерацию следующих токенов в разные экземпляры сервера; KV-кеш передаётся через коннектор. Это экспериментальный способ отдельно настраивать TTFT и задержку между токенами при смешанной нагрузке, а не гарантия большей пропускной способности. Для решения о внедрении сравните его с одним сервером и chunked prefill на одинаковых запросах.

**Проверено по vLLM Git-ревизии **`**f98d1fc**`** от 4 октября 2026 года.** Disaggregated prefill помечен разработчиками как экспериментальный. Описанные действия — процедура для вашего стенда, не результаты запуска Modelium. Следующие версии могут изменить совместимость и команды [s1](#src-s1)[s2](#src-s2).

## Какую задачу решает disaggregated prefill?

Обычный сервер выполняет обработку входного контекста (*prefill*) и последующую генерацию (*decode*) на одной группе ресурсов. При смешанных длинных и коротких запросах prefill может влиять на интервал выдачи токенов у уже обслуживаемых клиентов. В экспериментальной архитектуре vLLM два экземпляра разделяют фазы и передают вычисленный KV-кеш от производителя к потребителю; это позволяет настраивать время до первого токена (TTFT) и задержку между токенами (ITL) раздельно. В документации прямо сказано, что сам факт разделения **не повышает throughput** [s1](#src-s1).

Этот путь предназначен для команды, у которой уже есть работающая модель и сервер vLLM, инфраструктура для двух экземпляров и реальные измерения хвостовой задержки. Если у вас один GPU или короткие редкие запросы, начните с [одного сервера vLLM](/zhurnal/vllm/) и [chunked prefill](/zhurnal/vllm-chunked-prefill/): в vLLM V1 планировщик по возможности включает разбиение входа по умолчанию и приоритизирует decode в пределах бюджета токенов. Это альтернатива для проверки, не эквивалент раздельных экземпляров [s1](#src-s1)[s7](#src-s7).

Маршрут решения: тип запросов и целевая задержка → конкретная модель и её совместимость → отдельные GPU, сеть и память под оба процесса → NIXL и vLLM → прокси → сравнение реальных ответов и задержек. Размер файла модели нельзя превращать в минимальную RAM/VRAM каждого процесса. Документация CUDA даёт требования к платформе и способ установки, но ёмкость под **ваш** контекст, KV-кеш и параллелизм нужно оценивать и проверять отдельно [s2](#src-s2)[s3](#src-s3)[s8](#src-s8).

## Что проверить до установки двух экземпляров?

Сначала запишите версию vLLM, точный идентификатор модели, её архитектуру, dtype KV-кеша и attention backend. Матрица NixlConnector отмечает обычные dense-трансформеры как поддерживаемые для базового PD, но мультимодальные случаи — как ещё не проверенные, encoder-decoder — как неподдерживаемые. Handshake проверяет совместимость версии, модели, backend и dtype; не отключайте этот контроль ради запуска несовместимой пары. Два экземпляра могут использовать разный tensor parallelism в рамках ограничений матрицы, однако произвольные параметры обоих узлов не взаимозаменяемы [s3](#src-s3).

В документированном примере NIXL на одном хосте prefiller размещён на GPU 0, decoder — на GPU 1. Для CUDA-сборки vLLM указан NVIDIA GPU с compute capability не ниже 7.5, однако это **условие платформы для соответствующей установки**, не универсальный расчёт памяти или обещание, что любая модель поместится на двух картах. Убедитесь в наличии драйвера, отдельного окружения для установки vLLM и установленного NIXL; документация для NVIDIA предлагает `uv pip install nixl`, а список требуемых версий отсылает к файлу зависимостей того же релиза. Для других платформ установка и транспорт иные: не переносите CUDA-команды на ROCm или CPU [s2](#src-s2)[s8](#src-s8).

Проверьте сетевой маршрут между узлами и NIXL side channel. В примере producer и consumer используют разные `VLLM_NIXL_SIDE_CHANNEL_PORT` (5600 и 5601), переменные UCX и разные сервисные порты. На разных хостах нужен корректный `VLLM_NIXL_SIDE_CHANNEL_HOST`; значения `localhost` из примера нельзя просто копировать для межмашинного соединения. У NIXL есть разные transport backend, и доступность зависит от собранных плагинов [s2](#src-s2).

## Как воспроизвести документированную схему на тестовом стенде?

На **изолированном тестовом хосте с двумя совместимыми GPU** установите vLLM и NIXL по документации своей платформы. В первом процессе авторский пример запускает `Qwen/Qwen3-0.6B` с `CUDA_VISIBLE_DEVICES=0`, `UCX_NET_DEVICES=all`, `VLLM_NIXL_SIDE_CHANNEL_PORT=5600`, сервисным `--port 8100`, `--enforce-eager` и `--kv-transfer-config '{"kv_connector":"NixlConnector","kv_role":"kv_producer","kv_load_failure_policy":"fail"}'`. Во втором — **тот же идентификатор модели**, GPU 1, порт side channel 5601, `--port 8200` и `kv_role":"kv_consumer"` при тех же остальных настройках. Это пересказ проверенного примера vLLM, а не команда, запущенная Modelium; для конкретной архитектуры сверяйте матрицу, версию и формат KV [s2](#src-s2)[s3](#src-s3).

Затем нужен маршрутизатор: клиент не должен сам выдавать независимые обычные запросы двум экземплярам. Документация показывает `tests/v1/kv_connector/nixl_integration/toy_proxy_server.py` с `--prefiller-hosts localhost --prefiller-ports 8100 --decoder-hosts localhost --decoder-ports 8200 --port 8192`. Репозиторий также содержит демонстрационный `disagg_proxy_demo.py` для нескольких P/D-экземпляров; он маршрутизирует `/v1/completions` и `/v1/chat/completions`. Не смешивайте аргументы **разных** прокси. Начните с одного producer и одного consumer, проверьте ответы и передачу KV-кеша, затем масштабируйте; демо-код не заменяет аутентификацию, сетевую изоляцию и эксплуатационный контроль production-шлюза [s2](#src-s2)[s4](#src-s4).

Не раскрывайте сервисные и side-channel порты внешней сети без отдельного контроля доступа. В исходнике demo-прокси административный endpoint требует `ADMIN_API_KEY`, но пользовательские completion-маршруты отдельной проверки ключа не содержат, а ветка отказа даже пишет полученный неверный ключ в лог. Для конфиденциальных данных прокси из примера следует держать внутри доверенной сети и дорабатывать под вашу политику доступа, а не считать его защищённым API-шлюзом [s4](#src-s4).

## Как проверить ответ и сравнить задержки без самообмана?

Сначала проверьте корректность ответов и ошибки для короткого и длинного входа через прокси; на обеих сторонах убедитесь, что работает **одна и та же модель**, а NIXL действительно переносит блоки. При `kv_load_failure_policy=fail` ошибка чтения KV останавливает запрос; режим `recompute` может заставить decode-инстанс заново выполнить prefill и испортить интерпретацию задержек. Смотрите `Num successful transfers`, `Num failed transfers` и просроченные KV-запросы в NIXL-логах или соответствующие Prometheus-счётчики, а не только HTTP 200 клиента. Стандартные `/metrics` каждого сервера дают дополнительную картину очереди и фаз запроса [s2](#src-s2)[s6](#src-s6).

Сравните два варианта: базовый одиночный сервер с настроенным chunked prefill и раздельную схему. Сохраните один и тот же тег модели, ревизию, квантизацию, входы, длины выходов, частоту прихода запросов, параллелизм, cache policy, GPU и условия сети. Документированный `vllm bench serve` измеряет **со стороны клиента** TTFT (от отправки до первого потокового вывода), ITL (интервалы между потоковыми выводами), TPOT и throughput. Измеряйте через тот адрес, который реально видит клиент (для PD — прокси); сохраняйте распределение и p99, число успешных/ошибочных запросов и расход GPU. Повторный одинаковый dataset может использовать prefix cache и завысить производительность, поэтому сбрасывайте кеш или меняйте seed осознанно. [Отдельный гайд по bench serve](/zhurnal/vllm-bench-serve/) помогает сохранить воспроизводимые параметры [s5](#src-s5)[s7](#src-s7).

Интерпретация проста: если хвост ITL стал ровнее, но TTFT, сеть или стоимость обслуживания ухудшились, это не автоматический выигрыш; сравните с целевым SLO и операционной сложностью. Подменять ITL средним TPOT нельзя: при пакетной или speculative-выдаче один потоковый фрагмент может содержать несколько токенов. Modelium не выполнял этот эксперимент и не располагает собственными миллисекундами для вашего оборудования [s1](#src-s1)[s5](#src-s5).

## Когда разделение не подходит и что делать при сбое?

- **Несовместимая пара:** проверьте handshake, одинаковые model/backend/KV dtype и ограничения вашей архитектуры. Не отключайте `enforce_handshake_compat` лишь ради исчезновения ошибки. Для экспериментальной функции обновление версии требует повторной проверки [s1](#src-s1)[s3](#src-s3).

- **Передача KV не работает или тормозит:** проверьте порты, `VLLM_NIXL_SIDE_CHANNEL_HOST`, UCX-настройки и transport plugin; сопоставьте failed transfers и просроченные leases с клиентскими ошибками. `kv_load_failure_policy=recompute` может скрыть поломку переноса за повторным вычислением [s2](#src-s2).

- **Один сервер уже укладывается в SLO:** сначала оптимизируйте [chunked prefill](/zhurnal/vllm-chunked-prefill/) и оцените [метрики vLLM](/zhurnal/vllm-metrics-prometheus/); второй сервер, выделенные GPU, прокси и сеть вводят дополнительные точки отказа. Чисел об экономии без стенда нет [s1](#src-s1)[s6](#src-s6)[s7](#src-s7).

- **Нужно перенести настройку на несколько GPU:** не путайте самостоятельные producer/consumer с tensor или pipeline parallelism одного экземпляра. У некоторых гибридных моделей или конфигураций коннектора ограничения на неодинаковый TP, PP и block size; смотрите матрицу перед созданием стека [s3](#src-s3).

Итоговый следующий шаг — [измерить TTFT и ITL](/zhurnal/vllm-bench-serve/) на своих длинных и коротких запросах при одинаковой нагрузке. Сохраните исходную конфигурацию одного сервера как путь возврата: выключите тестовый прокси и два PD-процесса, верните прежний маршрутизатор и повторите контрольный запрос. Только фактический ответ модели, рабочий KV transfer и сравнительные клиентские метрики позволяют решить, нужен ли disaggregated prefill именно вашему сервису [s1](#src-s1)[s2](#src-s2)[s5](#src-s5).

## Источники

1. [vLLM: Disaggregated Prefilling (experimental)](https://github.com/vllm-project/vllm/blob/f98d1fc49361cb50457f6109c3c01a5c77e415b4/docs/features/disagg_prefill.md) — получено 2026-10-04
2. [vLLM: NixlConnector Usage Guide](https://github.com/vllm-project/vllm/blob/f98d1fc49361cb50457f6109c3c01a5c77e415b4/docs/features/nixl_connector_usage.md) — получено 2026-10-04
3. [vLLM: NixlConnector Compatibility Matrix](https://github.com/vllm-project/vllm/blob/f98d1fc49361cb50457f6109c3c01a5c77e415b4/docs/features/nixl_connector_compatibility.md) — получено 2026-10-04
4. [vLLM: демонстрационный proxy для disaggregated serving](https://github.com/vllm-project/vllm/blob/f98d1fc49361cb50457f6109c3c01a5c77e415b4/examples/disaggregated/disaggregated_serving/disagg_proxy_demo.py) — получено 2026-10-04
5. [vLLM: Benchmark CLI и определения TTFT, ITL, TPOT](https://github.com/vllm-project/vllm/blob/f98d1fc49361cb50457f6109c3c01a5c77e415b4/docs/benchmarking/cli.md) — получено 2026-10-04
6. [vLLM: Production Metrics и endpoint /metrics](https://github.com/vllm-project/vllm/blob/f98d1fc49361cb50457f6109c3c01a5c77e415b4/docs/usage/metrics.md) — получено 2026-10-04
7. [vLLM: Optimization и chunked prefill](https://github.com/vllm-project/vllm/blob/f98d1fc49361cb50457f6109c3c01a5c77e415b4/docs/configuration/optimization.md) — получено 2026-10-04
8. [vLLM: установка CUDA на NVIDIA GPU](https://github.com/vllm-project/vllm/blob/f98d1fc49361cb50457f6109c3c01a5c77e415b4/docs/getting_started/installation/gpu.cuda.inc.md) — получено 2026-10-04
