基于URMA 的远程内存语义传输层
关联讨论:
- #3167 brpc支持传输层多协议改进建议 —— Socket-Transport 架构改造共识
- #3217 openEuler总线协议 urma 和 obmm 技术路线讨论 —— 路线 B(URMA-UrmaTransport)与路线 C(双后端)共识
- #3226 基于UB协议OBMM共享内存Transport方案设计 —— OBMM/ShmTransport 实现方案
本提案对应 #3217 中的路线 B,并与 #3226 的 OBMM-ShmTransport 形成路线 C(双后端协同)。
1. 背景与动机
1.1 现有传输层的局限
brpc 当前内置三种传输层(SOCKET_MODE_TCP / SOCKET_MODE_RDMA / SOCKET_MODE_UBRING),均通过 #3167 落地的 Transport 抽象接入:
| 传输层 | 访问语义 | 单跳延迟 | 连接模型 | 适用场景 |
|---|---|---|---|---|
| TCP | Socket API(字节流) | ~50μs | 5-tuple | 广域网/局域网 |
| RDMA | Verbs API(异步) | ~2-5μs | RC-QP(每连接一个 QP) | 数据中心,baidu_std 协议 |
| UBRing | Load/Store(共享内存) | ~500ns | 共享内存环 | 同机 IPC / UBS-Mem 跨节点 |
三者各有边界,但在以下场景仍存在缺口:
- RDMA 的 QP 爆炸:RC 模型下每条逻辑连接占用一个 QP,千级节点规模下 QP 数量达百万级,耗尽网卡片上 SRAM,触发 QP 状态 Cache Miss,延迟从 ~12μs 跳变至 ~300μs。
- RDMA 的控制面/数据面资源争用:内存注册/注销与数据面竞争网卡内部资源,高并发下性能抖动。
- RDMA 编程模型笨重:
准备 WQE → Doorbell → 轮询 CQE的异步流程在小包(数百字节)场景下,WQE 构建开销占比可达 17.8%。 - UBRing 的跨节点能力受限:当前跨节点依赖 UBS-Mem(
ub_shm_type=2),需要额外libubsm_sdk.so;其 64 字节定长分块(60B payload + 4B header)对大包不友好,单消息上限受data_queue_size约束(超限返回EMSGSIZE)。 - 大包/小包协同缺失:UBRing 对小包极优(亚微秒、零系统调用),但大包要切分成 ~17k 个 64 字节块逐块拷贝;RDMA 对大包友好(直接 SGE 零拷贝),但小包开销高。目前两者各自独立,无法在一个连接内按包大小自动选择最优路径。
1.2 URMA 是什么
URMA(Unified Remote Memory Access)是 openEuler 在 UB(Unified Bus)总线上提供的用户态远程内存访问 SDK,核心特性:
- 无连接 UM(Unreliable Datagram)语义:驱动内置重传/CRC,可靠且免 RC-QP 爆炸——单 Jetty 可打 N 个远端,连接数为 O(N) 而非 O(N×线程数)。
- 统一内存操作语义:单边(
URMA_OP_SEND/URMA_OP_READ/URMA_OP_WRITE)、双边、原子操作通过同一套urma_post_send/recvAPI 实现,RoCE v2 / InfiniBand / PCIe P2P 三种底层统一对接。 - URMA-SHM 模式:远端映射同一物理页,本机与跨机共享内存统一访问,天然适配
one2all场景。 - Jetty 流控:硬件级流控替代 RDMA 的 PFC/ECN,避免拥塞丢包。
性能定位(引用 #3217 实测口径):
| 技术方案 | 访问方式 | 单跳延迟 | 编程模型 | 缓存一致性 | 适用边界 |
|---|---|---|---|---|---|
| RDMA | Verbs API(异步) | ~2-5μs | Read/Write/Send | 无(需软件同步) | 数据中心 |
| URMA | Segment API(异步) | ~2-5μs | Read/Write | 可选硬件一致性 | 超节点 |
| OBMM | Load/Store | ~几百ns | 直接内存访问 | 可选 NC 和 CC 两种 | 超节点 |
1.3 本提案的目标
面向超节点(UB 总线互联的 CPU/GPU 集群)场景,新增一个 UrmaTransport,作为 RDMA 在 UB 场景的对等替代与升级:
- 性能对标 RDMA:在 RoCE/IB 底层上达到与现有
RdmaTransport同量级延迟(~2-5μs),并通过 UM 语义消除 QP 爆炸。 - 与 UBSHMTransport 大小包协同:复用 #3226 的 OBMM-ShmTransport 小包极速路径,由 UrmaTransport 承接大包/跨节点高吞吐路径,运行时按包大小与拓扑自动选择(路线 C)。
- 复用现有 Transport 框架:完全沿用 #3167 落地的
Transport/TransportFactory/SocketMode抽象,不侵入Socket/InputMessenger/协议层。 - 平滑落地:参考
RdmaTransport的「compose TcpTransport + tri-state 协商 + TCP fallback」模式,保证未启用/对端不支持时透明回退 TCP。
2. 总体架构
2.1 在 brpc 中的位置
flowchart TD
subgraph UserAPI["用户接口层"]
CS["Channel / Server<br/>ChannelOptions / ServerOptions<br/>socket_mode = SOCKET_MODE_URMA"]
end
subgraph Factory["TransportFactory (transport_factory.{h,cpp})"]
F["if/else-if on SocketMode<br/>TCP → TcpTransport<br/>RDMA → RdmaTransport #if BRPC_WITH_RDMA<br/>UBRING → UBShmTransport #if BRPC_WITH_UBRING<br/>URMA → UrmaTransport #if BRPC_WITH_URMA ★新增"]
end
subgraph UrmaTransportLayer["UrmaTransport : public Transport (urma_transport.{h,cpp})"]
T["std::shared_ptr<TcpTransport> _tcp_transport (fallback)<br/>urma::UrmaEndpoint* _urma_ep<br/>UrmaState _urma_state {ON / OFF / UNKNOWN}"]
end
subgraph UrmaEndpointLayer["urma::UrmaEndpoint : public SocketUser (urma/urma_endpoint.*)"]
E["Jetty / URMA Segment 管理<br/>建链握手状态机(C/S 对称)<br/>数据收发:urma_post_send / urma_post_recv / CQ 轮询<br/>大小包分流:与小包 ShmTransport 协同"]
end
CS -->|"ContextInitOrDie(mode, ...)<br/>CreateTransport(mode)"| F
F --> T
T --> EUrmaTransport 与 RdmaTransport、UBShmTransport 并列为第四种 Transport 实现,对外行为完全一致:用户只需把 socket_mode 设为 SOCKET_MODE_URMA,其余 brpc API(Channel/Server/Controller/baidu_std 协议)零改动。
2.2 核心类设计
完全沿用 RDMA/UBShm 已验证的两层结构(Transport + Endpoint),保证代码风格与维护成本对齐:
| 类 | 文件 | 职责 | 参考 |
|---|---|---|---|
brpc::UrmaTransport |
src/brpc/urma_transport.{h,cpp} |
Transport 子类;持有 UrmaEndpoint* + fallback TcpTransport;三态 URMA_ON/OFF/UNKNOWN;ContextInitOrDie 校验协议/SSL |
rdma_transport.{h,cpp} / ubshm_transport.{h,cpp} |
urma::UrmaEndpoint |
src/brpc/urma/urma_endpoint.{h,cpp} |
每连接状态机;SocketUser 子类;BAIDU_CACHELINE_ALIGNMENT;Jetty/Segment 生命周期;CQ 轮询;收发数据路径 |
rdma/rdma_endpoint.{h,cpp} |
urma::UrmaConnect : public AppConnect |
同上 | 客户端握手驱动;StartConnect 起后台 bthread 跑 ProcessHandshakeAtClient |
RdmaConnect / UBConnect |
urma::UrmaHandshake |
src/brpc/urma/urma_handshake.{h,cpp} |
握手策略基类 + v2/v3 子类;ParsedHello 中间结构;magic 分发 |
rdma/rdma_handshake.{h,cpp} |
urma::UrmaResource |
同 endpoint | POD:urma_jetty*、urma_cq*、comp channel;预连接池 |
RdmaResource |
urma::BlockPool(复用) |
src/brpc/rdma/block_pool.{h,cpp} |
内存注册池;UrmaTransport 复用 RDMA 已有的 block_pool(URMA 底层同为 verbs 风格注册) |
现有 |
urma::UrmaHelper |
src/brpc/urma/urma_helper.{h,cpp} |
全局初始化;liburma dlopen;设备/Jetty 管理 |
rdma/rdma_helper.{h,cpp} |
关键设计决策(与 RDMA 对齐,便于复用与评审):
- Compose TCP,不替换:
UrmaTransport内嵌std::shared_ptr<TcpTransport>,握手未完成/对端不支持/资源不可用时,所有数据路径方法回退到 TCP,保证现有 RPC 语义不破。 - 三态协商:
URMA_UNKNOWN → URMA_ON / URMA_OFF,数据路径统一判断_urma_state != URMA_OFF,与 RDMA 的RDMA_UNKNOWN/ON/OFF完全对称。 - Send/Recv + Immediate:采用
URMA_OP_SEND_WITH_IMM(对应IBV_WR_SEND_WITH_IMM),32-bit immediate 携带 recv-WR ack 数,实现 piggyback 流控,避免额外控制报文。不使用单边 RDMA Write/Read 作为主路径,降低收端预注册缓冲的复杂度。 - 双窗口 credit 流控:
_remote_rq_window_size(对端 recv 容量)+_sq_window_size(本地 SQ 容量),均为 atomic;窗口耗尽返回 EAGAIN,WaitEpollOut阻塞在_epollout_butex。 - 复用
block_pool+ IOBuf allocator 劫持:URMA 同为 verbs 风格,需要注册内存。直接复用src/brpc/rdma/block_pool.{h,cpp}与rdma_helper.cpp中butil::iobuf::blockmem_allocate的替换逻辑,避免重复造轮子。若内存池后端需区分,通过RegisterCallback注入 Urma 版本的UrmaRegisterMemory。 - 预连接 Jetty 池:参考
FLAGS_rdma_prepared_qp_cnt(默认 1024),在GlobalUrmaInitializeOrDie时预分配 Jetty+CQ 集合,连接时从池中取,断开后 RESET 回收。 - dlopen
liburma.so:参考rdma_helper.cpp::ReadRdmaDynamicLib,运行时dlopen+dlsym解析urma_*符号,未启用时#else提供空 stub,保证非 URMA 构建干净。
2.3 大小包协同:路线 C 的关键
这是本提案相对于 #3226 OBMM 方案的核心增量价值。UBShmTransport(#3226)在 64 字节定长块上对小包极优,但对大包存在 EMSGSIZE 上限和 ~17k 块拷贝开销。UrmaTransport 通过 URMA 的 SGE 零拷贝对大包友好。两者协同设计:
flowchart LR
Sock["Socket<br/>(UrmaChannel)"]
Sock -->|"小包 < FLAGS_urma_shm_split_threshold<br/>(默认 64KB)"| Small
Sock -->|"大包 >= threshold"| Large
subgraph Small["小包路径"]
S1["走 UBSHMTransport / OBMM 共享内存<br/>Load/Store,~1μs"]
S2["适用:特征查询、CTR 分数、<br/>控制信令、ANN 倒排"]
S1 --- S2
end
subgraph Large["大包路径"]
L1["走 UrmaTransport<br/>URMA SGE 零拷贝,~2-5μs,无 EMSGSIZE"]
L2["适用:Embedding 大向量、<br/>模型参数拉取、特征序列化大包"]
L1 --- L2
end协同实现方式(两选一,倾向方案 A,留待社区讨论):
方案 A:应用层显式选择(推荐先行落地)
- 用户在
ChannelOptions中显式为不同服务选择socket_mode:小包服务用SOCKET_MODE_UBRING,大包服务用SOCKET_MODE_URMA。 - 不引入跨 transport 的连接复用复杂度,符合 brpc「一个 Socket 一种 transport」的既有不变量(见 §3.3 of the RDMA 探索报告:A given Socket is bound to exactly one transport for its lifetime)。
- 落地快、改动小、可测、可回退。
方案 B:单连接内按包分流(远期演进,本提案不立即实现)
- 新增
SOCKET_MODE_HYBRID_UB_URMA,HybridTransport内部同时持有UBShmEndpoint与UrmaEndpoint。 CutFromIOBufList根据IOBuf总长度路由:小包走 SHM ring,大包走 URMA send。- 需要解决:双 Socket 生命周期、双握手、双 CQ 轮询、
_read_buf归并解析、WaitEpollOut双路径唤醒。复杂度高,建议方案 A 稳定后再议。 - 对应 #3217 路线 C「单后端自动切换」。
本提案先交付方案 A 的 UrmaTransport 主体,并在文档中明确小包场景建议使用 SOCKET_MODE_UBRING(#3226),形成路线 C 的「双后端各司其职」格局。方案 B 作为后续演进方向记录。
3. 与 brpc 架构的结合
3.1 Transport 接口实现映射
Transport 基类(src/brpc/transport.h)定义了 10 个纯虚方法。下表给出 UrmaTransport 每个方法的实现要点,及其与 RdmaTransport 的对应关系:
Transport 虚方法 |
UrmaTransport 实现 |
RDMA 对照 |
|---|---|---|
Init(socket, options) |
构造 UrmaEndpoint(socket);创建 fallback TcpTransport;若 options.socket_mode == SOCKET_MODE_URMA 则设 _on_edge_trigger = urma::UrmaEndpoint::OnNewDataFromTcp |
RdmaTransport::Init |
Release() |
delete _urma_ep;释放 Jetty/CQ 资源回预连接池 |
RdmaTransport::Release |
Reset(expected_nref) |
UrmaEndpoint::Reset():Jetty 置 RESET,CQ drain,状态回 UNINIT,窗口计数器归零 |
RdmaEndpoint::Reset |
Connect() |
返回 std::make_shared<urma::UrmaConnect>()(或用户 _default_connect) |
RdmaTransport::Connect |
CutFromIOBuf(buf) |
_urma_ep && _urma_state != OFF → _urma_ep->CutFromIOBufList({buf},1);否则 _tcp_transport->CutFromIOBuf(buf) |
RdmaTransport::CutFromIOBuf |
CutFromIOBufList(bufs,n) |
URMA send 路径:构建 SGE → urma_post_send → 窗口扣减;EAGAIN 时返回 -1 |
RdmaEndpoint::CutFromIOBufList |
WaitEpollOut(butex,pollin,dt) |
URMA_ON 且 !IsWritable() → butex_wait(_epollout_butex);否则委托 TCP。注意 URMA 同样无法靠 write 探测失败,wait 后需重检 _socket->Failed() |
RdmaTransport::WaitEpollOut |
ProcessEvent(attr) |
与 TCP 版本一致:把 OnEdge(socket) 调度到 bthread |
RdmaTransport::ProcessEvent |
QueueMessage(msg,n,last) |
与 TCP 版本一致;尊重 FLAGS_urma_use_polling/FLAGS_urma_disable_bthread |
RdmaTransport::QueueMessage |
Debug(os) |
输出 UrmaEndpoint::DebugInfo(状态、Jetty、窗口大小、索引) |
RdmaTransport::Debug |
数据路径流向(与 RDMA 完全一致,便于评审对照):
flowchart TD
subgraph Write["写路径"]
W1["Socket::DoWrite"] --> W2["UrmaTransport::CutFromIOBufList"]
W2 --> W3["UrmaEndpoint::CutFromIOBufList"]
W3 --> W4["构建 ibv_sge<br/>(从 IOBuf block refs + block_pool lkey)"]
W4 --> W5["urma_post_send(JETTY,<br/>wr=SEND_WITH_IMM, imm=ack_count)"]
W5 --> W6{"窗口扣减"}
W6 -->|窗口满| W7["EAGAIN -> WaitEpollOut 阻塞"]
end
subgraph Read["读路径"]
R1["URMA CQ comp-channel fd<br/>epoll 触发"] --> R2["Transport::ProcessEvent -><br/>OnEdge -> _on_edge_trigger"]
R2 --> R3["UrmaEndpoint::PollCq -> ibv_poll_cq"]
R3 --> R4["HandleCompletion"]
R4 -->|"SEND"| R5["回收 SQ 窗口<br/>WakeAsEpollOut"]
R4 -->|"RECV"| R6["cutn 进 _socket->_read_buf<br/>(zerocopy 阈值)"]
R6 --> R7["imm -> 补 _remote_rq_window_size<br/>PostRecv(1) + SendAck(1)"]
R7 --> R8["InputMessenger::ProcessNewMessage<br/>(协议解析与 TCP 完全一致)"]
end3.2 注册机制(三处改动,完全沿用 RDMA 模板)
参考探索结论:brpc 无动态注册,新 transport 需改三处:
src/brpc/socket_mode.h— 新增枚举:
enum SocketMode {
SOCKET_MODE_TCP = 0,
SOCKET_MODE_RDMA = 1,
SOCKET_MODE_UBRING = 2,
SOCKET_MODE_URMA = 3 // 新增
};src/brpc/transport_factory.cpp— 两处if/else-if,#if BRPC_WITH_URMA守卫:
int TransportFactory::ContextInitOrDie(SocketMode mode, bool serverOrNot, const void* _options) {
if (mode == SOCKET_MODE_TCP) { return 0; }
#if BRPC_WITH_RDMA
else if (mode == SOCKET_MODE_RDMA) { return RdmaTransport::ContextInitOrDie(serverOrNot, _options); }
#endif
#if BRPC_WITH_UBRING
else if (mode == SOCKET_MODE_UBRING) { return UBShmTransport::ContextInitOrDie(serverOrNot, _options); }
#endif
#if BRPC_WITH_URMA
else if (mode == SOCKET_MODE_URMA) { return UrmaTransport::ContextInitOrDie(serverOrNot, _options); }
#endif
...
}
std::unique_ptr<Transport> TransportFactory::CreateTransport(SocketMode mode) {
...
#if BRPC_WITH_URMA
else if (mode == SOCKET_MODE_URMA) {
return std::unique_ptr<UrmaTransport>(new UrmaTransport());
}
#endif
}CMakeLists.txt— 新增 option 与宏:
option(WITH_URMA "With URMA (openEuler Unified Remote Memory Access)" OFF)
if(WITH_URMA)
set(BRPC_WITH_URMA 1)
# liburma 由 dlopen 加载,无需 link;可选 link libumdk
list(APPEND BRPC_PUBLIC_LIBS ${URMA_LIB})
endif()所有 urma* 源文件统一 #if BRPC_WITH_URMA ... #else <stub> #endif,非启用构建零侵入(与 RDMA/UBRing 一致)。
3.3 建链握手(双平面:TCP 控制 + URMA 数据)
沿用 RDMA/UBShm 已验证的**「TCP 控制面 + 原生数据面」双平面**模式,避免重新发明轮子:
- 控制面:复用 brpc 现有的 TCP connect +
AppConnect钩子。TCP 连接建立后,Socket::CheckConnectedAndKeepWrite调用_app_connect->StartConnect,在后台 bthread 跑 URMA 握手。 - 数据面:握手成功后切换到 URMA Jetty 收发,TCP fd 仅保留用于 epoll 生命周期与 fallback。
客户端状态机(ProcessHandshakeAtClient,参考 RdmaEndpoint::ProcessHandshakeAtClient):
stateDiagram-v2
[*] --> C_ALLOC_JETTY
C_ALLOC_JETTY --> C_HELLO_SEND: AllocateResources()<br/>(从预连接池取 Jetty+CQ,或新建)
C_HELLO_SEND --> C_HELLO_WAIT: handshake->SendLocalHello()<br/>(发 "URMA"/"URM3" magic + 本地参数)
C_HELLO_WAIT --> C_ACK_SEND: negotiated=true<br/>ApplyRemoteHello + BringUpJetty
C_HELLO_WAIT --> C_ACK_SEND: negotiated=false → URMA_OFF
C_ACK_SEND --> ESTABLISHED: URMA_ON<br/>(WriteToFd flags_be bit0=URMA_OK)
C_ACK_SEND --> FALLBACK_TCP: URMA_OFF
ESTABLISHED --> [*]
FALLBACK_TCP --> [*]服务端状态机(ProcessHandshakeAtServer,由 _on_edge_trigger = OnNewDataFromTcp 在首次可读时拉起):
stateDiagram-v2
[*] --> S_HELLO_WAIT
S_HELLO_WAIT --> FALLBACK_TCP: 非 URMA magic<br/>(字节推回 _read_buf, TryReadOnTcp())
S_HELLO_WAIT --> S_ALLOC_JETTY: negotiated=true<br/>ApplyRemoteHello
S_HELLO_WAIT --> FALLBACK_TCP: negotiated=false -> URMA_OFF
S_ALLOC_JETTY --> S_BRINGUP_JETTY: AllocateResources()
S_BRINGUP_JETTY --> S_HELLO_SEND: BringUpJetty()
S_HELLO_SEND --> S_ACK_WAIT: SendLocalHello()
S_ACK_WAIT --> ESTABLISHED: client_ack_ok && !URMA_OFF<br/>(URMA_ON)
S_ACK_WAIT --> FAILED: client_ack_ok && URMA_OFF<br/>(SetFailed EPROTO)
S_ACK_WAIT --> FALLBACK_TCP: !client_ack_ok<br/>(URMA_OFF)
ESTABLISHED --> [*]: TryReadOnTcp()
FALLBACK_TCP --> [*]: TryReadOnTcp()
FAILED --> [*]4 字节 ACK URMA_OK=0x1 为最终共识位,双端独立决策后确认,不一致即硬错误。与 RDMA 的 HELLO_ACK_RDMA_OK 完全对称。
握手 IO 复用 ReadFromFd/WriteToFd(EAGAIN 感知循环,_read_butex 等待 + 50ms WAIT_TIMEOUT_MS),直接照搬 rdma_endpoint.cpp:316-421。
3.4 CQ 作为第二个 Socket
URMA 每连接需要一个额外可 poll 的 fd(CQ comp-channel,或 polling 模式下的 carrier)。参考 RdmaEndpoint::AllocateResources(rdma_endpoint.cpp:1159-1178)的成熟做法:
- 创建第二个 brpc
Socket,其fd为 CQ comp-channel fd,options.user = this(即UrmaEndpoint),options.on_edge_triggered_events = PollCq。 - 释放时
SetFailed()该 CQ socket 并清_user,避免 endpoint 被双重释放(参考rdma_endpoint.cpp:1370-1380)。 - Polling 模式下,参考
PollerGroup/Poller(每 bthread tag 一组),FLAGS_urma_poller_num个轮询 bthread 循环遍历注册的 CQ socket id。
3.5 内存管理
URMA 底层为 verbs 风格,内存需注册。直接复用 RDMA 的 block_pool(src/brpc/rdma/block_pool.{h,cpp}),避免维护两套 slab 池:
block_pool的RegisterCallback注入UrmaRegisterMemory(使用IBV_ACCESS_LOCAL_WRITE | IBV_ACCESS_RELAXED_ORDERING,失败回退LOCAL_WRITE,与RdmaRegisterMemory一致)。UrmaHelper::GlobalUrmaInitializeOrDieImpl中同样劫持butil::iobuf::blockmem_allocate→BlockAllocate,使所有 IOBuf block 天然 URMA 可用(参考rdma_helper.cpp:584-587)。- 用户态注册内存:复用
RegisterMemoryForRdma/DeregisterMemoryForRdma/GetLKey(在block_pool的g_user_mrs表中),或新增urma::*别名包装以命名清晰。
讨论点:URMA 与 RDMA 是否共享同一个
block_pool实例?若同进程内 RDMA 与 URMA 同时启用(一般不会),需评估设备间 lkey 命名空间。初步建议:单进程二选一,由ContextInitOrDie校验互斥。
4. 关键接口与配置
4.1 用户接口
// 客户端
brpc::ChannelOptions opt;
opt.socket_mode = brpc::SOCKET_MODE_URMA; // 选择 UrmaTransport
opt.protocol = "baidu_std"; // URMA 仅支持 baidu_std(同 RDMA)
channel.Init("127.0.0.1:8000", &opt);
// 服务端
brpc::ServerOptions sopt;
sopt.socket_mode = brpc::SOCKET_MODE_URMA;
server.Start(port, &sopt);OptionsAvailableForUrma / OptionsAvailableOverUrma 校验:拒绝 SSL、RTMP、NSHEAD、MONGO;仅允许 baidu_std 协议(与 SupportedByRdma 对齐)。
4.2 gflags(对照 RDMA 命名,urma_* 前缀)
| Flag | 默认 | 文件 | 用途 |
|---|---|---|---|
--urma_max_sge |
0 | urma_helper.cpp |
每 WR SGE 上限;0=设备上限 |
--urma_device |
"" | urma_helper.cpp |
URMA 设备名;空=首个活跃 |
--urma_sq_size |
128 | urma_endpoint.cpp |
每连接 SQ 深度([16,4096]) |
--urma_rq_size |
128 | urma_endpoint.cpp |
每连接 RQ 深度 |
--urma_prepared_jetty_cnt |
1024 | urma_endpoint.cpp |
预连接 Jetty 池大小 |
--urma_cqe_poll_once |
32 | urma_endpoint.cpp |
每次 ibv_poll_cq 上限 |
--urma_recv_zerocopy |
true | urma_endpoint.cpp |
接收零拷贝开关 |
--urma_zerocopy_min_size |
512 | urma_endpoint.cpp |
零拷贝阈值(小于则拷贝) |
--urma_use_polling |
false | urma_endpoint.cpp |
轮询 vs 事件模式 |
--urma_poller_num |
1 | urma_endpoint.cpp |
每 bthread tag 轮询器数 |
--urma_poller_yield |
false | urma_endpoint.cpp |
轮询循环 bthread_yield |
--urma_disable_bthread |
false | urma_endpoint.cpp |
内联处理消息(不走 bthread) |
--urma_trace_verbose |
false | urma_endpoint.cpp |
握手详细日志 |
--urma_client_handshake_version |
2 | urma_handshake.cpp |
握手版本(2=binary,3=protobuf) |
--urma_memory_pool_initial_size_mb |
1024 | block_pool.cpp |
内存池初始大小(复用) |
--urma_memory_pool_max_regions |
3 | block_pool.cpp |
内存池区域上限(硬上限16) |
4.3 与 UBSHMTransport 的协同约定
| 场景 | 建议 socket_mode |
理由 |
|---|---|---|
| 同机 IPC(同主机进程间) | SOCKET_MODE_UBRING |
Load/Store 亚微秒,零系统调用 |
| 超节点跨机小包(<64KB,特征/分数/控制) | SOCKET_MODE_UBRING(ub_shm_type=2 UBS-Mem)或 SOCKET_MODE_URMA |
两者均优;UBRing 更低延迟 |
| 超节点跨机大包(>=64KB,Embedding/参数) | SOCKET_MODE_URMA |
SGE 零拷贝,无 EMSGSIZE |
| 传统 RoCE/IB 数据中心 | SOCKET_MODE_URMA 或 SOCKET_MODE_RDMA |
URMA 统一语义,无 QP 爆炸 |
--urma_shm_split_threshold(默认 64KB)为方案 B(hybrid)预留 flag,方案 A 阶段仅作文档阈值参考。
5. 文件清单与改动范围
5.1 新增文件
src/brpc/
├── urma_transport.h # UrmaTransport : public Transport
├── urma_transport.cpp
├── urma/
│ ├── urma_endpoint.h # UrmaEndpoint : public SocketUser, UrmaConnect, UrmaResource
│ ├── urma_endpoint.cpp
│ ├── urma_helper.h # 全局初始化, liburma dlopen, 设备管理
│ ├── urma_helper.cpp
│ ├── urma_handshake.h # 握手策略基类 + v2/v3 子类
│ ├── urma_handshake.cpp
│ ├── urma_handshake.proto # v3 protobuf 握手(参考 rdma_handshake.proto)
│ ├── mock_urma.cpp # URMA 链接时 mock(无 liburma 时编译,供 CI 测试)
│ ├── urma_api.h # URMA SDK API 声明(vendored from umdk)
│ ├── urma_types.h # URMA SDK 类型定义(vendored from umdk)
│ └── urma_opcode.h # URMA SDK opcode/常量(vendored from umdk)
docs/
├── cn/urma.md
└── en/urma.md
example/
└── urma_performance/
├── client.cpp
└── server.cpp
test/
└── brpc_urma_unittest.cpp5.2 修改文件
| 文件 | 改动 |
|---|---|
src/brpc/socket_mode.h |
新增 SOCKET_MODE_URMA = 3 |
src/brpc/transport_factory.{h,cpp} |
两个 if/else-if 分支,#if BRPC_WITH_URMA 守卫 |
src/brpc/channel.h |
ChannelOptions::socket_mode 注释补充 URMA |
src/brpc/server.h |
ServerOptions::socket_mode 注释补充 URMA |
src/brpc/input_messenger.h |
friend class urma::UrmaEndpoint; |
CMakeLists.txt |
option(WITH_URMA ...) + BRPC_WITH_URMA 宏 + 源文件列表 |
5.3 复用文件(不改或最小改)
src/brpc/rdma/block_pool.{h,cpp}— 内存注册池(RegisterCallback注入 Urma 版本)src/brpc/rdma/rdma_helper.cpp—blockmem_allocate劫持逻辑(提取为公共或 Urma 复制一份)src/brpc/tcp_transport.{h,cpp}— fallback 依赖
6. 落地计划
Phase 1:框架与最小可用(本提案目标)
SocketMode+TransportFactory注册通路。UrmaTransport+UrmaEndpoint骨架:三态、TCP fallback、握手状态机(v2 binary)。- 数据路径:
CutFromIOBufList走urma_post_send(SEND_WITH_IMM),PollCq+HandleCompletion走 recv。 - 复用
block_pool。 example/urma_performance可跑通baidu_stdecho。- 单测
brpc_urma_unittest.cpp(参考brpc_rdma_unittest.cpp与新写的brpc_ubring_unittest.cpp)。
Phase 2:性能与协同
- 握手 v3(protobuf
UrmaHello),前向兼容。 - Polling 模式 +
PollerGroupper-tag。 - Unsignaled/solicited 完成抑制(降低 CPU)。
- 与 UBSHMTransport 的大小包协同文档与示例(方案 A)。
Phase 3:演进(留待社区讨论)
- 方案 B:
SOCKET_MODE_HYBRID_UB_URMA单连接按包分流。 - URMA-SHM 模式接入,与本机 UBRing 统一。
- GPU memory 直接注册(
urma_register_memoryover HBM),对接 GPUDirect 类语义。
7. 风险与开放问题
- 硬件/驱动依赖:URMA 需要 openEuler UB 驱动 +
liburma。非 UB 平台只能 fallback TCP。是否接受 ko 依赖已在 #3217 征询,本提案沿用其结论。CI 无 URMA 硬件时的可测试性由 §7.1 的 mock 保证。 block_pool复用边界:URMA 与 RDMA 共享内存池的设备命名空间问题,倾向ContextInitOrDie互斥校验,
Source: apache/brpc