fabric-lib论文详解:面向LLM系统的可移植RDMA点对点通信
本文最后更新于 2026年6月12日 下午
1. 先说结论
版本说明:本文写于 2026-06-12,主要参考 arXiv 上的 fabric-lib: RDMA Point-to-Point Communication for LLM Systems v2,论文标注日期为 2026-04-13,并结合 Perplexity AI 开源仓库 perplexityai/pplx-garden 的 fabric-lib 文档整理。fabric-lib 是一个还在快速演进的系统,尤其是 EFA、NIXL、DeepEP、云厂商 RDMA NIC 支持都变化很快,生产使用要以实际 commit、驱动、libfabric/libibverbs 版本和实例拓扑为准。
一句话概括:
fabric-lib 不是又一个 NCCL 替代品,而是 Perplexity 为新一代 LLM 系统补的一层“可靠但不要求有序”的 RDMA 点对点通信抽象。它把 NVIDIA ConnectX-7 和 AWS EFA 这两类语义很不一样的网卡统一到同一套 TransferEngine API 下面,用 WRITE_IMM + ImmCounter 作为跨硬件的完成通知机制,再在上面实现 P/D 分离 KV 传输、RL 权重同步、MoE dispatch/combine。
这篇论文的核心价值有三点:
- 它把 LLM serving 里越来越重要的 非 collective 通信 单独拎了出来。P/D 分离、MoE expert routing、异步 RL rollout 权重更新,都不是传统 all-reduce/all-gather/broadcast 能舒服表达的模式。
- 它没有选择只服务 ConnectX + IBGDA 的最强路径,而是选择了更“云原生”的抽象边界:可靠即可,不假设网络传输有序。这让 AWS EFA 的 SRD 协议也能进入同一套上层系统。
- 它用三个真实生产场景证明这层抽象不是玩具:400 Gbps 级别带宽、1T 参数模型约 1.3 秒权重更新、ConnectX-7 上超过 DeepEP decode latency、EFA 上第一次给出可用的 MoE 低延迟实现。
如果只记一个心智模型,可以这样理解:
NCCL / torch.distributed:
适合固定成员、固定模式、强同步的 collective。
fabric-lib TransferEngine:
适合动态 peer、稀疏目标、变长数据、one-sided 写入、无需全局 world 的 P2P 通信。
DeepEP:
针对 MoE all-to-all 极致优化,但强绑定 ConnectX / IBGDA / mlx5。
fabric-lib:
牺牲一部分“只为单一硬件榨干最后几微秒”的自由,换取 ConnectX 与 EFA 之间的可移植生产路径。先放一张总图:
flowchart LR
App[LLM System<br/>P/D KV, RL rollout, MoE] --> API[TransferEngine API]
API --> SendRecv[SEND / RECV<br/>small RPC messages]
API --> Write[WRITE / WRITE_IMM<br/>bulk data path]
API --> Scatter[SCATTER / BARRIER<br/>MoE peer group]
Write --> IC[ImmCounter<br/>order-agnostic completion]
Scatter --> IC
SendRecv --> Worker[DomainGroup Worker<br/>one worker per GPU/domain group]
IC --> Worker
Worker --> D0[Domain NIC 0]
Worker --> D1[Domain NIC 1]
Worker --> D2[Domain NIC 2]
D0 --> CX[ConnectX-7<br/>libibverbs RC]
D1 --> EFA[AWS EFA<br/>libfabric SRD]
D2 --> Other[other RC-like NICs]
2. 为什么 collective 不够用了
传统大模型训练和推理长期依赖 collective 通信:
- tensor parallel 需要 all-reduce / reduce-scatter。
- data parallel 需要 all-reduce 梯度或参数同步。
- pipeline parallel 需要相邻 stage 之间的 send/recv,但成员关系和顺序通常比较固定。
- serving 框架可以把同构 worker 放进同一个 process group,用 NCCL 或 torch.distributed 管起来。
这些模式的共同点是:参与者相对固定,操作顺序相对固定,buffer shape 相对固定。
LLM serving 的新模式正在打破这些前提。
P/D 分离里,请求先由 prefiller 处理长 prompt,再把 KV cache 交给 decoder 持续 decode。prefiller 和 decoder 的匹配可能由全局调度器动态决定,不一定属于同一个固定通信 world。每个请求的 prompt 长度不同,KV 页数量也不同。
MoE 推理里,每个 token 只路由到少数 expert。每一轮 dispatch/combine 的目标 rank 和数据量取决于路由结果,是稀疏、动态、变长的。强行用 dense collective 表达,会多传大量空数据。
异步 RL fine-tuning 里,训练集群和推理 rollout 集群分离。每个训练 step 后要把新权重推给推理 GPU。如果按 Rank0 聚合再广播,很容易被少数 NIC 限速;如果做全局通信组,又会把训练和推理的生命周期绑得太死。
所以论文真正的问题不是“RDMA 比 TCP 快”这种常识,而是:
能不能给 LLM 系统提供一套足够低延迟、足够高带宽、支持 GPU memory、支持动态 peer、还能跨 ConnectX 和 EFA 的 P2P 数据面?
fabric-lib 的答案是:只抽象两类各家 RDMA NIC 都能较好支持的能力。
- 两边参与的
SEND/RECV:用于小消息、控制消息、RPC 风格交互。 - 单边
WRITE_IMM:发送方直接把数据写到接收方注册内存,同时带一个 32-bit immediate 值作为完成通知。
它刻意不把 READ、atomic、全局有序性放进核心依赖里。因为这些能力不是所有硬件都高效,也不是 EFA/ConnectX 之间最稳的公共交集。
3. fabric-lib 的抽象边界:可靠,但不要求有序
论文把 RDMA transport 做了一个很关键的重新取舍。
ConnectX 常用 RC,也就是 Reliable Connection。它提供可靠传输、连接语义和有序交付。很多高性能库会直接依赖这些强语义,把软件协议做得很薄。
EFA 的 SRD 则更像云环境里的另一条路线:可靠、connectionless,但不保证有序。它可以靠多路径和云网络特性做扩展,但如果上层系统假设消息按发送顺序抵达,就会出现语义不匹配。
fabric-lib 的选择是:
不要依赖 ConnectX RC 的有序性;
把 ConnectX 也降到“可靠但无序”的抽象来用;
这样 EFA SRD 与 ConnectX RC 才能共享同一套上层协议。这个选择很重要。它把问题从“如何封装所有 RDMA 功能”变成“LLM P2P 系统真正需要哪些最小语义”。
论文中的 transport 能力可以压缩成下面这张表:
| 能力 | ConnectX RC | EFA SRD | fabric-lib 核心抽象 |
|---|---|---|---|
| 可靠传输 | 支持 | 支持 | 依赖 |
| 有序交付 | 支持 | 不支持 | 不依赖 |
| connection 语义 | 支持 | 不支持 | 不暴露给上层 |
| SEND/RECV | 支持 | 支持 | 暴露 |
| WRITE_IMM | 支持 | 支持 | 暴露 |
| READ / Atomic | 支持 | 支持或部分支持 | 不作为关键路径 |
这就是论文标题里 “point-to-point communication” 的实际含义:不是把 collective 做快,而是给动态、稀疏、跨节点、跨硬件的 peer-to-peer 数据流一个可移植的最小语义层。
4. TransferEngine 架构
fabric-lib 的核心组件叫 TransferEngine。论文里的 API 是伪代码,但已经能看出它的边界:
trait TransferEngine {
fn main_address() -> NetAddr;
fn reg_mr(ptr, len, device) -> (MrHandle, MrDesc);
fn submit_send(addr: NetAddr, msg: &[u8], cb: fn () -> ());
fn submit_recvs(len: u64, cnt: u64, cb: fn (&[u8]) -> ());
fn expect_imm_count(imm: u32, count: u32, cb: fn () -> ());
fn submit_single_write(len: u64, imm: Option<u32>,
src: (MrHandle, Offset), dst: (MrDesc, Offset), done: OnDone);
fn submit_paged_writes(page_len: u64, imm: Option<u32>,
src: (MrHandle, Pages), dst: (MrDesc, Pages), done: OnDone);
fn add_peer_group(addrs: Vec<NetAddr>) -> PeerGroupHandle;
fn submit_scatter(...);
fn submit_barrier(...);
fn alloc_uvm_watcher(cb: fn(u64, u64) -> ()) -> NonNull<u64>;
}这套接口里有几个设计点值得单独拆开。
4.1 Memory Region:注册内存是所有 zero-copy 的起点
RDMA 不能随便访问任意虚拟地址。发送方和接收方都需要先把 host buffer 或 GPU buffer 注册成 memory region。注册后本地拿到 MrHandle,远端可以拿到可序列化的 MrDesc,里面包含远端地址、rkey 和每个 NIC 对应的访问描述。
这一步让上层可以做两件事:
- 把
MrDesc通过普通控制消息发给 peer。 - 后续数据面直接走 one-sided WRITE,不再让接收方参与每次数据搬运。
在 P/D KV 传输里,decoder 先分配好目标 KV pages,把目标描述符和 page index 发给 prefiller。prefiller 计算完每层 KV 后,直接把对应页写进 decoder GPU memory。
4.2 DomainGroup:一个 GPU 对多个 NIC
TransferEngine 的线程模型围绕 DomainGroup 展开。一个 worker 负责一个 GPU 相关的 domain group,内部可以管理 1 到 4 个 NIC。
这个设计是为了兼容不同硬件拓扑:
- ConnectX-7 场景里,一个 400 Gbps NIC 就可能给一个 GPU 提供足够带宽。
- AWS p5 场景里,常见拓扑是每个 GPU 聚合多个 100 Gbps EFA。
- p5en 这类实例可以是每 GPU 两个 200 Gbps EFA。
如果 API 只暴露“一个 GPU 对一个 NIC”,EFA 上就很难打满 400 Gbps。fabric-lib 把 NIC 聚合和分片留在 TransferEngine 内部,上层只提交一次 paged write 或 scatter。
flowchart TD
App[Application Thread] --> Q[lock-free queue]
Q --> W[DomainGroup Worker<br/>pinned on NUMA-local CPU core]
W --> NIC0[Domain 0<br/>NIC 0]
W --> NIC1[Domain 1<br/>NIC 1]
W --> NIC2[Domain 2<br/>NIC 2]
W --> NIC3[Domain 3<br/>NIC 3]
W --> CQ[completion polling]
CQ --> IC[ImmCounter]
CQ --> CB[callback worker]
GPU[GPU memory / UVM watcher] <--> W
worker 被 pin 到设备所在 NUMA node 的 CPU core 上,domain 相关数据也在 pin 之后分配,减少跨 NUMA 内存访问。跨线程通信走 lock-free queue。这个工程细节决定了它不是一个简单的 RDMA wrapper,而是在按微秒级 tail latency 优化。
4.3 ImmCounter:无序传输下的完成通知
WRITE_IMM 会把 payload 写到远端 memory region,同时在接收端 completion queue 里产生一个 immediate value。fabric-lib 在接收端维护 ImmCounter:每收到一个 immediate,就给对应 key 的计数器加一。
上层可以这样表达同步:
decoder 知道一个请求会收到 N 个 KV page writes;
prefiller 每个 WRITE_IMM 都带同一个 imm;
decoder 调用 expect_imm_count(imm, N, callback);
当 ImmCounter 累积到 N,decoder 再开始 decode。这个机制不要求消息顺序。只要最终收到 N 个完成事件,就能推进状态机。对 EFA SRD 这种 unordered transport 来说,这比“等待第 i 个包之后再等第 i+1 个包”稳得多。
这里有一个容易忽略的正确性问题:payload 写的是 GPU memory,immediate completion 由 CPU 侧观察。如果 CPU 先看到 completion,GPU 是否一定能看到数据?
论文的解释依赖两个事实:
- RDMA 规范要求
WRITE_IMM的数据 payload 先于 immediate 发出。 - host-proxy 架构中,CPU 在看到 ImmCounter 达标后,再通过 kernel launch 或 GDRCopy 更新 GPU 可见 flag;这些 CPU-to-GPU 事务会排在先前 NIC-to-GPU writes 之后。
也就是说,fabric-lib 不要求 GPU 自己直接 poll NIC completion,而是通过 CPU proxy 和 PCIe ordering 把完成事件转成 GPU 可见状态。
4.4 UVM Watcher:让 GPU 进度驱动网络传输
P/D KV 传输和 MoE kernel 都需要“GPU 算到某个点后,CPU 立刻提交 RDMA 操作”。CUDA Graph 场景里不能随便插 host callback。fabric-lib 用 alloc_uvm_watcher 分配一块 Unified Virtual Memory,GPU kernel 更新这个 word,CPU 线程用 GDRCopy 轮询它。
这有点像一个轻量的 GPU-to-CPU doorbell:
sequenceDiagram
participant K as CUDA kernel
participant U as UVM watched word
participant C as CPU watcher thread
participant T as TransferEngine
participant N as NIC
K->>K: finish layer / prepare send buffer
K->>U: write progress counter
C->>U: poll via GDRCopy
C->>T: callback(old, new)
T->>N: post RDMA WRITE / SCATTER
论文里 UVM watcher 在 CUDA Graph 下的 callback latency 很低:Rust callback p50 约 6.2 us,p99 约 12.6 us;Python callback p50 约 9.3 us,p99 约 20.4 us,但最大值会到毫秒级,说明 Python 适合控制面或低频路径,不适合极致 tail latency 的关键路径。
5. 硬件后端:EFA 和 ConnectX 的不同优化
fabric-lib 的上层抽象统一,但底层 backend 仍然是硬件特化的。
5.1 AWS EFA:libfabric + SRD,多 NIC 聚合
EFA 通过 libfabric 暴露 SRD。fabric-lib 为每个 NIC 建一个 domain,再把多个 domain 放进同一个 domain group。由于 EFA 的一些行为与标准 RDMA 细节不同,例如 zero-sized immediate write 仍需要有效 descriptor,fabric-lib 在所有传输上都强制使用有效描述符。
为了降低提交成本,EFA backend 会做 work request templating:把 libfabric descriptor 中不变的字段预先填好并保留,真正提交时只填变化部分。
EFA 的特点是可用、云上容易获得、支持多路径,但小消息和提交延迟会更吃亏。论文里 256 KiB single write 在 EFA 上只有 54 Gbps,而 ConnectX-7 是 116 Gbps;但 paged write 到 64 KiB 时,EFA 可以到 364 Gbps,接近 400 Gbps 级别。
5.2 ConnectX-7:libibverbs + RC,主动放弃有序依赖
ConnectX backend 走 libibverbs。每个 peer 会用 UD queue pair 做 RC handshake,然后建立两组 RC queue pair:
- 一组用于
SEND/RECV。 - 一组用于
WRITE/WRITE_IMM。
分开的原因是 receive completion 和 write immediate completion 都会消耗 work request,如果混在同一条有序队列里,上层高层语义会互相干扰。
ConnectX backend 还用了两个典型优化:
- WR chaining:最多把 4 个 work request 通过
next指针连起来,减少 doorbell 次数。 IBV_ACCESS_RELAXED_ORDERING:允许 NIC 和 GPU memory 之间的 PCIe transaction 更宽松地乱序,提高延迟表现。
从工程角度看,这套设计很务实:上层统一语义,底层并不假装硬件没有差异。真正的可移植性不是“同一份 backend 跑所有网卡”,而是“同一份应用协议跑在各自调优过的 backend 上”。
6. 场景一:P/D 分离中的 KV Cache 传输
P/D 分离的基本流程是:
- 调度器为请求选择一个 prefiller 和一个 decoder。
- 请求先发给 decoder。
- decoder 预分配目标 KV pages 和额外上下文 buffer,例如 hidden states、logits。
- decoder 用
SEND把目标MrDesc、page indices、请求元信息发给 prefiller。 - prefiller 执行 chunked prefill,每层算完后通过 UVM watcher 触发
submit_paged_writes。 - prefiller 把 KV pages 直接 RDMA WRITE 到 decoder GPU memory。
- decoder 通过
expect_imm_count等待所有层/页完成,然后开始 decode。
sequenceDiagram
participant S as Global Scheduler
participant D as Decoder
participant P as Prefiller
participant TE as TransferEngine
participant G as GPU KV Cache
S->>D: route request(prefiller=P)
D->>D: allocate KV pages and context buffers
D->>P: SEND page indices + MrDesc
loop each prefill chunk/layer
P->>P: compute attention and KV
P->>TE: UVM watcher triggers paged writes
TE->>G: RDMA WRITE_IMM KV pages to decoder GPU
end
TE->>D: ImmCounter reaches expected count
D->>D: start decode
这里的关键不是“能传 KV”,而是这些生产细节:
- 按层传输:prefill 不必等所有层都算完才传,层级 pipeline 可以把网络传输隐藏在后续计算后面。
- 支持 CUDA Graph:UVM watcher 可以在 CUDA Graph 场景下工作,不破坏高性能推理路径。
- 支持不同 KV layout:MLA 可能复制 compressed KV;GQA 需要按 page offset 和 stride 选择 heads;KV layout 要尽量让连续 heads 对应连续内存,减少小 write。
- 取消语义必须严谨:decoder 取消请求后,prefiller 必须确认不再有 pending remote write,否则 decoder 复用 KV page 会被迟到的 RDMA write 污染。
- 故障检测靠 heartbeat:如果 prefiller 不响应,decoder timeout 后取消请求,因为后续 KV 已经不可能安全抵达。
评测里,Qwen3-235B、H200、TP4、每 GPU 2x200 Gbps EFA,P/D KV transfer 对 TTFT 的影响如下:
| Seqlen | 非分离 TTFT | 分离 TTFT | 每层 compute | 每层 transfer |
|---|---|---|---|---|
| 4K | 214 ms | 260 ms | 2.267 ms | 0.661 ms |
| 8K | 433 ms | 501 ms | 4.578 ms | 0.952 ms |
| 16K | 929 ms | 1042 ms | 9.860 ms | 1.610 ms |
| 32K | 2179 ms | 2317 ms | 13.295 ms | 1.606 ms |
| 64K | 5681 ms | 5852 ms | 20.344 ms | 1.611 ms |
| 128K | 16735 ms | 17056 ms | 34.895 ms | 1.609 ms |
论文认为 TTFT slowdown 主要不是 KV transfer 本身,而是他们的 inference engine 为最后一个 input token 做了一次额外 decode pass。因为每层 transfer 大约 0.6 到 1.6 ms,而每层 compute 随序列长度增长到 35 ms,网络传输基本被计算隐藏了。
这对做 P/D 架构的人有一个直接启发:KV 传输优化不只看总带宽,更要看能否按层、按 chunk 及时启动,让 transfer 落进 compute overlap 窗口。
7. 场景二:RL rollout 的权重更新
异步 RL fine-tuning 的典型架构是训练 GPU 和推理 GPU 分离:
training cluster:
负责 PPO/GRPO/其他 RL 更新,产生新权重
inference rollout cluster:
负责用当前策略生成样本
每隔一个或多个 training step:
training 把新权重推给 inference很多系统会走 Rank0 聚合再广播:
training ranks -> training subgroup Rank0 -> inference subgroup Rank0 -> inference ranks这个路径的问题是 Rank0 NIC 变成瓶颈,而且训练/推理两边要形成较重的 collective 通信关系。
fabric-lib 的 P2P 方案是:
- controller 收集训练 GPU 和推理 GPU 的参数 metadata,包括 name、shape、dtype、DTensor sharding。
- controller 计算静态 transfer schedule:哪个 training GPU 把哪个 parameter slice 发给哪个 inference GPU。
- schedule 广播给 training workers。
- 每个 training step 后,training GPU 直接 one-sided RDMA WRITE 到 inference GPU memory。
- inference 节点不需要主动参与每个权重 shard 的接收逻辑。
flowchart LR
C[Controller] --> M[collect param metadata]
M --> R[compute static routing schedule]
R --> T0[Training GPU 0]
R --> T1[Training GPU 1]
R --> Tn[Training GPU N]
T0 -->|RDMA WRITE| I0[Inference GPU 0]
T0 -->|RDMA WRITE| I1[Inference GPU 1]
T1 -->|RDMA WRITE| I0
T1 -->|RDMA WRITE| I2[Inference GPU 2]
Tn -->|RDMA WRITE| Ik[Inference GPU K]
论文评测的是 Kimi-K2-1T 量级:256 张训练 GPU 上的 BF16 权重,传到 128 张推理 GPU 的 FP8 权重布局。它把每个参数 tensor 的传输拆成四个 pipeline stage:
- 如果 FSDP offload 到 CPU,先做 H2D memcpy。
- 参数准备:
full_tensor()还原完整 tensor,做 projection fusion,必要时量化。 - RDMA transfer:zero-copy WRITE 到远端推理 GPU memory。
- GLOO barrier:在 mesh group 之间做全局同步。
单 rank 的 latency breakdown:
| 操作 | 时间 | 平均每次 | 次数 |
|---|---|---|---|
| Total | 1233 ms | - | - |
| Memcpy H2D | 184 ms | 378 us | 487 |
| full_tensor() | 518 ms | 532 us | 974 |
| Fuse projections | 18 ms | 37 us | 487 |
| Quantize | 88 ms | 137 us | 647 |
| RDMA submit | 26 ms | 23 us | 1144 |
| Waiting for other ranks | 357 ms | - | - |
| Remaining | 42 ms | - | - |
这个表的重点是:RDMA 数据传输不是主要瓶颈。真正贵的是 FSDP unshard、rank 间等待和参数准备。RDMA transfer 大多被 pipeline overlap 掉,只剩约 42 ms 没有被隐藏。
这说明在 RL 权重更新里,优化方向不是单纯追求更快的单次 write latency,而是:
- schedule 要让所有 NIC 都参与,不要 Rank0 限速。
- 参数准备、H2D、量化、RDMA 要 pipeline。
- 临时 GPU memory 要有 watermark,避免多个
full_tensor()同时爆显存。 - 静态 sharding metadata 很重要,能让每步更新少走控制面。
8. 场景三:MoE dispatch/combine
MoE all-to-all 是这篇论文里最有意思也最容易误读的部分。
DeepEP 的强项是基于 ConnectX、IBGDA 和 mlx5,把 GPU 发起 RDMA 做到极低延迟。fabric-lib 的 MoE kernel 走的是另一条路线:GPU 准备数据,CPU host proxy 通过 GDRCopy 观察 GPU 进度,再提交 RDMA scatter/barrier。
按直觉,host proxy 一定比 GPU-initiated RDMA 慢。但论文的实验显示,在跨节点 decode 场景里,bulk transfer、pipeline 和更少 per-token 网络操作可以抵消这部分 proxy overhead,ConnectX-7 上甚至超过 DeepEP。
8.1 Dispatch 的数据路径
dispatch 要把本 rank 的 tokens 按 expert routing 发给目标 ranks。fabric-lib 的做法分成两轮:
- GPU kernel 先统计每个 expert 的 token count,并把 routing info 通过 UVM/GDRCopy 交给 host proxy。
- host proxy 用 TransferEngine 先 scatter routing info,同时 GPU 把一小段 tokens 先写入每个 peer 的 private buffer。
- 接收方拿到 routing info 后,就知道每个发送方应该写入 shared receive buffer 的哪个连续区间。
- 剩余 tokens 再被 scatter 到 shared buffer。
- RDMA 处理跨节点,NVLink 处理同节点 peer。
- receiver kernel 等 ImmCounter 确认 RDMA 完成后,把 receive buffer 里的 tokens shuffle 成 grouped GEMM 需要的布局。
flowchart LR
A[dispatch_send kernel] --> B[count tokens per expert]
B --> C[signal host proxy]
C --> D[scatter route metadata]
B --> E[pack token send buffers]
E --> F[first burst to private buffers]
D --> G[receiver computes shared offsets]
G --> H[remaining tokens to shared recv buffer]
H --> I[ImmCounter barrier]
I --> J[dispatch_recv shuffle for grouped GEMM]
为什么要 private buffer?因为 routing info 很小但有延迟。如果完全等 routing info 到达后再传 token,首包延迟更高。先投机性地把少量 token 放入每个源私有 buffer,可以隐藏 route exchange 的延迟。论文的 ablation 显示 private buffer 太小会让 p50 decode latency 明显变差,尤其 EFA 因 route exchange 更慢,对 private token 数更敏感。
8.2 Combine 的数据路径
combine 反过来把 expert 输出按原 token 位置聚合回去。dispatch 阶段已经集中处理过 routing 信息,所以 combine 可以复用 offset 信息,直接做一次 scatter。
它的流程是:
- grouped GEMM 或 shared expert 执行期间,host proxy 预先准备 combine scatter 命令。
- GPU sender 把要返回的 token 打包到 send buffer。
- 同节点走 NVLink,跨节点走 RDMA scatter。
- receiver 等待所有 token 到达后,本地计算加权平均。
DeepEP 在 prefill combine 上有一个强优化:在发送节点做 sender-side partial sum,减少 RDMA bytes。但这会影响累加精度和确定性,因为不是全程固定顺序 fp32 accumulation。fabric-lib 更偏保守,代价是 prefill 场景下 combine latency 可能不如 DeepEP。
8.3 MoE 结果怎么看
端到端 DeepSeek-V3 decode speed,EP=DP=64:
| Cluster | Kernel | batch=2 | batch=8 | batch=32 |
|---|---|---|---|---|
| H200 EFA | Ours | 66.752 | 56.459 | 32.003 |
| H200 EFA | pplx-kernels | 20.972 | 11.607 | 4.903 |
| H100 CX-7 | Ours | 78.420 | 67.666 | 36.066 |
| H100 CX-7 | DeepEP | 73.758 | 65.785 | 36.253 |
这张表有两个重点:
- EFA 上 fabric-lib 相比基于 NVSHMEM 的 pplx-kernels 有 3 到 6 倍提升,让 EFA 上的 real-time MoE 推理变得可行。
- ConnectX-7 上,host proxy 方案居然可以匹配或略超 DeepEP,说明 decode 的瓶颈不只在“谁更早发第一个 packet”,还在 bulk transfer、packet 数、pipeline 和 combine 设计。
microbenchmark 里 decode 延迟大致是:
| EP | Ours EFA dispatch/combine | Ours CX7 dispatch/combine | DeepEP CX7 dispatch/combine |
|---|---|---|---|
| EP64 | 406 us / 286 us | 311 us / 190 us | 327 us / 180 us |
| EP32 | 342 us / 236 us | 267 us / 155 us | 286 us / 160 us |
| EP16 | 243 us / 216 us | 186 us / 110 us | 203 us / 124 us |
| EP8 | 64 us / 53 us | 65 us / 52 us | 72 us / 43 us |
prefill 延迟则更复杂。DeepEP 在部分 EP 配置下更快,尤其 combine 侧受益于 sender-side partial sum;fabric-lib decode-optimized kernel 没有为 4096-token prefill 做 chunking,buffer memory overhead 也限制了一些部署形态。
所以这篇论文不是说 fabric-lib 在所有 MoE 场景全面打败 DeepEP,而是说:
decode、小 batch、跨节点、需要 EFA 可移植性:
fabric-lib 的 host proxy + bulk scatter 路径很有竞争力。
纯 ConnectX、只追求单硬件极限、允许依赖 IBGDA/mlx5:
DeepEP 仍然是非常强的 baseline。
prefill、大 token chunk、能接受 sender-side partial sum:
DeepEP 的一些专用优化仍有优势。9. 基础性能:single write 与 paged write
论文把 TransferEngine 和 NIXL v0.6.1 做了对比,并用底层硬件 benchmark 作为 peak reference。结论是:大块 single write 需要 16 MiB 以上才容易打满;paged write 在 32 KiB 到 64 KiB 级别就能接近峰值。
关键数据:
| 操作 | 大小 | EFA | ConnectX-7 |
|---|---|---|---|
| Single WRITE | 64 KiB | 16 Gbps | 44 Gbps |
| Single WRITE | 256 KiB | 54 Gbps | 116 Gbps |
| Single WRITE | 1 MiB | 145 Gbps | 245 Gbps |
| Single WRITE | 32 MiB | 336 Gbps | 378 Gbps |
| Paged WRITE | 1 KiB | 17 Gbps / 2.11M ops/s | 91 Gbps / 11.10M ops/s |
| Paged WRITE | 8 KiB | 138 Gbps / 2.10M ops/s | 320 Gbps / 4.89M ops/s |
| Paged WRITE | 16 KiB | 274 Gbps / 2.08M ops/s | 367 Gbps / 2.80M ops/s |
| Paged WRITE | 64 KiB | 364 Gbps / 0.69M ops/s | 370 Gbps / 0.71M ops/s |
这解释了三个上层现象:
- KV page 传输很适合 paged write。32 KiB page size 加上多页批量,足够靠近峰值。
- RL 权重传输消息远大于 saturation point,所以带宽不是主要问题。
- MoE decode 的 256 KiB 左右 single write 在 EFA 上还没有充分打满,因此 EFA decode latency 会落后 ConnectX,但差距没有达到带宽差距那么夸张,因为 decode 还受 CPU proxy、route exchange、kernel shuffle 等影响。
10. CPU host proxy 的代价到底多大
很多人看到 host proxy 会直接认为它不可能低延迟。论文的 ablation 给了更细的数字。
EP64 MoE all-to-all 的 CPU overhead breakdown:
| 事件 | 线程 | p50 | p90 | p99 | p99.9 |
|---|---|---|---|---|---|
| app 调用到 enqueue 完成 | App | 0.120 us | 0.156 us | 2.019 us | 3.484 us |
| worker enqueue 完成 | Worker | 0.855 us | 1.036 us | 1.450 us | 5.050 us |
| posting first WRITE 前 | Worker | 0.441 us | 0.535 us | 0.736 us | 4.889 us |
| posting last WRITE 后,EFA | Worker | 27.886 us | 30.240 us | 39.176 us | 43.323 us |
| posting last WRITE 后,CX-7 | Worker | 8.502 us | 9.151 us | 12.310 us | 14.474 us |
从 app call 到第一笔 RDMA WRITE 的 p50 低于 1.5 us。真正贵的是给多个 peer post 所有 WR,且 EFA/libfabric 的提交成本明显高于 ConnectX/libibverbs。
不同 EP 下 scatter 所有 WR 的 post time:
| NIC | EP8 | EP16 | EP32 | EP64 |
|---|---|---|---|---|
| EFA p50 | 3.081 us | 6.536 us | 13.374 us | 27.886 us |
| CX-7 p50 | 0.842 us | 1.926 us | 4.140 us | 8.502 us |
这说明 host proxy 的代价不是不可控的常数,而是大致随 peer 数量线性增长。EP64 时 EFA 的 28 us p50 已经不能忽略,但在 decode 总延迟几百微秒的背景下仍可接受;ConnectX 的 8.5 us 则很小。
11. 和 NIXL、Mooncake、NVSHMEM、DeepEP 的关系
这篇论文容易被读成“Perplexity 做了一个替代所有通信库的东西”。更准确的定位应该是:
11.1 fabric-lib vs NCCL / torch.distributed
NCCL 是 collective 的强者。固定 world、固定 op、GPU 间高吞吐 collective,它仍然是主路径。fabric-lib 补的是动态 P2P、稀疏 peer、paged write、异构网卡可移植性。
这两者是互补关系,不是简单替代关系。
11.2 fabric-lib vs NIXL
NIXL 也是 LLM inference transfer library,目标和 fabric-lib 有重叠。论文里提到 NIXL 基于 UCX,v0.6.1 时 EFA 支持还比较 preliminary;fabric-lib 的生产 EFA 实现更早、更完整。基础带宽上,两者在许多大小下接近,fabric-lib 略快。
长期看,NIXL 的优势可能是生态和 NVIDIA 推动,fabric-lib 的优势是 Perplexity 用真实 MoE/P-D/RL 场景把 API 和 backend 打穿。
11.3 fabric-lib vs Mooncake
Mooncake 关注 disaggregated inference 和 KV transfer/store,是 P/D 架构的重要系统工作。但论文认为 Mooncake 的 RDMA transfer engine 没有 EFA 支持。fabric-lib 更强调跨 ConnectX/EFA 的最小 RDMA P2P 抽象。
11.4 fabric-lib vs NVSHMEM
NVSHMEM 提供 symmetric heap、GPU/host initiated 通信和丰富 PGAS 风格接口,但论文指出其在 EFA 上性能退化明显。fabric-lib 没有试图提供完整 PGAS 编程模型,而是围绕 LLM 数据路径做少量高效原语。
11.5 fabric-lib vs DeepEP
DeepEP 是 MoE dispatch/combine 的强 baseline,优势是 ConnectX + IBGDA 上极低延迟。fabric-lib 的差异是:
- 支持 EFA。
- 不依赖 GPU-initiated RDMA。
- 使用 CPU host proxy,但通过 bulk transfer 和 overlap 把额外开销压下去。
- 在 decode inter-node 场景里可以匹配或超过 DeepEP。
- 在 prefill 和纯 ConnectX 极限场景下,DeepEP 仍有专用优化优势。
12. 这篇论文真正贡献了什么
我认为 fabric-lib 的贡献不在“发明了 RDMA WRITE_IMM”,而在这几个系统取舍:
- 抽象边界选得小:只依赖可靠无序、SEND/RECV、WRITE_IMM 和 immediate counter,不把 READ/atomic/order 放进关键语义。
- 把多 NIC per GPU 当成一等公民:这对 EFA 很关键,也更符合云实例现实。
- 把完成通知做成可组合的 ImmCounter:它是 P/D KV、MoE barrier、one-sided 权重更新的共同同步原语。
- 接受 host proxy,但严肃优化 proxy:NUMA pinning、lock-free queue、WR templating、WR chaining、UVM watcher、GDRCopy 都是为了把 CPU 参与的成本限制在微秒级。
- 用生产场景反推 API:KV transfer、RL weight update、MoE all-to-all 覆盖了 paged write、large bulk write、scatter/barrier 三类完全不同的通信形态。
这个设计对做 LLM serving 系统的人有一个很实际的启发:不要一上来就问“我能不能把 NCCL 用在这个场景”。先问:
参与者是否动态?
数据量是否稀疏或变长?
接收方是否需要主动参与?
是否必须跨云厂商或跨 NIC 类型?
是否可以接受可靠但无序?
完成通知能否用计数器表达?如果这些答案偏向动态、稀疏、单边写、跨硬件,那么 fabric-lib 这类 P2P transfer engine 会比 collective 更自然。
13. 局限和风险
fabric-lib 的设计很有现实价值,但也有明显边界。
第一,它不是通用分布式系统通信层。它假设上层已经处理了 discovery、routing、request lifecycle、错误恢复、调度策略。TransferEngine 给的是数据面原语,不是完整服务框架。
第二,取消和故障语义很复杂。P/D KV 传输里,只要远端 WRITE 可能还在路上,本地 KV page 就不能复用。真正生产实现需要 request-level cancellation token、heartbeat、timeout、pending operation drain 和明确的确认协议。
第三,host proxy 的 tail latency 仍然要小心。Rust callback 很稳,Python callback max latency 可以到毫秒级;EFA 的 WR posting p99/p99.9 也会随 EP 增长。关键路径必须尽量留在 Rust/C++/CUDA 侧。
第四,抽象虽然可移植,但 backend 仍需要硬件调优。ConnectX 的 relaxed ordering、WR chaining、RC QP 拆分,EFA 的 libfabric descriptor 模板、多 NIC 聚合,都不是自动来的。未来支持 eRDMA、Broadcom、AMD NIC,大概率还是要写专门 backend。
第五,MoE prefill 不是 fabric-lib 当前最强项。论文自己也指出 decode-optimized kernel 在 prefill 上受 buffer memory 和 lack of chunking 限制;DeepEP 的 sender-side partial sum 在一些 prefill combine 场景更快。
第六,论文评测基于 Perplexity 自研 inference engine,不是 vLLM/SGLang/TensorRT-LLM 的开箱即用结果。把同样收益迁移到其他引擎,需要适配 KV layout、scheduler、CUDA Graph、allocator、error handling 和 MoE kernel pipeline。
14. 对 vLLM / SGLang / llm-d 生态的启发
如果把 fabric-lib 放到当前推理生态里看,它很像一个底层拼图:
Router / Scheduler:
负责选择 prefiller、decoder、expert placement、rollout worker。
Inference Engine:
负责 batch、attention、KV cache manager、CUDA Graph、MoE kernel 调用。
TransferEngine:
负责把动态 P2P 数据流用 RDMA 高效搬过去。对 vLLM KV Connector 来说,fabric-lib 这类库可以成为 NIXL/Mooncake 之外的另一种 backend。scheduler 侧仍然决定哪些 blocks 需要 load/store,worker 侧仍然在 attention 前后挂钩,但实际数据面可以走 paged WRITE_IMM 和 ImmCounter。
对 llm-d 这类 Kubernetes serving stack 来说,fabric-lib 说明了一个方向:P/D 分离不只是调度器和 Router 的问题,还要求底层 transfer layer 支持动态 peer、弹性扩缩容和跨云网络硬件。否则上层调度再聪明,也会卡在通信 world 初始化、固定 membership 或某个云厂商 NIC 不支持上。
对 MoE serving 来说,论文也提示了一个折中:如果业务只跑 ConnectX 集群,DeepEP 这类硬件特化库可能最合适;如果要在 AWS EFA 上跑大规模 MoE,或者希望同一套引擎能跨云部署,那么“略微牺牲硬件专用性,换可移植低延迟 P2P”可能更重要。
15. 总结
fabric-lib 把一个正在变得越来越重要的问题讲清楚了:现代 LLM 系统的通信不再只有 collective。P/D 分离需要动态 KV page 写入,RL rollout 需要跨集群权重推送,MoE 需要低延迟稀疏 all-to-all。它们共同需要的是一套能跨硬件、能操作 GPU memory、能支持 one-sided、能表达完成通知的 P2P transfer layer。
它的核心设计是可靠但无序的 TransferEngine:用最小公共 RDMA 能力统一 ConnectX-7 和 EFA,用 ImmCounter 替代顺序假设,用 DomainGroup 聚合多 NIC,用 UVM watcher/GDRCopy/host proxy 把 GPU 进度和网络提交接起来。
这篇论文最值得借鉴的不是某一个 benchmark 数字,而是抽象边界:
只抽象上层真正需要、并且各类云 RDMA NIC 都能高效提供的能力;
把硬件差异封装在 backend;
把系统复杂度留给更接近业务语义的层处理。如果未来 LLM serving 越来越走向 P/D 分离、wide expert parallelism、异步训练-推理协同和多云部署,这类 P2P RDMA transfer engine 会成为和 collective library 同等重要的基础设施。
参考
fabric-lib: RDMA Point-to-Point Communication for LLM Systems, arXiv: https://arxiv.org/abs/2510.27656- Perplexity AI
pplx-gardenGitHub 仓库: https://github.com/perplexityai/pplx-garden pplx-garden/docs/fabric-lib.md: https://github.com/perplexityai/pplx-garden/blob/main/docs/fabric-lib.md- Perplexity Research 文章
RDMA Point-to-Point Communication for LLM Systems: https://research.perplexity.ai/articles/rdma-point-to-point-communication-for-llm-systems - DeepEP: https://github.com/deepseek-ai/DeepEP
- NVIDIA NIXL: https://github.com/ai-dynamo/nixl