LLM Infer Infra 学习资料库

Mooncake Transfer Engine:字节移动的实现案例

返回传输主题 · Store 对象协议 · 实验

本篇介绍经典 Transfer Engine。先按传输主题理解一般问题,再把地址、注册、batch 和完成映射到这个实现。TENT 等其他实现分支需要单独核对接口与行为。

执行与资源生命周期

经典 TE 的概念流程:

initialize → 注册本地内存 → 发现/打开远端 segment
→ 分配 batch → 构建 READ/WRITE 请求 → 提交
→ 查询完成状态 → 释放 batch
→ 所有在途请求安全结束 → 注销内存 → 释放 buffer

Store 负责 key 语义,TE 面向地址、长度、segment 和操作,不知道 token、模型或前缀是什么。

RDMA 需要理解的名词

名词 作用 常见工程问题
MR / registration 将内存区注册给 NIC,建立访问权限/映射 注册成本、memlock、生命周期、注册缓存
lkey / rkey 本地/远端内存访问所需 key 过期 descriptor、注销后的无效访问
QP 发送/接收工作请求的队列对 连接规模、队列深度、错误状态
CQ / completion 获取工作完成或错误 轮询 CPU、批完成、完成语义
RDMA READ/WRITE 直接操作已授权的远端内存 数据准备、接收方通知、同步和缓冲区保护
GPUDirect RDMA 在支持的硬件/驱动/拓扑上直接访问 GPU 内存 PCIe 拓扑、驱动支持、GPU stream 可见性

单边 RDMA 减少远端 CPU 参与数据复制,但控制面、连接建立、注册、轮询、错误处理仍会消耗 CPU。RDMA 不是端到端应用一致性的替代品。

多网卡与 NUMA

TE 利用拓扑选择合适路径并把大请求切成 slices,从多个网卡搬运。收益受最窄环节限制:源/目的内存带宽、PCIe、NIC、交换网络、GPU copy、CPU 轮询和慢 rail。

“两张网卡”不必然获得两倍吞吐。跨 NUMA 读取、PCIe 根复用、共享上联、热点目标、过细 slices 或慢链路都可能限制扩展。

零拷贝的四个层次

  1. Python bytes 变成新对象的拷贝。
  2. 应用 buffer 与传输 staging buffer 的拷贝。
  3. 网络栈/远端 CPU 的数据复制。
  4. CPU↔GPU、layout conversion、压缩/解压的搬运。

put_from/get_into、已注册 buffer、RDMA、GDR 分别消除或减少其中某些环节。性能报告应画真实路径,而不是只写“零拷贝”。

深入:一次 batch 传输在 TE 内部怎么走

上面是接口层面的流程。下面打开 TE 内部,看一个请求怎样被切分、分到多张网卡、确认完成,以及出错时怎样重试。内容依据 Mooncake 学习版本的 Transfer Engine 设计文档 和 mooncake-transfer-engine/ 源码。

Transfer Engine 内部结构

环节具体做法默认值(学习版本 config.h)为什么这样设计
切分超过 64 KiB 的请求切成多个 slice,每个 slice 独立选路、独立完成slice_size = 65536,可用环境变量 MC_SLICE_SIZE 调整让一个大请求同时用上多张网卡;slice 太小会增加提交和轮询开销
选路每台机器生成拓扑矩阵并广播。按本地 buffer 所在的 CPU/GPU 选优先网卡,按远端目标内存的位置选远端网卡可用 --auto_discovery 自动生成避免数据绕过 CPU 之间的 UPI 或共享的 PCIe 交换芯片
连接“本地网卡—远端网卡”之间用一对 endpoint 表示,每个 endpoint 含若干 QP;第一次用到时才建连,放入连接池num_qp_per_ep = 2,max_ep_per_ctx = 65536集群很大时不能两两预先建满连接;连接池满了按 SIEVE 算法淘汰
执行每张网卡一个 RdmaContext,由 worker 线程投递工作请求并轮询完成队列workers_per_ctx = 2,num_cq_per_ctx = 1投递和完成都在专门线程里做,应用线程不被阻塞
容错slice 失败时换一条可达路径重新提交;暂时避开出问题的网卡、上下文或完成队列;坏连接从两端连接池移除,下次再重建retry_cnt = 9(slice 重试上限相关配置)多网卡环境里常见的是某一张网卡暂时不可用,换路径往往就能成功
回收被淘汰或删除的 endpoint 先进入等待列表,等它上面的在途 slice 全部结束后再异步回收有约 1 Hz 的后台检查直接销毁正在使用的 QP 会让在途传输出错

下面一步步演示一次 256 KiB 的写入,其中一个 slice 第一次失败、换路径后成功:

