Skip to content

RDMA 编程:点对点数据传输 ​

大规模训练里最重的活是搬数据。Monarch 通过 RDMABuffer API 提供一等公民的 RDMA 支持:把"数据引用"与"数据搬运"彻底分离,让你按应用逻辑设计协调模式,而不是被框架的传输时机绑架。

1. 核心思想:引用与搬运分离 ​

传统做法是把大张量塞进消息里发出去。Monarch 的做法是:

  • 消息里只传轻量级的缓冲区引用(很小);
  • 真正的批量数据在你显式要求时才搬运(很大)。

RDMA 传输基于 libibverbs,走 InfiniBand 或 RoCE。与 NCCL 集合通信"发送必须配对接收"不同:拿到缓冲区句柄的一方,可以不需要所有者参与,直接单边读或写。

2. RDMABuffer:不拷贝数据的视图 ​

RDMABuffer 不复制数据,只是创建一个可传给其他进程的数据视图:

python
import torch
from monarch.actor import Actor, endpoint
from monarch.rdma import RDMABuffer

class ParameterServer(Actor):
    def __init__(self):
        self.weights = torch.rand(1000, 1000)  # 大块模型权重

        # RDMABuffer 不拷贝数据,只创建可传递的视图
        self.weight_buffer = RDMABuffer(self.weights.view(torch.uint8).flatten())

    @endpoint
    def get_weights(self) -> RDMABuffer:
        return self.weight_buffer  # 小消息:只传一个引用!

3. read_into:显式搬运 ​

Worker 拿到引用后,控制面先取轻量引用,数据面再决定何时搬运:

python
class Worker(Actor):
    def __init__(self):
        self.local_weights = torch.zeros(1000, 1000)

    @endpoint
    def sync_weights(self, server: ParameterServer):
        # 控制面:拿到轻量级引用(小消息)
        weight_ref = server.get_weights.call_one().get()

        # 数据面:需要时才显式批量传输
        weight_ref.read_into(self.local_weights.view(torch.uint8).flatten()).get()

把两端连接起来:

python
server_proc = this_host().spawn_procs(per_host={"gpus": 1})
worker_proc = this_host().spawn_procs(per_host={"gpus": 1})

server = server_proc.spawn("server", ParameterServer)
worker = worker_proc.spawn("worker", Worker)

worker.sync_weights.call_one(server).get()

4. 参数服务器模式的完整图景 ​

上面两段代码合起来就是一个极简参数服务器(Parameter Server):

text
ParameterServer                    Worker
     │                                │
     │◄── get_weights.call_one() ─────│  控制面:小消息
     │                                │
     │── RDMABuffer 引用 ────────────►│  (几十字节)
     │                                │
     │         (RDMA 单边读,         │
     │      weight_ref.read_into())   │  数据面:8MB 直达显存
     ▼                                ▼

关键点:

  • Server 的 endpoint 返回 RDMABuffer,没有把 8MB 权重塞进消息;
  • read_into 走 RDMA 单边传输,绕过两端 CPU;
  • Worker 可以在任何时刻发起同步,频率与时机完全由你的应用逻辑决定。

5. 使用须知 ​

官方对 RDMABuffer 的定位是刻意保持底层:

  • 它只做"被要求时的数据搬运";
  • 调用方负责保证搬运完成前数据不被修改(否则读到中间状态);
  • 更高级的库(张量传输、集合通信原语)将在这些原语之上构建。

这也是为什么教程把它放在高级篇:理解它,你才能理解 Monarch "控制平面/数据平面分离"的架构哲学(见《架构揭秘》)不是口号,而是落到了每一个 API 上。

6. 动手实验 ​

官方仓库提供两个现成的实验材料:

  • docs/source/examples/getting_started.py:RDMA 章节(Parameter Server 示例);
  • examples/rdma_pingpong/:RDMA 点对点传输基准测试,可用来测你网络的往返延迟与吞吐。

关键点 ​

  • 引用与搬运分离:消息传 RDMABuffer 引用,数据走显式 read_into;
  • 单边语义:拿到句柄即可读/写,无需所有者配合(对比 NCCL 的 send/recv 配对);
  • 零拷贝:RDMABuffer 只是视图,不复制数据;
  • 底层原语:数据一致性由调用方负责,高级库在其上生长。

Monarch中文教程