Оглавление
- Зачем разделять prefill и decode
- Как работает disaggregated serving
- Когда disaggregation действительно имеет смысл
- Как рассчитывать prefill и decode pools
- Минимальная схема на vLLM и NIXL
- Сеть и KV transfer: где чаще всего теряется выигрыш
- Что измерять: TTFT, ITL, throughput и transfer time
- Типичные проблемы и способы диагностики
- Как внедрять в production без риска
Готовы перейти на современную серверную инфраструктуру?
В King Servers мы предлагаем серверы как на AMD EPYC, так и на Intel Xeon, с гибкими конфигурациями под любые задачи — от виртуализации и веб-хостинга до S3-хранилищ и кластеров хранения данных.
- S3-совместимое хранилище для резервных копий
- Панель управления, API, масштабируемость
- Поддержку 24/7 и помощь в выборе конфигурации
Результат регистрации
...
Создайте аккаунт
Быстрая регистрация для доступа к инфраструктуре
Зачем разделять prefill и decode
LLM inference состоит как минимум из двух фаз с разным профилем нагрузки. Prefill обрабатывает все входные токены prompt и строит KV cache; эта часть хорошо загружает вычислительные блоки GPU и становится особенно тяжёлой на длинном контексте. Decode использует уже готовый KV cache и генерирует токены по одному; для него критичны пропускная способность памяти, объём VRAM и предсказуемая задержка между токенами.
В агрегированной схеме один worker выполняет обе фазы. Это просто и эффективно при умеренной нагрузке, но длинный prefill может конкурировать за GPU с уже идущим decode. В результате пользователь быстро получает первый ответ в одном запросе, но соседний поток неожиданно начинает выдавать токены рывками. Именно этот эффект особенно заметен в p95/p99 inter-token latency.
vLLM прямо указывает две основные причины использовать disaggregated prefilling: независимую настройку TTFT и ITL, а также контроль tail ITL. При этом документация отдельно предупреждает: само разделение фаз не гарантирует роста throughput. Архитектура нужна прежде всего для управляемой latency и независимого масштабирования, а не как универсальный способ получить больше tokens/s.
Если задача — просто снизить потребление VRAM или стоимость модели, сначала стоит посмотреть на квантизацию LLM. Disaggregation решает другой класс проблем: interference между фазами, разные SLO для TTFT/ITL и несимметричную потребность в prefill/decode capacity.
Как работает disaggregated serving
Базовый request path выглядит так: frontend принимает запрос, router выбирает prefill worker, тот строит KV cache, после чего cache переносится на decode worker. Decode worker получает уже подготовленное состояние attention и начинает генерацию токенов. В NVIDIA Dynamo этот обмен выполняется через NIXL; vLLM предоставляет KV connector abstraction и поддерживает несколько вариантов транспорта.
Ключевой объект здесь — не сам prompt, а KV cache. Для длинного контекста его объём может быть значительным, поэтому стоимость переноса нельзя считать нулевой. Если сеть между prefill и decode медленная или перегруженная, выигрыш от разделения может исчезнуть: TTFT будет включать не только вычисление prefill, но и передачу состояния на decode worker.
На одном узле транспорт может использовать GPU-to-GPU путь через CUDA IPC/NVLink-класс соединений. Между узлами важна RDMA-capable сеть — InfiniBand или соответствующим образом настроенный Ethernet/UCX. NVIDIA Dynamo описывает NIXL как слой, который может переносить KV cache напрямую VRAM-to-VRAM и выбирать доступный транспорт с минимальным участием CPU.
Архитектурно это похоже на pipeline, но есть важное отличие: prefill и decode pools масштабируются независимо. Например, два мощных prefill worker могут обслуживать четыре decode worker, если workload содержит длинные prompts и длинные ответы. Для другого сервиса пропорция может быть обратной.
Когда disaggregation действительно имеет смысл
Первый хороший сценарий — большой разброс длины входного контекста. Если часть запросов содержит 1–2 тыс. токенов, а часть — десятки тысяч, длинные prefills периодически блокируют более чувствительный к latency decode. Раздельные pools позволяют удерживать decode latency стабильнее.
Второй сценарий — разные SLO. Для интерактивного ассистента можно отдельно оптимизировать TTFT и ITL: prefill pool настроить под вычислительную плотность и крупный tensor parallelism, а decode pool — под большой KV cache и высокую memory bandwidth. Это особенно полезно, когда один тип GPU лучше соответствует одной фазе, чем другой.
Третий сценарий — большой multi-tenant inference. Отдельные очереди помогают ограничить влияние тяжёлых запросов одного клиента на потоковую генерацию другого. Но disaggregation не отменяет rate limits и quotas; для защиты дорогого AI API всё равно нужны лимиты по token budget, concurrency и стоимости запроса. Подробнее об этом — в материале про rate limits и quotas.
А вот для небольшого внутреннего ассистента на одной GPU или при низкой concurrency такая архитектура часто избыточна. Каждый новый слой — router, KV transfer, discovery, отдельные worker pools — увеличивает operational complexity. Сначала стоит измерить обычный vLLM deployment и chunked prefill; только затем доказывать необходимость disaggregation бенчмарком.
Как рассчитывать prefill и decode pools
Универсального соотношения xPyD не существует. Оно зависит от input/output token ratio, распределения длины prompt, concurrency, модели, tensor parallelism, частоты cache hit и требований к latency. Поэтому sizing нужно делать по реальному trace workload, а не по синтетическому одному prompt.
Для prefill основными сигналами будут prompt tokens/s, queue delay, TTFT и GPU compute utilization. Для decode — output tokens/s, active sequences, KV cache usage, ITL и memory bandwidth pressure. Если prefill queue растёт, а decode workers простаивают, добавление decode GPU проблему не решит.
Полезный ориентир — считать capacity не только в requests/s, а в токенах. Один запрос на 32 тыс. входных токенов создаёт совершенно другой prefill pressure, чем 32 запроса по 1 тыс. токенов, даже если RPS одинаков. Аналогично decode pool зависит от средней длины ответа и количества одновременно активных sequence.
Экономику лучше связывать с фактической загрузкой GPU. В статье как считать стоимость одного LLM-запроса разобран принцип расчёта через загрузку, токены и стоимость инфраструктуры. Для disaggregated serving эту модель нужно считать отдельно для каждого pool плюс стоимость network fabric.
Минимальная схема на vLLM и NIXL
В текущей документации vLLM disaggregated prefilling помечен как experimental. Для передачи KV cache доступны разные connectors; один из наиболее прямых вариантов — NixlConnector. Конкретные аргументы и compatibility matrix нужно сверять с вашей установленной версией vLLM, потому что интерфейс ещё меняется.
CUDA_VISIBLE_DEVICES=0 \
VLLM_NIXL_SIDE_CHANNEL_PORT=20001 \
vllm serve Qwen/Qwen3-8B \
--port 8001 \
--kv-transfer-config '{"kv_connector":"NixlConnector","kv_role":"kv_both","kv_buffer_device":"cuda","kv_connector_extra_config":{"backends":["UCX"]}}'
CUDA_VISIBLE_DEVICES=1 \
VLLM_NIXL_SIDE_CHANNEL_PORT=20002 \
vllm serve Qwen/Qwen3-8B \
--port 8002 \
--kv-transfer-config '{"kv_connector":"NixlConnector","kv_role":"kv_both","kv_buffer_device":"cuda","kv_connector_extra_config":{"backends":["UCX"]}}'
Эти команды показывают только worker-level основу. Production deployment требует router/frontend, discovery, health checks и корректного request handoff с kv_transfer_params. Если нужна готовая оркестрация, NVIDIA Dynamo предоставляет frontend, prefill router и backend integrations для vLLM/SGLang/TensorRT-LLM.
Обязательно фиксируйте версии CUDA, драйвера, vLLM, NIXL/UCX и используемого connector. Поскольку feature experimental, обновление serving stack без canary может сломать совместимость KV transfer даже при неизменной модели.
Сеть и KV transfer: где чаще всего теряется выигрыш
Перенос KV cache — главный новый overhead. Чем длиннее prompt и крупнее модель, тем больше данных нужно переместить. Если этот transfer попадает на обычный перегруженный 10/25GbE uplink вместе с storage и пользовательским трафиком, tail TTFT легко ухудшится вместо улучшения.
Поэтому при multi-node схеме нужно смотреть не только на номинальную скорость интерфейса, но и на реальную effective bandwidth, RTT, PCIe topology, NUMA placement и конкурирующий traffic. Для high-end GPU servers типичный production design использует отдельный high-speed fabric для GPU communication и контролирует topology-aware placement.
NVIDIA Dynamo отдельно поддерживает topology-aware KV transfer: router может учитывать rack/zone/domain и направлять decode ближе к выбранному prefill worker. Это важно, когда GPU pool распределён по нескольким стойкам или failure domains.
nvidia-smi topo -m
ip -br link
ethtool
ibv_devinfo 2>/dev/null || true
nvidia-smi --query-gpu=name,pci.bus_id,memory.total --format=csv
Если fabric не обеспечивает стабильную передачу KV, сначала исправляйте сеть. Увеличивать число GPU поверх узкого transport path обычно только усиливает queueing. Для распределённых AI workload требования к сети принципиально отличаются от обычного web hosting: важна не только внешняя пропускная способность, но и east-west traffic между accelerators.
Что измерять: TTFT, ITL, throughput и transfer time
Главная ошибка при тестировании — смотреть только на aggregate tokens/s. Disaggregation часто внедряют ради latency isolation, поэтому нужны как минимум TTFT p50/p95/p99, ITL p50/p95/p99, end-to-end latency, queue delay отдельно по prefill/decode, KV transfer duration, cache size, GPU utilization и VRAM occupancy.
Бенчмарк должен воспроизводить production mix: распределение input tokens, output tokens, concurrency и burstiness. Синтетический тест с одинаковыми короткими prompts может показать нулевой выигрыш и будет прав — для такого workload disaggregation действительно не нужен.
Полезно сравнить минимум три конфигурации: aggregated serving; aggregated + chunked prefill; disaggregated prefill/decode. Только так можно понять, компенсирует ли снижение tail ITL новый KV-transfer overhead.
A: aggregated vLLM
B: aggregated vLLM + tuned chunked prefill
C: disaggregated prefill/decode
For each:
- same model and quantization
- same prompt/output length distribution
- same concurrency profile
- record TTFT p50/p95/p99
- record ITL p50/p95/p99
- record total tokens/s
- record GPU utilization and KV cache usage
- for C: record KV transfer duration and network throughput
Если уже используется observability stack, метрики serving стоит связывать с GPU, NIC и application traces. Подход к объединению сигналов разобран в статье про OpenTelemetry.
Типичные проблемы и способы диагностики
TTFT вырос после разделения. Сначала проверяйте KV transfer: bandwidth, placement, side-channel connectivity, размер cache и очередь prefill. Если transfer занимает сопоставимое время с самим prefill, архитектура выбрана неудачно или сеть недостаточна.
Decode всё ещё дёргается. Проверьте, действительно ли запросы проходят через отдельный pool, нет ли decode saturation и достаточно ли VRAM для active sequences. Disaggregation не исправит перегруженный decode pool.
GPU utilization низкая, а latency высокая. Частая причина — router/queue/network bottleneck. Низкий SM utilization не означает, что GPU нужно добавить: worker может ждать KV cache или запросов из scheduler.
NIXL/connector не стартует. Проверяйте уникальность side-channel ports, совместимость версий, shared memory в контейнере, UCX/RDMA device visibility и firewall между worker nodes. В Kubernetes NVIDIA рекомендует явно выделять достаточный /dev/shm; дефолтные 64 MB контейнера могут быть недостаточны для transport metadata.
После обновления пропала совместимость. Experimental feature нельзя обновлять как обычный stateless web-сервис. Сохраняйте known-good image, прогоняйте accuracy/latency tests и имейте быстрый rollback на aggregated serving.
Как внедрять в production без риска
Начинайте не с полного переноса, а с baseline. Зафиксируйте aggregated serving на реальном workload и определите проблему численно: например, p99 ITL ухудшается при prompts более 16k токенов или TTFT не укладывается в SLO при burst traffic.
Затем поднимите отдельный небольшой prefill/decode pool и отправьте туда 1–5% traffic. Сравнивайте не только latency, но и error rate, tokens/s/GPU, network utilization и стоимость запроса. Если метрики ухудшились, rollback должен сводиться к маршрутизации traffic обратно в aggregated workers.
После подтверждения эффекта масштабируйте pools независимо. Меняйте только одну переменную за итерацию: количество prefill workers, количество decode workers, TP/PP, connector backend или network placement. Иначе будет невозможно понять причину улучшения.
Для крупных платформ disaggregation хорошо сочетается с Kubernetes и GPU scheduling, но не заменяет их. Если нужен общий слой распределения accelerators, очередей и autoscaling, см. материал Kubernetes для AI inference.
Итоговый критерий простой: архитектура оправдана, если она даёт измеримое улучшение SLO или экономики на вашем workload. Если единственный результат — больше сервисов, портов и сетевых зависимостей, лучше оставить aggregated serving и оптимизировать batching, chunked prefill, KV cache и модель.