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"}

关键证据:

  1. "editable": true — 说明是用 pip install -e 方式安装的(开发/源码模式)
  2. 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 传输

原因

  1. PD 分离是不同进程:Prefill 和 Decode 是独立的 sglang server 进程,各有自己的 CUDA Context,不能像同进程内 NCCL 那样直接走 NVLink。

  2. 设计目标是跨机器:PD 分离核心场景是 Prefill 和 Decode 在不同机器上,NVLink 只能连同一台机器内的 GPU。mooncake/nixl 用 RDMA 是为了同一套代码既能跨机也能单机。

  3. 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.cpp
  • mooncake-transfer-engine/nvlink-allocator/nvlink_allocator.cpp
  • mooncake-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 的零拷贝和内核旁路优势。

Logo

汇聚全球AI编程工具,助力开发者即刻编程。

更多推荐