【大模型推理】sglang PD分离 单节点通信方式
SGLang + Mooncake PD分离 & RDMA/PCIe/NVLink 完整笔记
1. SGLang 源码安装确认
SGLang 是以 editable 模式(pip install -e)安装的。
确认方式:查看 pip 的 direct_url.json 文件:
/home/admin/miniconda3/lib/python3.10/site-packages/sglang-0.5.11.dist-info/direct_url.json
内容:
{"dir_info": {"editable": true}, "url": "file:///home/admin/workspace/aop_lab/app_source/code/public/sglang/python"}
关键证据:
"editable": true— 说明是用pip install -e方式安装的(开发/源码模式)url指向本地路径 — 而不是从 PyPI 下载的包
如果是普通 pip install sglang 安装的,这个文件要么不存在,要么不会有 editable: true 字段。
2. 源码更新是否需要重新 pip install -e
大部分情况下不需要重新 pip install -e。
editable 模式下,Python 代码的修改会立即生效,因为 import 时直接读取源码目录。
但以下情况需要重新运行 pip install -e:
setup.py/pyproject.toml中的依赖项变了(新增了 requires)- C/C++ 扩展(如 CUDA kernel)有改动,需要重新编译
- entry_points(命令行入口)有变化
简单来说:纯 Python 改动不用管,涉及编译或包配置变动就需要重新装。
3. Mooncake 源码安装
Mooncake(mooncake-transfer-engine)已克隆到:
/home/admin/workspace/aop_lab/app_source/code/public/mooncake/
以 editable 模式安装:
cd /home/admin/workspace/aop_lab/app_source/code/public/mooncake/mooncake-wheel
pip install -e .
版本:0.3.11.post1,包名:mooncake-transfer-engine。
4. SGLang 与 Mooncake 配合使用
sglang 已内置 mooncake 集成,主要有三个使用场景:
4.1 PD 分离(Prefill-Decode disaggregation)
- 路径:
sglang/srt/disaggregation/mooncake/ - 用 mooncake transfer engine 在 Prefill 节点和 Decode 节点之间传输 KV Cache
4.2 KV Cache 外部存储
- 路径:
sglang/srt/mem_cache/storage/mooncake_store/ - 用 mooncake store 做 KV Cache 的分布式存储后端
4.3 MoE Expert Parallelism
- 路径:
sglang/srt/layers/moe/token_dispatcher/mooncake.py - 用 mooncake 做 MoE token dispatch
5. 单机8卡 PD 分离
单机 8 卡完全可以做 PD 分离。比如 4 卡 Prefill + 4 卡 Decode。
模型路径:
/home/admin/workspace/aop_lab/app_source/code/public/model/DeepSeek-V4-Flash
CI 参考脚本(sglang/scripts/ci/cuda/ci_start_disaggregation_servers.sh)展示了标准做法:GPU 0-3 做 Prefill,GPU 4-7 做 Decode。
每个 GPU 启动一个独立的 sglang server 实例:
# Prefill (GPU 0-3)
CUDA_VISIBLE_DEVICES=0 python3 -m sglang.launch_server \
--model-path $MODEL_PATH \
--disaggregation-mode prefill \
--host 127.0.0.1 --port 30001 \
--disaggregation-bootstrap-port 9001
# Decode (GPU 4-7)
CUDA_VISIBLE_DEVICES=4 python3 -m sglang.launch_server \
--model-path $MODEL_PATH \
--disaggregation-mode decode \
--host 127.0.0.5 --port 30005 \
--base-gpu-id 0
关键点:
- 单机用
127.0.0.x不同 IP 区分实例(或用不同端口) - 传输后端默认就是 mooncake(
disaggregation_transfer_backend: str = "mooncake") - 如果有 IB/RDMA 网卡加
--disaggregation-ib-device,没有的话 mooncake 也支持 TCP fallback - 比例可以灵活调,不一定 4:4,根据业务负载决定
6. IB 和 RDMA 的关系
- RDMA(Remote Direct Memory Access)是一种技术,允许网卡直接读写远端内存,绕过 CPU 和内核,延迟极低
- IB(InfiniBand)是 RDMA 的一种网络协议/物理层
- RoCE(RDMA over Converged Ethernet)是另一种,在以太网上跑 RDMA
本机网卡是 Mellanox ConnectX-6(MT4126),Link layer 是 Ethernet,所以用的是 RoCE(以太网上的 RDMA),不是传统 IB。但对 mooncake 来说效果一样。
活跃的 RDMA 设备
从 32 个端口中,有 4 个是 Active 的:
| 设备 | 状态 | 速率 |
|---|---|---|
mlx5_4 |
Active/LinkUp | 200 Gbps |
mlx5_8 |
Active/LinkUp | 200 Gbps |
mlx5_24 |
Active/LinkUp | 200 Gbps |
mlx5_29 |
Active/LinkUp | 200 Gbps |
7. GPU NVLink 拓扑
所有 8 个 GPU 之间都是 NV18(18 条 NVLink bonded)全互联,单向带宽约 900 GB/s,这是 H800/H100 级别的配置。
8. 完整机器拓扑图
═══════════════════════════════════════════════════════════════════════════════════
NVSwitch (NV18 全互联, 900GB/s)
GPU0 ═══ GPU1 ═══ GPU2 ═══ GPU3 ═══ GPU4 ═══ GPU5 ═══ GPU6 ═══ GPU7
║ ║ ║ ║ ║ ║ ║ ║
═══════════════════════════════════════════════════════════════════════════════════
║ ║ ║ ║ ║ ║ ║ ║
PCIe PCIe PCIe PCIe PCIe PCIe PCIe PCIe
║ ║ ║ ║ ║ ║ ║ ║
▼ ║ ▼ ║ ▼ ║ ▼ ║
┌─────────┐ ║ ┌─────────┐ ║ ┌─────────┐ ║ ┌─────────┐ ║
│PCIe SW 0│ ║ │PCIe SW 1│ ║ │PCIe SW 2│ ║ │PCIe SW 3│ ║
│ │ ║ │ │ ║ │ │ ║ │ │ ║
│ GPU0 │ ║ │ GPU2 │ ║ │ GPU4 │ ║ │ GPU6 │ ║
│ NIC0~7 │ ║ │ NIC8~15 │ ║ │NIC16~23 │ ║ │NIC24~31 │ ║
│(mlx5_0 │ ║ │(mlx5_8 │ ║ │(mlx5_16 │ ║ │(mlx5_24 │ ║
│ ~7) │ ║ │ ~15) │ ║ │ ~23) │ ║ │ ~31) │ ║
│ │ ║ │ │ ║ │ │ ║ │ │ ║
│ 活跃: │ ║ │ 活跃: │ ║ │ 活跃: │ ║ │ 活跃: │ ║
│ mlx5_4✓ │ ║ │ mlx5_8✓ │ ║ │ 无 ✗ │ ║ │mlx5_24✓ │ ║
│ │ ║ │ │ ║ │ │ ║ │mlx5_29✓ │ ║
└────┬────┘ ║ └────┬────┘ ║ └────┬────┘ ║ └────┬────┘ ║
│ ║ │ ║ │ ║ │ ║
▼ ▼ ▼ ▼ ▼ ▼ ▼ ▼
┌──────────────────────────────────────────────────────────────────────────────────┐
│ │
│ CPU (PCIe Host Bridge) │
│ │
│ GPU1、GPU3、GPU5、GPU7 直接连到 CPU,没有共享 PCIe Switch 的网卡 │
│ 它们要访问任何 NIC 都必须:GPU → CPU → PCIe Switch → NIC (NODE路径) │
│ │
└──────────────────────────────────────────────────────────────────────────────────┘
9. NIC 与 GPU 的完整亲和关系
| GPU | 直连 NIC 组(PIX) | 组内活跃网卡 | 到其他活跃网卡 |
|---|---|---|---|
| GPU0 | mlx5_0~7 | mlx5_4 ✓ | 其余 NODE |
| GPU1 | 无(全是 NODE) | 无 | 全部 NODE |
| GPU2 | mlx5_8~15 | mlx5_8 ✓ | 其余 NODE |
| GPU3 | 无(全是 NODE) | 无 | 全部 NODE |
| GPU4 | mlx5_16~23 | 无(都没激活) | 全部 NODE |
| GPU5 | 无(全是 NODE) | 无 | 全部 NODE |
| GPU6 | mlx5_24~31 | mlx5_24 ✓, mlx5_29 ✓ | 其余 NODE |
| GPU7 | 无(全是 NODE) | 无 | 全部 NODE |
10. PCIe 概念解释
PCIe(Peripheral Component Interconnect Express)是主板上连接各种硬件设备的高速总线。
┌─────────────────────────────────────────────┐
│ CPU │
│ (中央处理器) │
└──────┬──────────┬──────────┬────────────────┘
│ PCIe │ PCIe │ PCIe
▼ ▼ ▼
┌─────┐ ┌─────┐ ┌──────┐
│ GPU │ │ NIC │ │ SSD │
│显卡 │ │网卡 │ │硬盘 │
└─────┘ └─────┘ └──────┘
拓扑矩阵中 PCIe 相关缩写
| 缩写 | 含义 | 距离 |
|---|---|---|
| PIX | 同一个 PCIe Switch 下面 | 最近,一跳 |
| PXB | 跨多个 PCIe Bridge | 稍远 |
| PHB | 要经过 PCIe Host Bridge(CPU) | 更远 |
| NODE | 同一个 NUMA 节点内,但跨 PCIe Host Bridge | 远 |
| SYS | 跨 NUMA 节点(跨 CPU socket) | 最远 |
PIX 含义
PIX = PCI Interconnect via one bridge(经过最多一个 PCIe 桥)
两个设备插在同一块 PCIe Switch 板子上,是物理距离最近的 PCIe 连接关系。
┌──────────────────────┐
│ PCIe Switch 0 │ ← 一块芯片/板卡
├──────────┬───────────┤
│ 插槽A │ 插槽B │
│ │ │
│ GPU0 │ NIC0~7 │ ← 两个设备插在同一个Switch下
└──────────┴───────────┘
GPU0 到 NIC0~7 只过一个桥 = PIX
对比 NODE:
┌───────────┐ ┌───────────┐
│PCIe Switch│ │PCIe Switch│
│ GPU1 │ │ NIC0~7 │
└─────┬─────┘ └─────┬─────┘
│ │
▼ ▼
┌────────────────────────────────────┐
│ CPU │ ← 要绕过CPU才能互通
└────────────────────────────────────┘
GPU1 到 NIC0~7 要经过CPU = NODE(慢)
核心区别:
- PIX:数据直接在 Switch 内部转发,CPU 不参与
- NODE:数据要上行到 CPU,再下行到另一个 Switch,CPU 参与转发,延迟更高
PCIe 路由
PCIe 是一个树状网络,数据包从一个设备发到另一个设备,需要经过中间节点转发,这个转发过程就叫"路由"。
跟网络路由器转发 IP 包是一个概念——PCIe 包也有地址,经过每个节点时决定往哪个方向转发。
PIX 路径(Switch 内部路由):
┌──────────────────────────────┐
│ PCIe Switch 0 │
│ │
│ 端口A 路由表 端口B │
│ GPU0 ──→ [查表:去端口B] ──→ mlx5_4 │
│ │
└──────────────────────────────┘
数据包: GPU0发出 → Switch查路由表 → 发现目标在端口B → 直接转到mlx5_4
路由只在Switch芯片内部完成,一跳到达
NODE 路径(跨 Switch,CPU 芯片做路由):
┌────────────┐ ┌─────────────────┐ ┌────────────┐
│PCIe Switch2│ │ CPU 芯片 │ │PCIe Switch3│
│ │ │ │ │ │
│ GPU4 │──PCIe──→ PCIe Root Complex ──PCIe──→ mlx5_24 │
│ │ │ (路由转发器) │ │ │
└────────────┘ └─────────────────┘ └────────────┘
数据包: GPU4发出 → 到达CPU芯片的PCIe Root Complex → 查路由表 → 转发到Switch3 → mlx5_24
CPU芯片充当了"路由器"角色,但CPU核心(运算单元)没有干活
类比:
- PCIe Switch = 立交桥(车辆直接转向,不用停)
- CPU PCIe Root Complex = 收费站/中转站(车辆要经过它才能到另一条路)
- GPU / NIC = 出发地和目的地
- 路由 = 在每个路口决定走哪条路
11. 各种传输方式对比(以本机器举例)
11.1 NVLink(GPU ↔ GPU 直连)
GPU0 ════════NV18════════► GPU4
18条NVLink绑定直连
带宽: ~900 GB/s (双向)
延迟: ~1μs
场景: GPU间直接传tensor/KV Cache,比如TP(Tensor Parallel)通信
特点: 不经过PCIe,不经过CPU,不经过网卡,纯GPU互联
本机所有 GPU 对之间都是 NV18,全互联,任意两卡间都是这个速度。
11.2 PCIe P2P(GPU → PCIe → GPU)
本机没有这种情况。因为每个 PCIe Switch 上只挂了 1 个 GPU,没有两个 GPU 共享同一个 Switch。GPU 间通信全走 NVLink,不需要 PCIe P2P。
(PCIe P2P 常见于消费级多卡,比如两张 4090 插在同一个 PCIe Switch 下,没有 NVLink 的情况)
11.3 TCP loopback(走内核网络协议栈)
Prefill进程(GPU0) Decode进程(GPU4)
GPU0 显存 GPU4 显存
│ cudaMemcpy D2H ▲ cudaMemcpy H2D
▼ │
CPU内存(用户态buffer) CPU内存(用户态buffer)
│ send()系统调用 ▲ recv()系统调用
▼ │
Linux内核TCP协议栈 Linux内核TCP协议栈
│ │
└──────── 127.0.0.1 loopback ──────────────────┘
带宽: ~10-20 GB/s (受限于内存拷贝和内核开销)
延迟: ~10-50μs
路径: GPU显存 → PCIe → CPU内存 → 内核拷贝 → 内核协议栈 → CPU内存 → PCIe → GPU显存
特点: 多次内存拷贝,内核上下文切换,CPU参与全程
这是 mooncake 不配 RDMA 时的 fallback 方式——最慢但无需特殊硬件。
11.4 RDMA / RoCE(绕过 CPU)
Prefill进程(GPU0) Decode进程(GPU4)
GPU0 显存 GPU4 显存
│ GPUDirect RDMA ▲ GPUDirect RDMA
▼ │
mlx5_4 网卡 ════ RoCE 200Gbps ════════════► mlx5_24 网卡
(PCIe Switch 0) (PCIe Switch 3)
带宽: ~25 GB/s (200Gbps 单口)
延迟: ~2-5μs
路径: GPU显存 → PCIe → RDMA网卡 → 网卡直接写入 → 对端RDMA网卡 → PCIe → GPU显存
特点: 零拷贝,CPU不参与数据搬运,内核不介入
11.5 本机对比总结
| 方式 | 本机路径举例 | 带宽 | 延迟 | CPU参与 |
|---|---|---|---|---|
| NVLink | GPU0 → GPU4 | ~900 GB/s | ~1μs | 不参与 |
| RDMA (RoCE) | GPU0→mlx5_4→mlx5_24→GPU4 | ~25 GB/s | ~2-5μs | 不参与 |
| TCP loopback | GPU0→CPU→内核→CPU→GPU4 | ~10-20 GB/s | ~10-50μs | 全程参与 |
12. RDMA vs TCP 详细对比(GPU 视角)
TCP 方式:GPU0 发 KV Cache 给 GPU4
GPU0 显存 [KV Cache数据]
│
│ cudaMemcpy D2H ← CPU核心发起DMA,数据拷贝到CPU内存
▼
CPU内存 [数据副本]
│
│ send() 系统调用 ← CPU核心陷入内核
▼
Linux内核 TCP协议栈 ← CPU核心跑代码:分包、校验和、拥塞控制
│
│ 内核再拷贝到socket buffer ← CPU核心做memcpy
▼
网卡发出 → 127.0.0.1 → 网卡收到
│
│ 内核收包、TCP协议处理 ← CPU核心跑代码
│ 拷贝到用户态buffer ← CPU核心做memcpy
▼
CPU内存 [数据副本]
│
│ cudaMemcpy H2D ← CPU核心发起DMA,数据拷贝到GPU显存
▼
GPU4 显存 [收到KV Cache]
CPU核心全程忙碌:拷贝、协议处理、系统调用、中断处理
RDMA 方式:GPU0 发 KV Cache 给 GPU4
GPU0 显存 [KV Cache数据]
│
│ 网卡DMA直接读取GPU显存(GPUDirect RDMA)
▼
mlx5_4 网卡硬件
│ 网卡自己完成:分包、协议封装、发送、等待确认
│ (这些全是网卡芯片内部的硬件逻辑做的)
▼
网线/交换机
│
▼
mlx5_24 网卡硬件
│ 网卡自己完成:收包、协议校验、重组
│ 网卡DMA直接写入GPU显存(GPUDirect RDMA)
▼
GPU4 显存 [收到KV Cache]
CPU核心只在最开头做了一件事:
告诉mlx5_4 "从GPU0显存地址0x...读N字节,发到对端mlx5_24,写入GPU4显存地址0x..."
然后CPU就不管了,全是网卡硬件自己干完的
对比
| TCP | RDMA | |
|---|---|---|
| CPU核心干了什么 | 拷贝数据、跑协议栈、处理中断、系统调用 | 只发一条指令 |
| 数据经过CPU内存 | 是,至少2次 | 不经过(GPUDirect) |
| 谁在做数据搬运 | CPU | 网卡硬件DMA引擎 |
| GPU→GPU总拷贝次数 | 3-4次 | 0次 |
一句话:TCP 是 CPU 当搬运工,RDMA 是网卡当搬运工。
13. RDMA 三个关键优势
13.1 内核旁路(Kernel Bypass)
TCP: 应用 → 系统调用 → 内核态 → 协议栈 → 驱动 → 网卡
RDMA: 进程 → 直接写网卡寄存器 → 网卡
TCP 每次收发都要 用户态↔内核态 切换,RDMA 完全在用户态完成。
13.2 CPU 卸载(CPU Offload)
| TCP | RDMA | |
|---|---|---|
| 协议处理 | CPU 执行 | 网卡硬件执行 |
| 数据拷贝 | CPU 执行 | 网卡 DMA 引擎执行 |
| 发 1GB 数据时 CPU 占用 | 很高 | 接近零 |
13.3 零拷贝(Zero Copy)
TCP (发1GB KV Cache):
GPU显存 →拷贝→ CPU内存 →拷贝→ 内核buffer →拷贝→ 网卡buffer → 发出
共 3 次拷贝
RDMA + GPUDirect (发1GB KV Cache):
GPU显存 → 网卡直接DMA读取 → 发出
0 次拷贝
14. cudaMemcpy D2H 的完整过程
第一步:CPU 核心发指令(很快,纳秒级)
CPU核心
│
│ 程序调用 cudaMemcpy(dst_cpu, src_gpu, size, D2H)
│ NVIDIA驱动把这个请求写入GPU的控制寄存器(通过PCIe MMIO)
│ 告诉GPU的DMA引擎:"从你的显存地址X读N字节,写到CPU内存地址Y"
│
▼
CPU核心的工作到此结束,接下来全是GPU的DMA引擎在干活
第二步:GPU 的 DMA 引擎搬运数据(耗时,毫秒级)
┌──────────────┐ ┌─────────────┐ ┌──────┐
│ GPU0 │ │ │ │ │
│ │ │ PCIe Switch │ │ CPU │
│ HBM显存 ──DMA读取──→ ──PCIe写──→ 内存 │
│ [KV Cache] │ │ 0 │ │控制器│──→ DRAM
│ │ │ │ │ │
│ DMA引擎 │ │ │ │ │
│ (硬件自动搬) │ │ │ │ │
└──────────────┘ └─────────────┘ └──────┘
具体经过:
1. GPU HBM(高带宽显存)
│ GPU内部总线读取,带宽~3TB/s
▼
2. GPU 的 DMA 引擎
│ 把数据打包成 PCIe TLP(Transaction Layer Packet)
│ TLP里写着:"这是一个Memory Write请求,目标地址=CPU内存地址Y"
▼
3. GPU 的 PCIe 端口(Endpoint)
│ 发出PCIe数据包
▼
4. PCIe Switch 0(本机GPU0挂在这里)
│ Switch查路由表,发现目标地址在上游(CPU方向),转发
▼
5. CPU 芯片的 PCIe Root Complex
│ 收到PCIe写请求,解析目标内存地址
▼
6. CPU 的内存控制器
│ 把数据写入DRAM对应地址
▼
7. DRAM(CPU内存)
[数据到达]
谁在干活
| 组件 | 做了什么 |
|---|---|
| CPU 核心 | 只在开头写了几个寄存器(发指令),之后空闲/去干别的 |
| GPU DMA 引擎 | 真正的搬运工,从HBM读数据,组装PCIe包,持续发送 |
| PCIe Switch | 路由转发,硬件自动完成 |
| CPU 内存控制器 | 接收PCIe写请求,写入DRAM,硬件自动完成 |
CPU 核心本身不搬数据,是 GPU 的 DMA 引擎通过 PCIe 主动把数据"推"到 CPU 内存里。CPU 核心只是在开头说了一句"开始搬",然后就可以去做别的事了。
15. 普通 RDMA vs GPUDirect RDMA
两个独立维度
维度1 - 网络协议(数据怎么在网线上传):
| 协议 | 跑在什么网络上 |
|---|---|
| InfiniBand (IB) | 专用 IB 网络、IB 交换机 |
| RoCE | 普通以太网、普通交换机 |
维度2 - 内存访问方式(网卡从哪里读 GPU 数据):
| 方式 | 网卡能直接读 GPU 显存吗 |
|---|---|
| 普通 RDMA | 不能,必须先拷到 CPU 内存 |
| GPUDirect RDMA | 能,网卡直接 DMA 读写 GPU 显存 |
两个维度独立组合:
普通RDMA GPUDirect RDMA
(经过CPU内存) (不经过CPU内存)
┌─────────────────┬─────────────────────┐
InfiniBand │ IB + 普通 │ IB + GPUDirect │
├─────────────────┼─────────────────────┤
RoCE(以太网) │ RoCE + 普通 │ RoCE + GPUDirect │
└─────────────────┴─────────────────────┘
RoCE 不等于普通 RDMA。RoCE 是网络协议,普通/GPUDirect 是内存访问方式。
普通 RDMA(没有 GPUDirect)
数据要先经过 CPU 内存做中转:
发送端:
GPU0 显存 ──cudaMemcpy──→ CPU内存 ──RDMA发送──→ mlx5_4 网卡
接收端:
mlx5_24 网卡 ──RDMA接收──→ CPU内存 ──cudaMemcpy──→ GPU4 显存
即使 GPU 和网卡在同一个 PCIe Switch(PIX),网卡也不会去读 GPU 显存——网卡驱动不认识 GPU 显存地址。软件必须先 cudaMemcpy 到 CPU 内存,网卡再从 CPU 内存读。
GPUDirect RDMA(网卡直接读写 GPU 显存)
发送端:
GPU0 显存 ──────DMA──────→ mlx5_4 网卡
网卡直接从GPU显存读取,不经过CPU内存
接收端:
mlx5_24 网卡 ──────DMA──────→ GPU4 显存
网卡直接写入GPU显存,不经过CPU内存
对比
| 普通 RDMA | GPUDirect RDMA | |
|---|---|---|
| 路径 | GPU→CPU内存→网卡 | GPU→网卡(直达) |
| 内存拷贝 | 2次(D2H + H2D) | 0次 |
| CPU内存带宽占用 | 有 | 无 |
| 延迟 | 较低 | 最低 |
| 要求 | RDMA网卡 | RDMA网卡 + nvidia_peermem模块 |
16. 本机 GPUDirect RDMA 支持确认
本机完全支持 GPUDirect RDMA,所有必要组件已加载:
| 组件 | 状态 | 作用 |
|---|---|---|
nvidia_peermem |
✓ 已加载 | 让 RDMA 网卡直接访问 GPU 显存的核心模块 |
gdrdrv |
✓ 已加载 | GDRCopy 驱动,用户态直接访问 GPU 显存 |
mlx5_ib |
✓ 已加载 | Mellanox 网卡的 RDMA/IB 驱动 |
ib_core |
✓ 已加载 | Linux RDMA 核心框架 |
| NVIDIA 驱动 | 580.82.07 | 支持 peermem |
不需要额外配置。nvidia_peermem 模块已经加载,mooncake 会自动检测并使用 GPUDirect RDMA。
只要启动 sglang 时加上 --disaggregation-ib-device mlx5_24,mooncake 就会自动走 GPUDirect RDMA 路径。开箱即用。
17. 对 PD 分离的意义
单机内 Prefill GPU 把 KV Cache 传给 Decode GPU:
- 最理想:如果 mooncake 能直接利用 NVLink(NCCL/cuMemcpy),那是 900 GB/s
- 用 RDMA:mooncake 走 RoCE + GPUDirect RDMA 网卡,25 GB/s,比 TCP 快但远不如 NVLink
- 用 TCP:最慢的 fallback
mooncake 作为进程间传输引擎,在单机上主要走 RDMA 或 TCP。NVLink 直连通常是同一个进程内(如 NCCL all-reduce)才用。PD 分离因为是不同进程,所以用 mooncake 的 RDMA 通道是最佳选择。
18. 为什么 PD 分离不用 NVLink 传输
原因
-
PD 分离是不同进程:Prefill 和 Decode 是独立的 sglang server 进程,各有自己的 CUDA Context,不能像同进程内 NCCL 那样直接走 NVLink。
-
设计目标是跨机器:PD 分离核心场景是 Prefill 和 Decode 在不同机器上,NVLink 只能连同一台机器内的 GPU。mooncake/nixl 用 RDMA 是为了同一套代码既能跨机也能单机。
-
NVLink 跨进程需要特殊机制:比如 CUDA IPC 或 Mooncake 的 NVLink Allocator,需要额外的内存映射。
Mooncake NVLink Transport(开发中)
Mooncake 正在开发 NVLink 传输模式,代码里标注了 TODO:
# TODO(shangming): Fix me (use 'cuda') when nvlink_transport of Mooncake is bug-free
通过环境变量启用(目前不稳定):
export SGLANG_MOONCAKE_CUSTOM_MEM_POOL=NVLINK
源码位置:
mooncake-transfer-engine/src/transport/nvlink_transport/nvlink_transport.cppmooncake-transfer-engine/nvlink-allocator/nvlink_allocator.cppmooncake-integration/allocator.py
等 NVLink transport 稳定后,单机 PD 分离性能将从 25 GB/s 提升到 900 GB/s。
相关 GitHub 链接
- Mooncake: https://github.com/kvcache-ai/Mooncake
- SGLang: https://github.com/sgl-project/sglang
- NIXL: https://github.com/ai-dynamo/nixl
19. NIXL 简介
NIXL(NVIDIA Inference Xfer Library)是 NVIDIA 开发的高性能数据传输库,专门用于 LLM 推理场景中 GPU 之间的 KV Cache 传输。开源,Apache 2.0 协议。
| Mooncake | NIXL | |
|---|---|---|
| 开发方 | kvcache.ai(社区) | NVIDIA |
| 传输方式 | RDMA (RoCE/IB) + TCP fallback | RDMA (RoCE/IB) + NVLink + GDRCopy |
| 安装难度 | pip 可装 | 需从源码编译 |
| sglang 集成 | 默认 backend | 可选 backend |
20. sglang-router 安装
Router 是 Rust 写的(maturin 构建 Python 绑定),需要单独安装。
源码编译需要 Rust 1.85+(本机 Rust 1.75 太旧),但可以直接 pip 安装预编译包:
pip install sglang-router
21. GPUDirect RDMA 无需手动配置
不需要在脚本里指定用普通 RDMA 还是 GPUDirect RDMA,由内核模块决定:
nvidia_peermem 已加载 → 自动用 GPUDirect RDMA(网卡直接读写GPU显存)
nvidia_peermem 未加载 → 自动退化为普通 RDMA(经过CPU内存中转)
如需手动切换:
# 卸载(强制用普通 RDMA)
sudo rmmod nvidia_peermem
# 加载(恢复 GPUDirect RDMA)
sudo modprobe nvidia_peermem
22. NODE 路径也支持 GPUDirect RDMA
GPUDirect RDMA 和 PCIe 物理路径(PIX/NODE)是两个独立的概念:
- GPUDirect = 网卡有能力寻址 GPU 显存(软件/驱动层的能力)
- PIX/NODE = 数据包走哪条物理路径(硬件拓扑)
NODE 路径下 GPUDirect RDMA 的数据流:
GPU显存 ──PCIe包──→ CPU Root Complex ──PCIe包转发──→ 网卡
(纯硬件路由转发,数据不落地到DRAM)
数据穿过 CPU 芯片但不停留在 CPU 内存。就像车经过收费站但不下高速。
23. RoCE 实际可用设备确认
检查发现只有 mlx5_24 可用于 RoCE 通信:
| RDMA 设备 | 物理状态 | 绑定网络接口 | GID | 可用于 RoCE |
|---|---|---|---|---|
| mlx5_4 | Active/LinkUp | 无 | 空 | ✗ |
| mlx5_8 | Active/LinkUp | 无 | 空 | ✗ |
| mlx5_24 | Active/LinkUp | eth0 (172.30.195.128) | 有 | ✓ |
| mlx5_29 | Active/LinkUp | 无 | 空 | ✗ |
RoCE 必须有 IP/GID 才能通信。mlx5_4/mlx5_8/mlx5_29 虽然物理链路 UP,但没有关联网络接口,无法用于 RoCE。
24. PD 分离启动脚本(最终版)
使用 Mooncake(推荐)
脚本路径:code/public/scripts/pd_disagg_mooncake.sh
关键配置:
MODEL_PATH="/home/admin/workspace/aop_lab/app_source/code/public/model/DeepSeek-V4-Flash"
IB_DEVICE="mlx5_24" # 唯一可用的 RoCE 设备
export SGLANG_HOST_IP="172.30.195.128" # mlx5_24 绑定的 eth0 IP
PREFILL_HOST="172.30.195.128" # Prefill 监听地址
DECODE_HOST="172.30.195.128" # Decode 监听地址(同机器同IP,不同端口)
架构:
GPU 0-3 (Prefill, TP=4) ──RDMA via mlx5_24──→ GPU 4-7 (Decode, TP=4)
│
Router (负载均衡, 端口8000)
│
用户请求入口
不使用 Mooncake
脚本路径:code/public/scripts/pd_disagg_no_mooncake.sh
使用 nixl 作为替代 transfer backend(需要单独安装 NVIDIA NIXL),或 fake 做功能测试。
单机 RDMA 回环说明
单机 PD 分离使用 RDMA 时,实际数据路径是:
Prefill (GPU0-3) → NODE路径 → mlx5_24 → RoCE本地回环 → mlx5_24 → NODE路径 → Decode (GPU4-7)
因为 Prefill 和 Decode 在同一台机器、使用同一张网卡,RDMA 走本地回环(loopback),数据不真正出网线,但仍享受 RDMA 的零拷贝和内核旁路优势。
更多推荐



所有评论(0)