OpenURMA Unified Bus
先说判断
我的判断是,OpenURMA 最有价值的贡献不是“比 RoCE 快 4.37 倍”,而是把四个原本纠缠在 QP 里的责任拆开,并用一套开放实现检验它们能否重新组合:
- 应用身份由每个本地发起上下文的 Jetty 保存;
- 可靠传输由每个远端主机的 TP Channel 共享;
- 短同步访问用 CPU Load/Store 与 TP Bypass 直达远端内存;
- 顺序保证不再全部常开,而由应用按服务模式、执行顺序、Fence 和完成顺序选择。
这四项不是互不相关的功能清单,而是一条依赖链:
1 | Jetty / TP Channel 拆分 |
其中,论文真正实现并评估的是一套 OpenClickNP / FPGA HLS 传输与事务层、双节点 SystemC 模型和 gem5 全系统脚手架。Controller 的片上放置与 CPU Load/Store 路径在模型中闭合,但没有在真实 CPU SoC、FPGA 板卡或 Ascend 950 上测量。这条边界贯穿全文。
QP 为何同时带来两笔成本
从应用看,RDMA READ 很像“读一个远端地址”;从设备看,它却是提交给 PCIe 外设的一项队列事务。RoCEv2 RC 的 QP 同时绑定本地应用、远端端点和可靠传输上下文,于是两类成本一起出现。
状态按连接对增长
设一台主机有 $N$ 个独立发起上下文,需要访问 $M$ 台远端主机。论文的 matched OpenRoCE 模型为每个 QP 计约 512 B 状态,于是:
$$
S_{\text{RoCE}}
\approx N \times M \times S_{\text{QP}}.
$$
这里的 $N$ 不是随意放大的“应用数”。多个线程共享同一个 Work Queue 会在队列上串行,因此论文把追求高性能时需要独立发起的上下文计入 $N$。如果一台远端主机还暴露多个独立端点,真实 QP 数可能继续乘上端点数。
当 $N=M=1024$ 时,模型得到约 104 万个 QP、536.9 MB 状态。论文进一步假设 NIC 约有 256 KB 上下文 SRAM;活跃 QP 超过缓存容量后,每项操作都可能增加一次到 Host DRAM 的状态回取。状态问题因此不只是容量问题,还会变成延迟台阶。这些数字来自论文 Table 4、Table 5 与 Figure 14 的模型,不应外推成所有商用 RNIC 都使用同样的 QP 布局和缓存大小。OpenURMA arXiv v1 §7
小操作仍要穿过外围协议
标准队列路径至少包含下面这些动作:
- CPU 构造 WQE,并通过 MMIO Doorbell 通知 NIC;
- NIC 通过 PCIe DMA 获取 WQE;
- 目标 NIC 访问目标 Host Memory;
- 响应数据和 CQE 通过 DMA 回到发起端内存;
- CPU 轮询并看到完成记录。
论文导言把发起端编程模型概括为四次 PCIe Traversal;其双端成本模型又显式加入目标 NIC 与目标 DRAM 之间的一次 DMA,因此 Figure 1 标出了五次。这不是两套互相矛盾的协议,而是单端计数与端到端计数的观察边界不同。OpenURMA arXiv v1 §2.1、§5.2
这张图最值得观察的不是蓝色路径比红色路径少了几根箭头,而是:状态拆分先让 Controller 的工作集变小,片上放置再让 Load/Store 快路径成立。只优化 Doorbell、批量提交或 QP Cache,能够削减其中一段成本,却没有改变这条依赖关系。
Jetty 与 TP Channel 怎样拆开状态
OpenURMA 不再让一个对象同时回答“谁在发请求”和“怎样可靠送到对端”两个问题。
| 对象 | 所在位置与粒度 | 保存内容 | 生命周期 |
|---|---|---|---|
| Jetty | 本地 NIC,每个独立应用发起上下文一份 | 完成队列句柄、访问 Token、类型与状态、排序计数 | 随应用上下文长期存在 |
| TP Channel | 本地 NIC,每个远端主机一份 | PSN 窗口、SACK Bitmap、重传与拥塞状态 | 随主机间传输关系长期存在,可被多个 Jetty 复用 |
| Destination Jetty ID | 每个 Packet Header | 指明远端应用端点 | 只随当前 Packet 移动 |
| WQE / CQE | Host Memory 与 Controller | 异步 Work Request 及完成记录 | 仅 URMA Work Queue 路径使用 |
| LD/ST Outstanding Entry | Load/Store Engine | 同步请求的 TASSN 与 Opcode | 请求返回后释放;论文源码表大小为 64 |
发送时,Jetty 表先完成应用级准入与排序,再按目标主机找到共享 TP Channel,由后者分配序号、选择路径并管理可靠传输。接收时顺序反过来:TP Channel 先验证和重排 Packet,再根据 Header 中的 Destination Jetty ID 投递给目标应用。
状态规模因此变成:
$$
S_{\text{UB}}
\approx N \times S_{\text{Jetty}}
- M \times S_{\text{TP}}
- S_{\text{shared}}.
$$
一个最小例子就能看出差别。2 个本地线程访问 3 台远端主机时,RoCE RC 的简化全连接模型需要 6 个 QP;UB 使用 2 个 Jetty 与 3 个 TP Channel。任何 Jetty 都能借用目标主机对应的 TP Channel,远端应用身份放进 Packet Header,而不是固化在连接对象里。
论文实现的 Jetty MVP 为 20 B,TP Channel 为 56 B;把尚未实现的完整 Jetty 状态机、异常模式与 Group Back-pointer 计入后,Jetty 预计为 48 B。在 $N=M=1024$ 时,MVP 状态为 110.6 KB,对比 OpenRoCE 的 536.9 MB,比例为 4855 倍;按完整 Jetty 估计则为 139.3 KB,比例降为 3855 倍。数量级结论来自结构拆分,4855 这个精确倍数则依赖论文的字段与对照模型。OpenURMA arXiv v1 Table 4、Table 5
拆分也有代价:每个 Packet 多携带目标 Jetty ID,收发两端各多一次索引;Jetty 与 TP Channel 成为生命周期不同的两个资源,Runtime 和故障恢复必须分别管理。它消除的是跨主机的 $N\times M$ 状态乘积,不会让多个线程共享同一 Jetty 时的队列串行凭空消失。
为什么同时保留两条数据路径
把 Controller 放到片上总线,并不意味着所有通信都应该变成 CPU 指令。OpenURMA 保留了两条互补路径。
URMA Work Queue 路径
异步 READ、WRITE、SEND、Atomic 和较大 Payload 仍由 Work Request 驱动。请求依次经过 Jetty Scheduler、Ordering、BTAH、TP Channel、Congestion Window、Retransmission、RTP Header 和 Ethernet Encapsulation。它拥有传输层可靠性、队列深度和异步并发能力,适合吞吐受限或需要多个 In-flight Operation 的工作负载。
即使这条路径仍使用 WQE / CQE,片上 UB Controller 也不再支付外围 RNIC 的 PCIe Doorbell、WQE DMA 和 CQE DMA 固定成本。论文同时比较 UB URMA 与 UB LD/ST,正是为了区分“Controller 放置位置”和“是否绕过队列”各自贡献了什么。
Load/Store 与 TP Bypass
小型 Load、Store 和同路径 Atomic 可以由 CPU 指令进入 Controller,构造事务 Header,并标记 TP_BYPASS。它不分配传输层 PSN,不占用 Retransmission Buffer,也不生成独立 CQE;Load 的数据沿指令依赖返回 CPU Register,超时后由 CPU 重新发起。
论文提交前的源码没有正式 Tag。本文把提交前约 16 分钟的 0c60151 作为接近 arXiv v1 的源码锚点,这是根据提交时间作出的版本推断,不是作者声明的 Release。其 Load/Store Topology 把发起端路径写得很直接:
1 | host_in -> doorbell -> ldst |
这段摘录只展示发起端请求与响应相关路径;目标端的 Memory Region 检查、Read / Write / Atomic Dispatch 和本地内存访问位于同一 Topology 后续分支,没有被省略为“无检查直通”。真正应注意的是,快路径没有出现 Jetty_Sched → TPChannel_TX → Cong_Window → Retrans_Buffer → RTPH_Build 链。examples/openurma_loadstore/topology.clnp L53-L75
代码仍复用了名为 doorbell 的 Host-Ingress Element 来校验模拟 Flit,因此 Topology 文本里能看到这个名字;论文的延迟模型把该输入解释为片上总线上的 Load/Store Aperture 请求,没有重新计入 PCIe MMIO Doorbell 与 WQE DMA。这里必须区分 Element 名称和物理提交路径。
按需排序怎样工作
RoCE RC 在每个 QP 内提供严格有序语义。它简单,但某项早期操作停住时,后续无关操作也可能被 Head-of-Line Blocking。UB 把顺序拆成四个可选择的轴:
| 轴 | 回答的问题 | 典型选择 |
|---|---|---|
| Service Mode | Packet 在链路与目标侧允许怎样重排 | ROI、ROT、ROL、UNO |
| Execution Order | 当前事务是否必须等待更早事务 | NO、RO、SO |
| Fence | 应用是否显式建立前后 Barrier | 开 / 关 |
| Completion Order | Completion 按到达还是提交顺序退休 | Out-of-order / In-order |
OpenURMA 将四个轴实现为四个 Pipeline Gate。请求没有选择 Gate 时,所有服务模式的冷路径都在 24 Cycle 发出第一帧;真正请求 Fence 或 Strong Order 时,论文测得额外等待低于 50 Cycle。由于 Gate 按 Jetty 的本地计数器索引,一个 Initiator 被卡住时,其他 Initiator 的 Unordered Request 仍能绕过,不需要共享一个全局严格顺序。OpenURMA arXiv v1 §9
“按需”不等于“没有一致性责任”。相反,应用或 Collective Runtime 必须知道哪些操作可交换、哪里需要 Fence、Completion 被观察时远端数据是否已经可见。对于交换律成立且最终在 Barrier 汇合的梯度聚合,放松逐操作顺序可能合理;对于分布式锁、引用计数或生产者—消费者 Flag,错误选择会直接变成正确性问题。
开放实现验证了什么
OpenURMA 的实验设计比单一模拟器更有说服力,因为同一套 .clnp Element Source 被放到三个层级,并为每层准备 matched OpenRoCE:
- 软件 Emulator验证 Wire Format、Opcode、Ordering、Atomic 与数据完整性;
- SystemC 双节点模型测量 Pipeline Cycle、双端 Wire / DRAM 成本、延迟、吞吐、丢包与拥塞;
- gem5 全系统脚手架启动 Linux 4.14、模拟 Driver 和真实 ARM Binary,测量 MMIO、Syscall、Scheduler 与 CQE Delivery 的软件开销;
- 同一 Element Source 再经 Vitis HLS 与 Vivado,在 Alveo U50 目标上做 Out-of-context 综合和 P&R。
这套结构让两个问题得以分开:RTL 能不能综合并收敛时序,以及新协议在相同成本模型下是否改变关键路径。它不能回答的,是带真实 PHY、MAC、Host Bus、Driver、Firmware、功耗和拥塞环境的物理芯片会跑出什么绝对数字。
论文 v1 报告 38 个 Work-Queue-Driven Element 全部达到 322 MHz;OpenURMA 占 122,710 LUT、194,266 FF 和 328 BRAM18,约为 U50 LUT 预算的 14.1%。matched OpenRoCE 使用 46,636 LUT,因此 OpenURMA 并不比基线“更省硬件”:它分别多用 2.63 倍 LUT、2.11 倍 FF 和 4.90 倍 BRAM18,换取更丰富的排序、选择重传、多路径与事务层能力。OpenURMA arXiv v1 Table 2、Table 3
实验数字应该怎样读
论文 Figure 19 选择了三种小操作:64 B Pointer Chase、64 B Bulk Write 和 8 B FAA Distributed Barrier。共同设置是单向 Link Delay 100 ns、Concurrency 1,并显式计入目标 NIC 到 DRAM 的访问。
| 主张 | 论文设置 | v1 结果 | 能支持的结论 |
|---|---|---|---|
| 连接状态有界 | $N=M=1024$,MVP 字段模型 | 110.6 KB vs 536.9 MB,4855 倍 | Jetty / TP Channel 拆分消除了状态乘积 |
| 短同步读取更低延迟 | 64 B、Link 100 ns、Concurrency 1 | UB LD/ST 500 ns;UB URMA 757 ns;RoCE DMA 2186 ns | 片上放置与 TP Bypass 分别减少固定提交成本 |
| 稳态 WR 吞吐更高 | 256 WR Burst,matched Pipeline | 150.36 vs 53.62 WR/μs,2.80 倍 | 当前两套模型的依赖与 Initiation Interval 不同 |
| 按需排序不增加无阻塞级数 | 12 种 Service / Execution 组合 | 第一帧均为 24 Cycle;按需 Gate 低于 50 Cycle | 未请求的 Gate 可旁路,阻塞按 Initiator 隔离 |
| 资源可综合 | U50、322 MHz、Out-of-context P&R | 14.1% LUT,全部 Element 正 WNS | 传输与事务层在该 FPGA 目标上具备实现可行性 |
其中最容易被误读的是 4.37 倍。它比较的是同一模型中“CPU 获取 64 B 远端数据”的两种等价语义:UB 用 §8.3 LOAD,RoCE 用 RC READ。它没有比较经过多年产品优化的 ConnectX-7 与量产 Ascend 950,也没有证明所有 URMA Verb 都能获得 4.37 倍。
论文自己给出了三个收敛区间:
- 16–64 KB Bulk Transfer 进入带宽受限区后,各方案相差约 5%;
- RoCE Blue Flame 的 WRITE / SEND 能省去 WQE DMA,差距可缩到 2 倍以内;
- 小于约 23 个活跃端点时,论文模型中的 QP Cache 尚未溢出,UB 只剩每操作固定路径优势。
YCSB-A 的 9.2 倍吞吐同样需要谨慎。论文在 10K 个 64 B Value、Zipfian Key、Concurrency 256 下得到 UB LD/ST 4.6 Mops/s、RoCE DMA 0.50 Mops/s,并把超过 4.37 倍的部分归因于热点 Key 的 CPU Cache 命中;作者估计更现实的 Set-associative Cache 会把倍数降到约 6 倍。与此同时,gem5 AtomicCPU 并不能定量区分 WB / WT / UC Cache Policy,这部分真实 Cache Hierarchy 收益仍需要 Timing CPU 或物理系统验证。OpenURMA arXiv v1 §8.2、§8.3、§10.2
论文与滚动源码不要混读
调研时,OpenURMA 没有与 arXiv v1 对应的正式 Git Tag。论文于 2026 年 5 月 27 日提交;当前仓库 main 已继续增加 UMDK 集成、应用覆盖和 Pipeline Data Path,数字也发生了变化。
| 口径 | 源码锚点 | Remote Fetch | Pipeline 描述 |
|---|---|---|---|
| arXiv v1 | 提交前的 0c60151 作为近似锚点 |
500 ns vs 2186 ns,4.37 倍 | 论文报告 38 个 Work-Queue-Driven Element |
调研时 main |
0ae5dce,2026-06-12 |
README 为 500 ns vs 2236 ns,4.47 倍 | README 清单为 41 个 UB Element |
因此,本文所有实验结论固定到 arXiv v1;当前仓库只用于说明后续工程状态。若要复现,应该先选择论文口径还是滚动 main,记录 Commit、OpenClickNP 版本和 Result CSV,再比较输出。直接在当前 main 运行 ./reproduce.sh smoke,不应期待得到 v1 的 2186 ns。OpenURMA 当前 README
官方 openEuler UMDK 提供 URMA 的单边、双边和 Atomic 软件接口。OpenURMA 当前仓库声称可在 gem5 中运行未修改的 UMDK Stack,这是比早期自定义 libopenurma Harness 更接近真实软件面的进展;它仍然是模拟平台上的集成,不应写成物理 UB NIC 已经开放。
适用场景与工程缺口
这套方案最值得投入的场景具有几个共同特征:
- 端点扇出很大,QP Working Set 已经成为 NIC SRAM 或 Host Memory 压力;
- 8–64 B 短操作很多,Doorbell、DMA 和 Completion 的固定成本高于 Payload 传输;
- Pointer Chase、KV Hot Row 或细粒度 Atomic 有局部性,同步 Load/Store 能进入 CPU Cache Hierarchy;
- 平台能共同设计 CPU / NPU、片上总线与 Controller,而不是只能把标准 RNIC 插在 PCIe 后面;
- 应用能准确表达 Ordering,愿意用 Fence 和 Completion Contract 换取 Relaxed Path。
以下场景更适合继续使用成熟 RDMA,或者至少保留双路径:
- 主要负载是 16 KB 以上的大块顺序传输,瓶颈已转向链路带宽;
- 需要跨厂商互通、成熟 Driver、Telemetry、Tenant Isolation 和现场故障工具;
- 现有系统已经充分使用 Blue Flame、Inline WQE、Batch、Unsignaled Completion 与 GPUDirect;
- 无法把 Controller 集成到片上总线,只能在 PCIe FPGA / SmartNIC 上模拟协议功能。
如果把 OpenURMA 当作工程起点,我会优先补齐六项验证,而不是先追更漂亮的平均延迟:
- 固定 Spec 与源码版本:建立 UB 2.0.1 字段、OpenURMA Commit 和 UMDK ABI 的一一映射;
- 重做状态账本:用目标并发、Endpoint、MR、TPG、Retransmit Window 和 Tenant 数计算真实 SRAM 上界;
- 定义双路径阈值:明确哪些 Opcode、Payload、并发和可靠性进入 LD/ST,哪些退回 URMA WR;
- 证明 Timeout 与幂等:覆盖 Load Retry、Store 重放、Atomic 重复执行和故障恢复;
- 补齐物理路径:在真实 Host Bus、Driver、PHY 和 DRAM 上测 Mean、Tail、Power 与 Congestion;
- 保留 matched baseline:同硬件、同拓扑、同 Payload、同 Completion Mode 对比生产级 RoCE,而不是跨论文抄倍数。
结论
回到开头那次 64 B 读取,OpenURMA 的答案不是把某个 DMA Kernel 再压缩几十纳秒,而是追问:既然应用想表达一次远端 Load,为什么必须先把它包装成 PCIe 外设的队列事务?
Jetty / TP Channel 拆分让状态从乘法变成加法;有界状态让 Controller 有机会留在片上;片上 Controller 让 Load/Store 与 TP Bypass 成为真正不同的短路径;按需排序再避免为不需要的顺序持续付费。这条依赖链,是论文已经用开放实现和 matched model 支持的核心结论。
尚未被证明的是量产性能:4.37 倍来自 arXiv v1 的特定模型,OpenRoCE 不是生产 ConnectX,OpenURMA 也不是物理 Ascend 950。更稳妥的定位是:它已经把 Unified Bus 从一份难以测量的规范推进为可检查、可综合、可复现实验的研究载体;下一步应该用真实硅、完整 Driver 与生产 Workload 检验这条架构链,而不是把模拟数字提前写成产品承诺。
参考资料
- Bojie Li, OpenURMA: A Clean-Room Open Implementation of the Unified Bus Protocol, arXiv v1, 2026-05-27;HTML 全文。
- OpenURMA, 接近 arXiv v1 的源码锚点
0c60151。该对应关系由提交时间推断,仓库未提供正式论文 Tag。 - OpenURMA, 调研时
main:0ae5dce,用于核对论文之后的实现与复现口径变化。 - openEuler, Unified Memory Development Kit,用于核对 URMA 软件接口与公开组件边界。
- Bojie Li, OpenClickNP,OpenURMA 的 Element Compiler、Software Emulator、SystemC 与 FPGA HLS 基础工具链。