Skip to content
团子云技术 Lite 1.048596
Go back

Mooncake TE 阅读手记-11-TE 接口设计

团团虾导读:为什么 TE 不直接复用 gRPC?因为 LLM 推理场景下 KV Cache 是 GPU VRAM 中的连续张量,序列化只会引入不必要的 CPU-GPU 搬运开销。这篇从传统 RPC 路径的三个根本问题出发,拆解 TE 的数据面三原语——target_id+target_offset(远端寻址)、Transport 选择、Batch/Task polling(完成监控)。

文章一:TE 接口设计 — 绕开 RPC 序列化,暴露数据面原语

传统 RPC 路径的问题

在分布式训练/推理框架中,数据传输通常走框架级 RPC 通道(gRPC、brpc 等),路径大致是:

数据 → 序列化(protobuf/flatbuffers) → Socket/TCP/RDMA → 反序列化 → 使用

对于 LLM 推理中的 KV cache 传输(单次传输可达几 GB),这套路径有三个根本问题:

序列化开销巨大:KV cache 是 GPU VRAM 中的一大块连续张量数据,不需要序列化,序列化只会引入不必要的 CPU-GPU 数据搬运和内存分配。

Collective 语义不匹配:RPC 调用通常隐含同步语义(请求-响应),而 KV cache 传输天然是异步的、单向的(节点 A 读节点 B 的某块内存)。硬塞进 RPC 语义需要额外的协调开销。

无法精细控制硬件资源:框架级 RPC 把 RDMA QP、MR、CQ 等硬件资源封装在黑盒里,上层无法针对传输模式做优化。

TE 的数据面三原语

Mooncake Transfer Engine 把数据传输抽象成三个明确的”数据面原语”,全部暴露给上层应用直接控制:

1. 远端哪个 Buffer 可访问  →  target_id + target_offset(Segment 协议)
2. 用哪个 Transport        →  Transport 选择(基于 metadata 的 protocol 字段)
3. 如何监控完成            →  Batch/Task 级别的 polling(getTransferStatus)

对应的 API 核心接口(mooncake-transfer-engine/include/transport/transport.h):

// 1. 提交传输:指定远端 Segment、偏移量、本地地址、长度
struct TransferRequest {
    enum OpCode { READ, WRITE };
    OpCode opcode;
    void *source;           // 本地 buffer 地址
    SegmentID target_id;    // 远端 Segment 标识
    uint64_t target_offset; // 远端 buffer 内偏移
    size_t length;          // 传输长度
    int advise_retry_cnt = 0;  // 单请求粒度的重试次数建议
};

// 2. 批量提交到 Batch
Status submitTransfer(BatchID batch_id,
                      const std::vector<TransferRequest>& entries);

// 3. 轮询完成状态
Status getTransferStatus(BatchID batch_id, size_t task_id,
                         TransferStatus& status);

这套接口没有序列化参数,没有 protobuf message,没有同步返回。 它只是告诉引擎”从本地地址 A 读(或写入)远端 Segment B 的偏移 C,长度 D”。引擎直接操作注册过的内存区域。

对比:RPC 方式 vs TE 方式

维度框架级 RPCTransfer Engine
序列化必须经过 protobuf/序列化零拷贝,直接 DMA
同步语义Request-Response异步 Submit + Poll
地址模型Hostname:PortSegmentID + Offset
传输选择框架内建,黑盒显式 Transport 安装
完成通知自动返回显式 polling / Notify

代码验证

TransferRequest 只有地址和偏移,没有序列化参数:

mooncake-transfer-engine/include/transport/transport.h:58-66 定义了 TransferRequest 结构体,包含 opcode、source 地址、target_id、target_offset、length、advise_retry_cnt 六个字段。没有序列化缓冲区、没有 protobuf message 引用。其中 advise_retry_cnt 为单请求粒度的重试次数建议,被 RdmaTransport::submitTransferTask()rdma_transport.cpp:507-508)消费。

submitTransfer 的调用链完全不走序列化:

  1. TransferEngine API 声明在 transfer_engine.h:100,实际转发给 TransferEngineImpl 的实现位于 transfer_engine.cpp:90-93
  2. TransferEngineImpl::submitTransfer()transfer_engine_impl.h:117)转发给 MultiTransport
  3. MultiTransport::submitTransfer()multi_transport.cpp:107)按请求的 target_id 查找 Segment 描述中的 protocol 字段,选择对应的 Transport
  4. Transport 将请求拆成 Slice 列表,提交给 RdmaContext 的 WorkerPool

MultiTransport 中的 Transport 选择逻辑:

mooncake-transfer-engine/src/multi_transport.cpp:432-453selectTransport() 方法通过 metadata_->getSegmentDescByID(entry.target_id) 获取目标 Segment 的 protocol 字段,然后从 transport_map_ 查找对应的 Transport 实例。整个选择过程没有序列化、没有 RPC 调用。

上层使用示例(benchmark 代码):

mooncake-transfer-engine/example/transfer_engine_bench.cpp:398-441 展示了标准使用模式:

// 1. 分配 Batch
auto batch_id = engine->allocateBatchID(FLAGS_batch_size);

// 2. 构造批量请求(纯地址 + 偏移)
std::vector<TransferRequest> requests;
for (int i = 0; i < FLAGS_batch_size; ++i) {
    TransferRequest entry;
    entry.opcode = opcode;
    entry.source = (uint8_t*)(addr) + block_size * (i * threads + thread_id);
    entry.target_id = segment_id;
    entry.target_offset = remote_base + block_size * (i * threads + thread_id);
    entry.length = block_size;
    requests.emplace_back(entry);
}

// 3. 提交
engine->submitTransfer(batch_id, requests);

// 4. Poll 每个 task 直到完成
for (int task_id = 0; task_id < FLAGS_batch_size; ++task_id) {
    TransferStatus status;
    while (!completed) {
        engine->getTransferStatus(batch_id, task_id, status);
        if (status.s == TransferStatusEnum::COMPLETED) completed = true;
    }
}

注意:整个过程中,数据传输直接在两个节点的注册内存间进行(RDMA Read/Write),CPU 完全不参与数据搬运。所谓的”远端哪个 Buffer 可访问”由 target_id + target_offset 唯一定位,“用哪个 Transport”由 metadata 中的 protocol 字段自动选择。



Share this post on:

Previous Post
Mooncake TE 阅读手记-12-Transport 核心概念与线程模型
Next Post
Mooncake TE 阅读手记-10-QP Depth 与 Slice 处理