应用调用申请 batch:allocateBatchID(1)
slice(还没有)
网卡与连接mlx5_0 空闲 mlx5_1 空闲
任务状态无任务
应用调用submitTransfer:WRITE 256 KiB,本地 buffer → 远端 segment
slice(还没有)
网卡与连接mlx5_0 空闲 mlx5_1 空闲
任务状态任务已提交,0/4 完成
应用调用按 64 KiB 切成 4 个 slice
sliceS0S1S2S3
网卡与连接按拓扑矩阵选出 buffer 所在 CPU 的优先网卡:mlx5_0、mlx5_1
任务状态0/4 完成
应用调用slice 分到不同网卡,worker 线程投递 RDMA WRITE
sliceS0→mlx5_0S1→mlx5_1S2→mlx5_0S3→mlx5_1
网卡与连接两张网卡并行工作;与远端网卡的连接按需建立并放入连接池
任务状态0/4 完成
应用调用轮询完成队列(CQ):S0、S2 成功,S3 成功,S1 报错
sliceS0 ✓S1 ✗S2 ✓S3 ✓
网卡与连接S1 所用的 endpoint(本地网卡—远端网卡的 QP)出错,S3 用的是另一个 QP
任务状态3/4 完成
应用调用S1 换一条可达路径重试(重试次数有上限)
sliceS0 ✓S1→mlx5_0S2 ✓S3 ✓
网卡与连接暂时避开出错的网卡/连接;坏连接从连接池移除,下次再重建
任务状态3/4 完成
应用调用全部 slice 成功,任务状态变为 COMPLETED
sliceS0 ✓S1 ✓S2 ✓S3 ✓
网卡与连接—
任务状态4/4 完成
应用调用freeBatchID;此后应用才能复用或释放本地 buffer
slice(还没有)
网卡与连接—
任务状态batch 已释放
  1. 应用先申请一个 batch ID,后面的请求都挂在这个 batch 下。
  2. 提交一个传输请求:操作类型(READ/WRITE)、本地地址、远端 segment、远端偏移、长度。函数返回只代表“已提交”,不代表已完成。
  3. 超过 64 KiB 的请求被切成多个 slice(默认 64 KiB,可用 MC_SLICE_SIZE 调整)。根据集群广播的拓扑矩阵,为本地 buffer 所在的 CPU/GPU 选出优先的网卡,远端也按目标内存位置选网卡。
  4. 不同 slice 可以走不同的网卡,多张网卡一起工作。本地网卡和远端网卡之间的连接(endpoint,内含若干 QP)在第一次用到时才建立,连接数有上限,超出时按 SIEVE 算法淘汰。
  5. worker 线程轮询完成队列,逐个确认 slice 的结果。这里 S1 所用的连接(QP)出错了;失败的粒度是 endpoint / slice,同一张网卡上的其他 slice 可能不受影响。
  6. TE 会为失败的 slice 换一条可达路径重新提交,并暂时避开有问题的网卡或连接;超过重试上限仍失败,整个任务就标记为失败。
  7. 所有 slice 都成功,任务才算完成。应用通过 getTransferStatus / getBatchTransferStatus 查到这个状态。
  8. 释放 batch。只有确认任务完成(或确认失败且不再有在途 slice)之后,应用才能复用或释放本地 buffer、注销内存。

面试要点:①“提交成功”“slice 完成”“任务完成”“上层对象可读”是四个不同的时刻;② 多网卡不一定线性加速——Mooncake README 公布的 8×400G 结果约 190 GB/s,只用到线速的一半左右(见公开数据);③ 超时或失败后,必须等在途 slice 都结束才能释放本地 buffer,这和存储层的租约、上层的设备事件是不同层次的保护。

带着问题读源码和文档

入口 阅读问题
经典 TE 设计 segment 怎样被发现?READ/WRITE 使用什么描述信息?
TE Python API 注册、提交与完成的接口前提、返回值和释放顺序是什么?
TE 源码目录 batch 与 slice 的状态如何汇总?错误怎样向上报告?
benchmark 调优 对象大小、并发、拓扑和轮询怎样影响结果?

阅读时记录三个不同的资源:应用持有的 buffer、传输运行时管理的任务、上层 Store/Connector 的保护与发布状态。不要根据其中一个对象销毁或一个调用成功,就推断其他层已经完成。

验证方式

先完成字节校验和失败处理,再测稳态传输;分别记录是否包含注册、建连、host staging 和设备同步。与 Store 对象吞吐、推理 TTFT 对比时,明确新增的目录、分配和消费成本。实际步骤见传输与存储实验。

输入主题、术语或问题