Аннотация
В работе исследуется влияние транспорта KV-кэша на производительность и работоспособность disaggregated inference, в котором стадии prefill и decode выполняются на разных GPU-узлах. На кластере из двух узлов с восемью NVIDIA B200 на каждом сравниваются RDMA/InfiniBand с GPUDirect и TCP/Ethernet в инференсе MiniMax-M3 через sglang. Эксперимент включает четыре уровня наблюдения: потолок фабрики (L0), изолированный microbenchmark transfer engine (L0b), фактический маршрут трафика по счётчикам портов (L1) и end-to-end метрики движка и запросов (L2/L3). Всего проанализировано 268 прогонов.
В основной матрице M1 RDMA/InfiniBand уменьшил медианный TTFT в 5,4-11,5 раза и увеличил output throughput в 2,8-3,8 раза относительно TCP на валидных прогонах. TPOT оставался сопоставимым, что указывает на локализацию влияния транспорта в переходе prefill → decode, а не в самом цикле decode. TCP завершился отказом в 31 из 75 прогонов (41%); все 75 прогонов IB были валидны. По логам отказ начинается с ошибки копирования из CUDA memory (out of memory), продолжается таймаутом KV-transfer, после чего prefill завершается, а router возвращает HTTP 503. Изолированный microbenchmark воспроизвёл отказ TCP и одновременно показал преимущество GPUDirect над host staging на 6,3-6,5%.
End-to-end поток не насыщал ни одну из сетевых фабрик: TCP достигал максимум 8,7 Гбит/с из 200 Гбит/с, IB - 52,4-61,7 Гбит/с из приблизительно 2933 Гбит/с суммарного измеренного L0-потолка восьми NDR-портов. Следовательно, наблюдаемая разница объясняется прежде всего архитектурой переноса - host staging против zero-copy GPUDirect, - а не нехваткой полосы. Дополнительная верификация attention backend показала снижение TTFT на 33,5-44,4% у triton относительно fa4, однако эти пути используют разные layouts KV и требуют отдельной проверки на production-нагрузках.
Вывод: в исследованной конфигурации выбор KV-транспорта является архитектурным ограничением PD-инференса. TCP нельзя рассматривать как функционально эквивалентный fallback для тяжёлых профилей MiniMax-M3.
Ключевые слова: LLM inference, PD-disaggregation, KV-cache, RDMA, InfiniBand, GPUDirect, TCP, TTFT, sglang, MiniMax-M3.
1. Введение
Увеличение размеров языковых моделей делает полезным разделение inference pipeline на две стадии. На стадии prefill модель обрабатывает входной контекст и формирует KV-кэш attention. На стадии decode этот кэш используется для последовательной генерации выходных токенов. При PD-disaggregation стадии размещаются на разных GPU-узлах, поэтому KV-кэш пересекает границу машины.
Такое размещение меняет статус сетевого транспорта. В монолитном inference KV остаётся внутри памяти одного процесса или узла; в PD-топологии транспорт KV входит в критический путь получения первого токена. При этом пропускная способность интерфейса не описывает всю стоимость переноса: реализация может потребовать копирования данных из VRAM в host memory, дополнительных буферов и синхронизации.
Цель исследования - разделить влияние сетевой фабрики и программного пути переноса, а также проверить, сохраняет ли TCP функциональную работоспособность на профилях с большим KV-кэшем.
1.1. Исследовательские вопросы
Как транспорт KV влияет на TTFT, TPOT, E2E latency и throughput?
При каких профилях длины входа/выхода и конкурентности TCP перестаёт быть работоспособным?
Является ли причиной деградации насыщение сетевой фабрики или программный staging path?
Даёт ли устойчивый выигрыш выбор backend'а переноса, микро-тюнинг mooncake или смена attention backend?
1.2. Проверяемые гипотезы
Обозначение | Гипотеза | Критерий проверки |
|---|---|---|
H1 | Транспорт влияет главным образом на TTFT, а не на TPOT | Сопоставление TTFT и TPOT |
H2 | Деградация TCP растёт с размером KV-переноса | Профили ISL/OSL и concurrency |
H3 | GPUDirect быстрее host staging | VRAM против DRAM в L0b |
H4 | TCP ограничен LACP-хэшем или полосой Ethernet | End-to-end rate против L0/L1 |
H5 | RDMA даёт кратный выигрыш на длинных prefill | Валидные M1-прогоны |
H6 |
| Запуск |
H7 | Микро-тюнинг даёт не более порядка 10% | OFAT по sglang и mooncake |
H8 | nixl/UCX сопоставим с mooncake на RDMA | M2 на одинаковых профилях |
2. Материалы и методы
2.1. Топология эксперимента
Две машины использовались следующим образом: node-a выполняла prefill и router, node-b - decode. Каждая роль модели работала с tensor parallelism TP=8 на восьми B200. Имена узлов, адреса и имена сетевых устройств обезличены; конфигурация воспроизводима на любой паре узлов с тем же железом.
PD-disaggregation (prefill/decode disaggregation) разделяет две фазы инференса по разным GPU-узлам: prefill считает префилл промпта и владеет его KV-кэшем, decode генерирует токены, читая этот кэш. Между фазами KV-кэш (единицы-сотни МиБ на запрос) должен физически переехать с узла prefill на узел decode - этот перенос и есть предмет работы. Поскольку decode не может начать работу до переноса, латентность транспорта лежит в критическом пути TTFT, но не в цикле декодирования (TPOT), где KV читается локально из памяти своей ноды.
После завершения prefill KV-кэш передавался на decode. Сравнивались RDMA/InfiniBand через восемь NDR-портов 400G с GPUDirect и TCP/Ethernet через bond0, LACP 2 × 100G, MTU 9000.
2.2. Аппаратная и программная конфигурация
Компонент | Значение |
|---|---|
GPU | 2 узла × 8 NVIDIA B200 |
Модель | MiniMax-M3 MXFP8, 31 shard, 443 771 536 556 байт |
Engine | sglang |
MoE backend |
|
Attention backend |
|
KV backend M1 | mooncake |
RDMA | NDR 400G, HCA |
Ethernet |
|
CUDA / OFED | CUDA 13.0.1 / MLNX-OFED 24.10 |
Базовый runtime |
|
Для исключения расхождения артефактов контейнер был пересобран с подавлением записи __pycache__ и очисткой TorchInductor cache. Content-addressed diffID всех 64 слоёв совпадали на обеих нодах.
2.3. Матрица нагрузок
Основная матрица M1 содержала пять профилей ISL/OSL, три уровня concurrency и пять повторов для каждого из двух транспортов.
Профиль ISL/OSL | Назначение |
|---|---|
| короткий вход и короткий выход |
| базовый прикладной профиль |
| длинный вход |
| очень длинный вход |
| длинный вход и длинный выход |
Всего M1: 5 × 3 × 5 × 2 = 150 прогонов. Дополнительно проведены M2, ось D (18 конфигураций sglang), ось C (13 конфигураций mooncake), отдельная верификация fa4/triton и L0/L0b. В summary.json - 268 записей.
2.4. Процедура и метрики
Перед стартом выполнялась жёсткая очистка контейнеров и orphan-процессов. Стек запускался в порядке prefill → decode → router. Перед измерениями выполнялись прогрев MSA JIT, HF processor и warmup-запрос. Использовались фиксированные seed=1234, num-prompts=32 и пять повторов на ячейку.
В аналитических графиках M1 валидным считался прогон с положительным TTFT выше артефактного порога и положительным output throughput. Отказы с ttft=0 не включались в медианы производительности, но учитывались отдельно в failure-rate и heatmap. Для агрегатов использовались медианы, графики показывают межквартильный разброс.
Термины адаптеров. HCA (Host Channel Adapter) - сетевой адаптер InfiniBand, ядро видит его как устройство mlx5_N. NDR = 400 Гбит/с на порт, HDR = 100 Гбит/с - поколения скорости IB. PIX-local означает, что адаптер и GPU висят под общим PCIe-switch: копия VRAM→HCA не пересекает межпроцессорную шину, что важно для GPUDirect. bond0 - агрегированный Ethernet-интерфейс узла (Linux bonding): два порта объединены по LACP 802.3ad в один логический канал 2 × 100G с MTU 9000. Счётчики bond0 и IB-портов mlx5_* независимы, поэтому по их дельтам однозначно определяется фактический маршрут KV (раздел 3.3).
Уровень | Инструмент | Наблюдаемые величины |
|---|---|---|
L0 |
| потолок фабрики, latency |
L0b |
| GDR/host staging, read/write, status |
L1 | IB counters, | дельта переданных байтов |
L2 |
| alloc/bootstrap KV-transfer |
L3 |
| TTFT, TPOT, ITL (inter-token latency), E2E, tok/s |
L0 и L0b измеряют железо и переносчик в изоляции (L0 - полосу IB утилитой ib_write_bw независимо от sglang; L0b - transfer_engine_bench без модели). L1-L3 - end-to-end: L1 по счётчикам портов доказывает, куда пошёл байт, L2 берёт телеметрию движка, L3 - пользовательские метрики. IB-счётчики PortXmitData/PortRcvData отдаются в 4-байтных словах (×4 для байт); величины ib_tx/bond_tx - приращение за прогон (after - before), а не абсолютное значение счётчика.
3. Результаты
3.1. End-to-end сравнение транспорта
При concurrency=8 и только на валидных прогонах получены следующие медианы. Здесь TTFT - время до первого токена, TPOT - время на последующие токены.
Профиль | IB TTFT, мс | TCP TTFT, мс | Выигрыш IB | IB tok/s | TCP tok/s | Выигрыш IB |
|---|---|---|---|---|---|---|
128/128 | 451.8 | 2427.8 | 5.4× | 414.7 | 128.4 | 3.2× |
2048/256 | 398.2 | 4575.5 | 11.5× | 519.2 | 182.2 | 2.8× |
8192/256 | 554.0 | 3215.5 | 5.8× | 495.7 | 130.4 | 3.8× |
Транспорт входит в TTFT через перенос KV и bootstrap, но не участвует в основном decode-цикле. Поэтому TPOT составил 10.8-11.7 мс для IB и 10.0-10.5 мс для TCP; небольшое преимущество TCP не является причинным эффектом и связано со смещением валидной выборки после исключения отказов.
3.2. Граница отказа TCP
Все 75 прогонов IB были валидны. Для TCP 44 из 75 прогонов были валидны, а 31 завершились отказом.
Профиль | c=1 | c=8 | c=32 |
|---|---|---|---|
128/128 | 0/5 bad | 0/5 bad | 0/5 bad |
2048/256 | 0/5 bad | 0/5 bad | 0/5 bad |
8192/256 | 0/5 bad | 0/5 bad | 1/5 bad |
2048/2048 | 5/5 bad | 5/5 bad | 5/5 bad |
32768/256 | 5/5 bad | 5/5 bad | 5/5 bad |
ClientSession::writeBody failed to copy from CUDA memory: out of memory
↓
KVTransferError: timeout after 300 s in KVPoll.WaitingForInput
↓
prefill: running_phase_sigquit_handler → kill_process_tree
↓
router: No available prefill workers
↓
HTTP 503Профиль 2048/2048 ломается уже при c=1, тогда как 8192/256 выдерживает c=8. Это согласуется с зависимостью от объёма KV на запрос, а не только от concurrency. Изолированный transfer_engine_bench с TCP и VRAM-буфером воспроизвёл тот же класс отказа.
3.3. Маршрут KV и утилизация фабрики
Транспорт |
|
|
|---|---|---|
IB | 3 654-206 714 МиБ | примерно 0-12 МиБ |
TCP | 0 | 3 642-55 158 МиБ (валидные прогоны) |
В самых тяжёлых валидных TCP-прогонах скорость по bond0 составляла 7.4-8.7 Гбит/с, до 4.4% от 200 Гбит/с. Для IB максимум составлял 61,7 Гбит/с (c=32), не более 2,1% от суммарного измеренного L0-потолка восьми NDR-портов - около 2933 Гбит/с. Гипотеза о насыщении LACP-хэша не подтверждена.
3.4. L0 и L0b: GPUDirect против host staging
Cross-node ib_write_bw на восьми рабочих NDR-портах дал среднюю скорость 366.7 Гбит/с при диапазоне 364.2-368.5 Гбит/с; latency на измеренных портах составляла 1.78-2.00 мкс.
Режим | GB/s | Гбит/с |
|---|---|---|
GDR write из VRAM | 48.82 | 390.56 |
host write из DRAM | 45.93 | 367.44 |
GDR read из VRAM | 49.00 | 392.00 |
host read из DRAM | 46.01 | 368.08 |
TCP write из VRAM | FAILED | - |
GDR составил 97.6% от номинальных 400 Гбит/с NDR и превысил host staging на 6.29% для write и 6.50% для read. L0 и L0b используют разные инструменты и режимы, поэтому их абсолютные значения нельзя смешивать как одно измерение.
3.5. Backend переноса: mooncake и nixl
M2 проводился на IB и холодном стеке. Поэтому значения TTFT сопоставимы только между backend'ами внутри M2.
Backend | Профиль | c | TTFT, мс | TPOT, мс | Output tok/s |
|
|---|---|---|---|---|---|---|
mooncake | 2048/256 | 8 | 4295.6 | 11.28 | 200.1 | 16 016 |
mooncake | 2048/256 | 32 | 1344.8 | 13.45 | 749.6 | 16 016 |
mooncake | 8192/256 | 8 | 3121.6 | 10.27 | 254.4 | 51 430 |
mooncake | 8192/256 | 32 | 3901.3 | 13.10 | 567.7 | 51 430 |
nixl | 2048/256 | 8 | 3775.9 | 11.37 | 194.4 | 15 862 |
nixl | 2048/256 | 32 | 4104.7 | 13.33 | 595.8 | 15 862 |
nixl | 8192/256 | 8 | 2065.4 | 10.64 | 268.4 | 50 938 |
nixl | 8192/256 | 32 | 4407.6 | 12.19 | 589.2 | 50 938 |
nixl работоспособен и использует сопоставимый IB-маршрут: различие ib_tx не превышало примерно 1%. fake не может использоваться как prefill backend в PD-режиме: sglang завершается assertion в pd_disaggregation_hook.py:70.
3.6. OFAT и attention backend
По оси D измерены 18 конфигураций sglang (n=2-3, с прогревом) на профиле 2048/256, c=8, IB, mooncake. Единственный выигрыш дал attention-backend triton; остальные 16 измеренных параметров базу не улучшили - их TTFT составил 435-562 мс против 418 мс у base (включая варианты max-running-req 8/16/32/64, mem-fraction 0.70-0.90, schedule-policy fcfs/random, chunked-prefill off/4096/16384, cuda-graph-* и max-total-tokens 500k). Ещё две конфигурации - cuda-graph-prefill full и attention-backend fa3 - стек не подняли (таймаут health) и в число 18 не входят. По оси C проверены 13 параметров mooncake на профиле 8192/256, c=8, IB; все рабочие варианты находились в диапазоне разброса M1, а MC_NUM_QP_PER_EP=4 приводил к зависанию decode.
Профиль |
|
| Δ TTFT | Δ throughput |
|---|---|---|---|---|
2048/256 | 536.4 мс | 298.3 мс | -44.4% | +19.9% |
8192/256 | 578.7 мс | 384.7 мс | -33.5% | +17.7% |
В обоих режимах завершились 32/32 запроса. При temperature=0 два повторения детерминированного semantic check дали идентичные корректные ответы. Однако fa4 использовал page_size=128, а triton - page_size=None; результат не доказывает эквивалентность на длинных контекстах или при speculative decoding.
4. Обсуждение
4.1. Почему эффект транспорта виден в TTFT, но не в TPOT
В PD-disaggregation decode не может начать полноценную генерацию до получения KV-кэша. Поэтому задержка переноса прибавляется ко времени первого токена. После bootstrap decode выполняет вычисления локально; транспорт уже не участвует в каждом шаге генерации. Наблюдаемая разница - секунды по TTFT против менее 1.7 мс/токен по TPOT - соответствует этой структуре пути.
4.2. Почему скорость линии не объясняет результат
Если бы TCP упирался в физическую полосу или LACP-хэш, end-to-end поток должен был бы приближаться к потолку bond0. Наблюдалось обратное: максимум 8.7 Гбит/с составлял лишь 4.4% от 200 Гбит/с. Наиболее согласованная с логами и L0b интерпретация - различие software path: TCP требует копирования KV из VRAM в host bounce-buffer, а GPUDirect RDMA передаёт KV без промежуточного host staging.
4.3. Практическое значение результата triton
Выигрыш triton существенно превышал эффекты микро-тюнинга mooncake, но получен сменой вычислительного пути attention, а не оптимизацией транспорта. Перед production-переключением требуются тесты на реальных распределениях контекстов, длинных цепочках, p99, потреблении памяти, качестве ответов и speculative decoding.
5. Ограничения и угрозы валидности
Ограниченный набор транспортов. Сравнивались только RDMA/InfiniBand с GPUDirect и TCP/Ethernet; иные варианты транспорта KV не исследовались.
Масштаб decode. Использовался один decode-узел; влияние числа decode workers и балансировки не исследовалось.
Смещение TCP-выборки. 31 TCP-прогон завершился отказом и исключён из агрегатов производительности. Failure rate приводится отдельно.
Холодный M2. Абсолютные TTFT M2 нельзя сравнивать с прогретым M1; M2 предназначен для относительного сравнения backend'ов.
Малые выборки tuning. В осях C/D и attention verification использовалось 2-3 тёплых повтора на конфигурацию.
Attention layouts.
fa4иtritonнеэквивалентны на уровне paged/non-paged KV layout.Метрики движка и дельты.
sglang:kv_transfer_*- монотонные счётчики Prometheus, поэтому в харнессе берётся дельтаafter - beforeза прогон (чтение_afterдаёт lifetime-среднее).kv_transfer_speed_gb_s/_latency_ms/_total_mbотдаёт передающая (prefill) сторона,_bootstrap_ms/_alloc_ms- принимающая (decode). Скорость KV подтверждается двумя независимыми источниками - метриками движка и Δ портовых счётчиков, - абсолюты которых расходятся.Конкретная сборка. Вывод о TCP относится к MiniMax-M3, данной версии sglang/mooncake и конфигурации памяти.
Конфаундеры модели. Перенос результата на другую точность, модель или layout требует отдельного эксперимента.
6. Что забрать в свой кластер
Измерять end-to-end
Считать TTFT, TPOT и failure rate одновременно.
Проверять маршрут KV дельтой счётчиков IB и Ethernet после тестового запроса.
Включать длинный input, длинный output и их сочетание.
Не портить benchmark
Прогревать стек и исключать cold-run: 4,1-4,3 с против 0,41-0,43 с TTFT.
Ставить health-gate после каждого прогона и не измерять мёртвый prefill.
Разделять transport tuning и смену attention backend.