本文分析 NVSHMEM GPU-to-GPU 通信过程种的 Memory Ordering 和 Synchronization,覆盖四类典型数据路径:跨节点 IBRC、IBGDA ,以及 Peer GPU 可直接访问时的 PCIe P2P、NVLink P2P 。重点关注 PUT、GET、Signal/Wait、Fence/Quiet、Signal、P2P system-scope ordering、GPUDirect RDMA Write Ordering 与 CST 。分析始终区分两个角色:Initiator(请求发起方) 与 Target(请求接收方)。
核心结论如下:
| 场景 | 结论 |
|---|---|
| PUT / Initiator | 若只要求后续远端操作不能越过前面的 PUT,使用 nvshmem_fence();若下一阶段必须等待前序通信完成,使用 nvshmem_quiet()。 |
| PUT / Target | NVSHMEM 使用 payload → ordered signal→ wait(signal) → read(payload) 建立接收侧发布/消费关系。 |
| GET / IBRC、IBGDA | GPU 消费 GET payload 前,需要 native GDR write ordering,或 requester-side consistency(IBRC enforce_cst() / IBGDA CST)。 |
| GET / PCIe P2P | Blocking GET 等待 read response 返回后完成。 |
| PUT / PCIe P2P | 顺序由 NVSHMEM fence/P2P system-scope ordering 建立,Target 仍通过 signal/wait 判断何时消费 payload。 |
| NVLink P2P | 数据同步仍使用 system-scope fence/quiet 与 signal/wait。GET 则等待 peer read 的返回数据完成。 |
| GDR Ordering / CST | 这两类机制只解决 RNIC 发起的 GPUDirect RDMA Write 写回 GPU 后,GPU consumer 的可见性。 |
1. 通信模型
1.1 架构
每台主机内部采用相同的 PCIe 拓扑:Host / CPU → Root Complex → PCIe Switch → GPU / RNIC。在每台主机内,两个 GPU 同时连接到 PCIe Switch,并可通过主机内 PCIe P2P 直接互访;两个 GPU 下方还连接到 NVLink Fabric ,因此同一主机内的 GPU 对还可以通过 NVLink P2P 通信。跨主机场景下,两端 RNIC 通过 IB/RoCE Fabric 连接。

同一主机内两个 GPU 之间则存在两条 P2P fast path:
GPU ↔ PCIe Switch ↔ Peer GPU(PCIe P2P)GPU ↔ NVLink Fabric ↔ Peer GPU(NVLink P2P)
IBRC 与 IBGDA 的核心区别在于谁驱动 Initiator 侧 RNIC:
- IBRC:GPU 发出的 NVSHMEM 请求进入 Host Proxy,由 CPU proxy thread 驱动 RNIC;
- IBGDA:GPU 直接构造/提交 RNIC WQE,并直接处理 completion,CPU 不处于 fast path。
本文关注以下核心一致性问题:
| 场景 | 核心问题 | 典型机制 |
|---|---|---|
| PUT / Initiator | 后续操作是否允许越过前面的 PUT;若下一阶段必须等 PUT 完成,如何等待 | nvshmem_fence()、nvshmem_quiet() |
| PUT / Target | Target 如何知道 payload 已可消费 | payload → signal/AMO → wait → read |
| GET / RNIC path | RNIC→GPU 的最后一次 PCIe Write 是 Posted,GPU 如何确认 result 已可见 | native GDR ordering、flush / CST |
| PUT / P2P | GPU→Peer GPU 的 write 如何与后发 signal 保序 | P2P system-scope ordering、fence、signal/wait |
| GET / P2P | Peer read 返回后是否还需要 requester-side CST | 直接 peer read;无 RNIC writeback,因此无需 CST |
1.2 常用接口
本文只分析与 Memory Ordering 和 Synchronization 相关的常用接口。
| 接口定义 | 使用场景 | 接口作用 |
|---|---|---|
nvshmem_TYPENAME_put(dest, source, nelems, pe) |
把本地 payload 推送到远端 PE | Blocking PUT;保证本地调用所要求的完成语义,但 Target 若要判断数据何时可消费,仍需要显式同步 |
nvshmem_TYPENAME_get(dest, source, nelems, pe) |
从远端 PE 拉取数据到本地 | Blocking GET;返回结果最终写回 Initiator 本地内存 |
nvshmem_fence() |
PUT 后还要发布 signal、tail、counter 等依赖前序数据的更新 | 保证同一 Target 上前后通信的 delivery ordering,不保证 completion |
nvshmem_quiet() |
下一阶段必须等待此前通信完成 | 等待此前由本 PE 发起的相关操作完成;GET 路径必要时还要补足本地 GPU visibility |
nvshmemx_signal_op(sig_addr, signal, sig_op, pe) |
payload 与通知分开发送 | 更新远端 symmetric signal;典型用法为 PUT → fence → signal |
nvshmem_putmem_signal(...) / typed put_signal |
payload 与 signal 作为一个关联发布操作 | 先传输 dest 数据,随后更新远端 signal;signal 更新用于通知 Target |
nvshmem_signal_wait_until(sig_addr, cmp, val) |
Target 等待远端数据发布完成 | 等待本 PE 的 signal 满足条件,再进入 payload 消费阶段 |
1.3 ring-bcast
nvshmem中的ring-bcast例子可以很好解释PUT / Initiator 和 PUT / Target 之间的同步,examples/ring-bcast.cu 的核心代码可简化为:S9
cpp
nvshmem_signal_wait_until(psync, NVSHMEM_CMP_NE, 0);
nvshmem_int_put(data, data, nelem, peer);
nvshmem_fence();
nvshmemx_signal_op(psync, 1, NVSHMEM_SIGNAL_SET, peer);

这个例子把数据和同步明确分开:
put(data):传输 payload;fence():保证该 payload 先于后续 signal delivery;signal_op():发布"前序数据已经可以按协议消费"的同步状态;signal_wait_until():Target 以 signal 为消费同步点,而不是等待发送端 CQE。
2. IBRC
2.1 概述
IBRC 的特点是 控制路径经过 CPU Proxy,而 payload 数据仍由 RNIC 通过 GPUDirect RDMA 直接访问 GPU memory。Initiator GPU 把 NVSHMEM 请求交给 Host 侧 Proxy;CPU Proxy 再驱动本地 RNIC 发起 RDMA 操作。Target 侧 one-sided 数据访问通常由 RNIC 直接访问 GPU memory,不需要 Target CPU Proxy 参与。

图 3:IBRC 的控制路径与数据路径。
2.2 PUT
IBRC PUT 需要同时解决两个问题:Initiator 侧的发送顺序/完成 ,以及 Target 侧的数据完成通知。

2.2.1 发送端
GPU A 先把 PUT 请求写入 Proxy channel,CPU Proxy A 再向 RNIC A 提交 RDMA WRITE。Target RNIC B 收到数据后,通过 PCIe Memory Write 把 payload 写入 GPU B。
发送端根据后续操作的要求选择同步手段:
- 只要求顺序 :使用
nvshmem_fence(),保证同一 Target 上 fence 前的 PUT 先于后续 signal/tail/counter 等远端更新 delivery; - 必须等待发送完成 :使用
nvshmem_quiet()。CPU Proxy 等待相关 RNIC/QP/CQ completion,再向 GPU 返回 quiet ack。S1S4S6
需要注意:Target RNIC→GPU B 的 PCIe Memory Write 是 Posted Request ,没有 PCIe Completion。因此 sender 的 RDMA completion/quiet 解决的是 Initiator completion,不能替代 Target 的数据完成通知。
2.2.2 接收端
Target 侧使用 signal 作为数据完成通知。典型过程是:
PUT(payload) → fence / ordering → signal update → Target wait(signal) → read(payload)
signal 是 Target PE 的 symmetric object,通常位于 GPU 可直接访问的 symmetric memory 中;Target GPU 直接执行 nvshmem_signal_wait_until(),不需要 Target CPU Proxy 转发通知。
对于 native SIGNAL_ADD 路径,CPU Proxy A 向 RNIC A 提交 RDMA Atomic,RNIC B 最终更新 GPU B 上的 signal;该 AMO 的作用是发布位于 payload 之后的同步状态 。为什么 Target 观察到 signal 后可以安全读取 payload,统一在 6.3 AMO Signal 中分析。S2S5
典型代码:
cpp
nvshmem_int_put(dst, src, n, peer);
nvshmem_fence();
nvshmemx_signal_op(signal, 1, NVSHMEM_SIGNAL_ADD, peer);
// Target GPU
nvshmem_signal_wait_until(signal, NVSHMEM_CMP_EQ, 1);
consume(dst);
因此 IBRC PUT 的职责可以概括为:Proxy/quiet 解决发送端 completion,fence 解决发送顺序,signal/wait 解决接收端数据完成通知。
2.3 GET
IBRC GET 的关键问题位于 Initiator:远端数据虽然已经通过 RDMA READ 返回 Local RNIC,但最后还要由 Local RNIC 以 PCIe Posted Write 写入 Initiator GPU。

基本过程为:
- GPU A 把 GET 请求交给 CPU Proxy A;
- Proxy A 提交 RDMA READ;
- RNIC B 通过 PCIe Read 从 GPU B 取数,PCIe Read 有
CplD; - RDMA Read Response 返回 RNIC A;
- RNIC A 通过 PCIe Memory Write 把 result 写入 GPU A;该 Write 是 Posted,没有 Cpl;
- GPU A 在确认本地可见性后读取 result。
因此 IBRC blocking GET / quiet 的终点不能只是"网络 Response 已返回"。当前 Proxy 在 transport quiet 完成后,如果此前发起过 GET,会调用 enforce_cst(),完成 requester-side consistency 后才向 GPU 返回 quiet ack。S4
enforce_cst() 的选择依据是本 GPU 的 CUDA GDR 一致性能力和当前 transport 能力 这里的选择细节和 CST 原理统一放到 6.4 GDR Ordering 与 6.5 CST 中展开。
3. IBGDA
3.1 概述
IBGDA 的核心特点是 GPU 直接驱动本地 RNIC。GPU 在 device 侧生成并提交 RNIC 请求,CPU Proxy 不处于 fast path;payload 的跨节点数据路径仍然经过本地 RNIC、IB/RoCE Fabric、远端 RNIC,最终访问 Target GPU memory。

IBGDA 与 IBRC 面临的 PUT/GET 一致性问题类型相同,主要区别是 ordering、completion 和 consistency 操作由 GPU 与 RNIC 直接协作完成。
3.2 PUT
IBGDA PUT 与 IBRC 的一致性目标相同,但控制路径不经过 CPU Proxy:GPU A 直接构造并提交 RNIC WQE。

3.2.1 发送端
GPU A 先提交 payload 的 RDMA WRITE,再提交 signal 对应的同步操作。发送端同样区分两种需求:
- 只要求顺序 :依赖同一 QP/WQE ordering,或使用
nvshmem_fence(); - 必须等待发送完成 :使用
nvshmem_quiet()/ibgda_quiet()等待对应 WQE/CQ completion。S1S7
Target RNIC B→GPU B 的 payload write 仍然是 PCIe Posted Write,没有 PCIe Cpl,因此 sender CQ completion 不能直接充当 Target 的数据通知。
3.2.2 接收端
Target GPU B 依赖后发 signal:
RDMA WRITE(payload) → ordered signal → wait(signal) → read(payload)
对于 signal 路径,GPU A 直接构造 signal Atomic WQE;Target GPU B 轮询 symmetric signal。接收侧不执行 CST,也不需要知道 payload 的精确到达时刻。AMO signal 为什么可以代表前序 payload 已经达到可消费顺序点,在 6.3 AMO Signal 中统一分析。S2S7
因此 IBGDA PUT 可以概括为:GPU 直接控制 RNIC;fence/QP ordering 保证发送顺序,quiet 等待 sender completion,signal/wait 提供 Target 数据完成通知。
3.3 GET
IBGDA GET 同样存在"返回数据最后由 Local RNIC 以 PCIe Posted Write 写入 Local GPU"的问题。

数据路径为:
GPU A → RNIC A → RDMA READ → RNIC B → PCIe Read GPU B → Read Response → RNIC A → PCIe MemWr(result) → GPU A
远端的 PCIe Read 是 Non-Posted,有 CplD;但本地 result writeback 是 Posted Write,没有 Cpl。因此 GPU A 在读取 GET result 前,需要一个 requester-side consistency point。
当前 IBGDA 使用 may_skip_cst 决定是否追加 DUMP/CST。详细判断条件、DUMP/CST 的 PCIe Read 行为,以及为什么它能补足 Posted Write 无 Cpl 的缺口,放在 6.4 GDR Ordering 与 6.5 CST 中说明。
4. PCIe P2P
4.1 概述
当两个 GPU 具备 CUDA Peer-to-Peer memory access 能力时,一个 GPU kernel 可以直接解引用另一个 GPU 的 peer-mapped memory。这种能力取决于 PCIe/NVLink 拓扑,并可通过 cudaDeviceCanAccessPeer() 判断;NVSHMEM 中也可以用 nvshmem_ptr() 判断远端 PE 是否 P2P-accessible。S12S14

本文图示采用两个 GPU 位于同一 PCIe Switch 下的典型拓扑。实际 TLP 是否直接在 Switch 内横向转发、是否被 ACS 重定向 upstream,取决于平台 PCIe 拓扑和配置;只要 peer access 成立,GPU A 就可以通过 peer-mapped address 访问 GPU B memory。S12
4.2 PUT
PCIe P2P PUT 的同步目标仍然是两件事:Initiator 保证发送顺序/完成 ,以及 Target 在正确的发布点之后读取 payload 。

4.2.1 发送端
GPU A 对 GPU B 的 peer-mapped destination 执行 PUT 时,底层可形成 GPU 发起的 PCIe P2P Memory Write。PCIe Memory Write 是 Posted Request,没有 Completion。
NVSHMEM 使用两个同步层次:
nvshmem_fence():在 payload 之后、signal/tail/counter 等发布操作之前建立 system-scope ordering,保证后发布状态不会越过前序 payload;nvshmem_quiet():当 Initiator 的下一阶段必须等待此前 P2P 操作达到 NVSHMEM completion point 时使用。当前 P2P LD/ST 路径通过__threadfence_system()建立这个完成/可见点;若采用异步 TMA,则先 drain TMA group,再执行 system-scope fence。详细实现见 6.1 Fence 和 6.2 Quiet。S18
4.2.2 接收端
Target GPU 按以下顺序消费数据:
P2P PUT(payload) → fence/order → signal → signal_wait_until(signal) → read(payload)
fence 保证 payload 与 signal 的顺序,Target 观察到 signal 满足条件后,再读取对应 payload。如果使用 SIGNAL_ADD 等 AMO signal,底层 atomic transport 由平台/NVSHMEM transport 决定。当前 NVSHMEM 发布说明仍指出,纯 PCIe P2P 系统若要使用 NVSHMEM atomics API,需要 InfiniBand 支持,或者使用 UCX 在没有 IB 时提供 atomics。S15
4.3 GET
PCIe P2P GET 使用 PCIe Memory Read 请求/完成模型。

PCIe Memory Read 是 Non-Posted Request。Target 返回 Completion with Data,读取结果回到 Initiator GPU。Blocking GET 在 read response 返回并完成本地 destination 更新后返回;随后 GPU A 可以读取 GET result。
因此 PCIe P2P GET 的同步方法就是:等待 Peer Read 的 CplD(data) 返回,并完成本地 copy/result 更新。对于 nonblocking GET,则由后续 NVSHMEM completion 操作等待对应 outstanding read 完成。
5. NVLink P2P
5.1 概述
NVLink P2P 与 PCIe P2P 使用相同的 Peer GPU memory 编程模型,但物理事务由 NVLink/NVSwitch 承载。CUDA 多 GPU文档把 NVLink 与 PCIe 都作为 peer access 的底层互连。S12

NVLink 事务说明如下:S20S21
- PUT / Write :Initiator 发送 Write Command/Data,Target 接收后返回 Write ACK;无错误时多个 Write ACK 可以被合并;
- GET / Read :Initiator 发送 Read Request,Target 通过 Response Data 返回读取的数据;
- Atomic:请求与 response/result 由 NVLink 自身事务协议完成。
因此本文把 NVLink P2P 画成:
- PUT:
Write Data → Target,随后Write ACK → Initiator; - GET:
Read Request → Target,随后Response Data → Initiator。
Write ACK 是 NVLink 协议级确认。NVSHMEM 对程序暴露的同步点仍然由 fence/quiet 和 signal/wait 定义:协议 ACK 由 GPU/NVLink 硬件处理,软件无法直接获取 ACK。
5.2 PUT
NVLink P2P PUT 的同步结构是:
PUT(payload) → system-scope fence/order → signal → Target wait → read(payload)

5.2.1 发送端
GPU A 把 payload 写入 GPU B peer memory。普通 P2P 路径可以使用 peer ld/st;符合条件的操作也可以使用 TMA-backed NVLink PUT。S16S18
同步方法为:
nvshmem_fence():建立前序 payload 与后发 signal/update 的 system-scope ordering;nvshmem_quiet():等待此前 P2P 操作达到 NVSHMEM 定义的 completion point。普通 peer LD/ST 路径通过__threadfence_system()等待 system-scope fence 退休;TMA path 先等待 TMA group 完成,再执行 system-scope fence。S18
5.2.2 接收端
Target GPU 使用 signal 作为 payload 的发布点:
signal_wait_until(signal) → read payload
Target 观察到 signal 后即可按 NVSHMEM 语义读取对应 payload。signal 可以是 SET,也可以是 NVLink 支持路径上的 system-scoped AMO。S17
5.3 GET
NVLink P2P GET 是明确的 Read Request / Response Data 数据流。

对于普通 peer-load 实现:
- GPU A 对 GPU B peer address 发起 Read Request;
- Read Request 经 NVLink/NVSwitch 到达 GPU B;
- GPU B 返回 Response Data;
- Response Data 回到 GPU A,并形成/写入本地 GET result;
- blocking GET 在本地 result 就绪后返回,随后 GPU A 可以消费数据。
NVIDIA CUPTI 对 NVLink response traffic 的说明明确指出,Response Data 包含 read request 的返回数据。S20
符合条件的 block-scoped GET 还可以选择 TMA-backed NVLink GET。TMA copy 是异步操作,此时由 TMA completion/group-wait 等待 outstanding copy 完成;随后本地 consumer 再读取 GET result。S16S18
6. 一致性机制
6.1 Fence
NVSHMEM 对 fence 的定义是:对同一 Target PE,fence 前的相关 symmetric-memory 操作必须先于 fence 后的相关操作 delivery;fence 保证 ordering,不保证 quiet 所要求的 completion。S1
典型用途是:
PUT(payload) → fence → signal/tail/counter
6.1.1 RDMA
当前 NVSHMEM transport 按实际 QP/transport 建立等价 ordering:S6S7
- 单 QP:主要依赖 RC QP/WQE 的顺序规则;
- 多 QP / 多路径:需要同步相关 QP,避免 fence 后的操作经另一 QP 提前 delivery;当前 IB common transport 在涉及多个 QP 时会先执行 quiet,再建立后续 ordering;
- IBGDA:由 GPU 侧 transport/QP ordering 建立相同的 fence 语义。
因此 RDMA 场景的 nvshmem_fence() 本质上是 NVSHMEM API ordering → RDMA transport ordering 的映射。
6.1.2 P2P
当前 NVSHMEM device P2P 路径中,nvshmemi_fence() 的关键实现是:S18
cpp
nvshmemi_tma_drain_if_registered();
if (!nvshmemi_use_ldst_path())
nvshmemi_transfer_fence(...);
__threadfence_system();
对于普通 peer LD/ST,核心是 __threadfence_system()。CUDA 将它定义为 system-scope sequentially-consistent fence,等价于:
cpp
cuda::atomic_thread_fence(cuda::memory_order_seq_cst,
cuda::thread_scope_system);
其可见范围包括当前 GPU、Host 以及 peer devices。S13
PTX ABI 对 system-scope SC fence 的映射是:
text
fence.sc.sys;
S19
因此 thread_scope_system 的底层机制可以理解为:GPU memory subsystem 建立一个 system-scope ordering point 。在该 fence 退休前,前序 peer-memory 写不能在系统可观察顺序上落到 fence 后发布的 signal/update 之后。CUDA/PTX 保证的是 .sys scope 的 memory-ordering 语义。
如果前序 PUT 使用 TMA,nvshmemi_tma_drain_if_registered() 会先执行:
text
TMA commit_group → wait_group 0
把当前线程 pending 的 TMA bulk operations drain 掉,然后再执行 __threadfence_system(),从而把异步 copy completion 与 system-scope ordering 串起来。S18
同一套 system-scope fence 可以覆盖 PCIe P2P 和 NVLink P2P;区别只在 peer memory transaction 实际走哪一种互连。
6.2 Quiet
nvshmem_quiet() 要求此前由调用 PE 发起的相关操作达到 NVSHMEM 定义的 completion point。S1
6.2.1 RDMA
IBRC:
GPU quiet → CPU Proxy → transport quiet → poll QP/CQ → 必要时 requester consistency → quiet_ack → GPU
IB common transport 等待相关 QP completion;如果此前包含 GET,Proxy 返回 quiet ack 前还可能执行 enforce_cst()。S4S6
IBGDA :GPU 直接执行 transport/ibgda_quiet(),等待对应 WQE/CQ completion。对于 GET,如果 native GDR ordering 不足,还需要 CST 才能允许 GPU 消费返回结果。S7
6.2.2 P2P
当前 NVSHMEM device 实现中,P2P quiet 的关键代码是:S18
cpp
nvshmemi_tma_drain_if_registered();
if (nvshmemi_use_ldst_path()) {
if (!myIdx)
__threadfence_system();
nvshmemi_threadgroup_sync<SCOPE>();
}
也就是说,P2P quiet 的软件等待点非常明确。
普通 peer LD/ST PUT:
- GPU 线程已经发出对 peer-mapped address 的 store;
quiet执行__threadfence_system();- 调用线程在 system-scope fence 退休之前不能继续越过该完成点;
- thread/warp/block 版本再执行对应的 threadgroup sync;
quiet返回后,NVSHMEM 将此前 P2P stores 视为已经达到 API 所要求的 completion/visibility point。S18
NVSHMEM 源码对这一点的注释非常直接:__threadfence_system is required for P2P store visibility。S18
对于 NVLink,底层协议会返回 Write ACK;但 quiet 暴露给程序的等待对象不是"某个可见的软件 ACK 队列",而是 system-scope fence。NVSHMEM 依赖 CUDA system-scope fence 语义,而不是要求应用处理 ACK。S13S21
TMA-backed NVLink PUT/GET:
TMA 是异步 copy,因此 quiet 首先调用 nvshmemi_tma_drain_if_registered()。源码定义该 helper 等价于:S18
text
commit_group → wait_group 0
wait_group 0 等待此前 TMA bulk operations 不再 outstanding;随后 __threadfence_system() 建立跨 GPU system-scope completion/ordering。因此 TMA PUT 的等待链可以概括为:
TMA copy → commit_group → wait_group 0 → fence.sc.sys → quiet returns
对于 TMA GET,wait_group 0 负责等待异步 read/copy 返回并完成本地 destination 更新;blocking GET/quiet 返回后,consumer 才继续使用结果。S18
这里还要注意:P2P fence 与 quiet 在当前 LD/ST fast path 都可能使用 __threadfence_system(),但 API contract 不同。 fence 只承诺 ordering;quiet 承诺 completion。当前实现使用一个足够强的 system-scope fence 同时满足这两个不同层次的 API 要求。
6.3 AMO Signal
AMO signal 是 PUT 接收侧最重要的同步机制之一。对于 IBRC/IBGDA native AMO 路径,它用于解决:payload 的最后一步 RNIC→GPU PCIe Memory Write 是 Posted、没有 Completion,Target 如何获得一个可靠的"数据可消费"通知。 。
6.3.1 WRITE → Atomic
对于 native AMO signal 路径,可以把端到端关系写成:
Requester: RDMA WRITE(payload) → RDMA Atomic(signal)
Responder: PCIe MemWr(payload) → PCIe AtomicOp(signal)
Consumer: wait(signal) → read(payload)
IB 协议 TRANSACTION ORDERING 可用于解释 NVSHMEM AMO signal 的数据同步:
Requester 先发送 RDMA WRITE,再发送有序的 Atomic;当 Responder/Target 已经观察到 atomic operand 被更新,说明后发 Atomic 的目标内存更新已经发生。按 WRITE→Atomic ordering,前序 RDMA WRITE 必须已经先达到其目标更新/placement ordering point,因此 Target 可以把该 atomic signal 当作前序 payload 的发布完成标志。
这也是 AMO 在这里的真正用途:不是 Fetch 返回值,而是对同步变量进行后发、原子、可观察的更新。
6.3.2 PCIe Ordering
对于 native GPU atomic path:
- payload:
PCIe Memory Write,Posted,无 Cpl; - signal:
PCIe AtomicOp,Non-Posted,有 Completion。
PCIe 默认 ordering 规则中,后发 Non-Posted Request 不能越过之前的 Posted Request;Atomic Operation 是 Non-Posted transaction,Completer 必须返回 Completion。S11
因此可以把后发 Atomic 看成一个 ordered witness:
MemWr(payload) → AtomicOp(signal) → Atomic Completion
当 GPU 自己已经从 signal memory 观察到新值时,atomic update 必然已经生效;在上述 ordering 条件下,前面的 payload Posted Write 已经先越过对应 ordering point。
这正好弥补了"PCIe MemWr 没有 Completion"这一缺口。
6.3.3 适用边界
上面的 IB WRITE → Atomic → PCIe AtomicOp 推导直接适用于 native AMO signal 路径,例如当前 IBRC native SIGNAL_ADD,以及 IBGDA atomic signal path。S5S7
不能把它扩大成"任何 signal API 都一定生成 PCIe AtomicOp":当前 IBRC SIGNAL_SET 可以用 RDMA Write 实现。S5 NVSHMEM API 层仍要求 signal update 对 signal 对象具有规定的原子/同步语义,因此非 native-Atomic 编码由 transport 的其他 ordering 实现满足同一 API contract。S2
同时,WRITE 与 Atomic 必须处于需要互相排序的 communication/ordering domain;不能通过 Relaxed Ordering 等方式破坏这条依赖链。S11
6.3.4 P2P Signal
P2P 的同步目标与 RDMA 相同,仍然是:
peer write(payload) → ordered signal update → Target wait → read payload
- NVLink P2P:NVSHMEM 支持 system-scoped AMO/signal,signal 可以直接作为跨 GPU 原子发布操作。S17
- PCIe P2P :
SIGNAL_SET可以按 peer write 的发布模型理解;而 NVSHMEM atomics API 是否使用 native GPU PCIe AtomicOp 取决于平台/transport。当前 3.7.x 发布说明仍要求 PCIe P2P 系统提供 IB 或 UCX 来支持 NVSHMEM atomics。S15
因此,P2P 数据一致性应从 NVSHMEM fence + signal/wait 的 API 顺序 理解,而不是无条件假设底层一定存在 PCIe AtomicOp。
6.4 GDR Ordering
CU_DEVICE_ATTRIBUTE_GPU_DIRECT_RDMA_WRITES_ORDERING 描述 GPUDirect RDMA writes 到 GPU 后,在指定 consumer scope 内是否已经具备足够的 native ordering。若返回能力覆盖 OWNER,本 GPU 消费这类 incoming writes 时可以不执行额外 flush。S8

图 15:GPU 读取 GET 返回数据时,GDR Ordering 与 CST 的选择。
本文只在 GPU 读取由 Local RNIC 写回本地 GPU 的 GET 返回数据 时使用该属性做一致性判断。PCIe P2P/NVLink P2P GET 不经过 Local RNIC writeback,因此明确排除在外:
- IBRC GET :CPU Proxy 的
enforce_cst()根据 native ordering 与 CUDA flush 能力决定是否 flush; - IBGDA GET :
may_skip_cst根据 native ordering 以及 transport 条件决定是否追加 DUMP/CST。
PUT Target、PUT+Signal、signal_wait_until()、PUT sender 的 fence/quiet,以及 PCIe/NVLink direct P2P PUT/GET ,都 不使用该属性做动态判断。
CUDA 定义的典型 ordering scope 包括:S8
NONE:不能依赖 native ordering,需要 flush/consistency;OWNER:本 GPU 可以一致消费 remote writes;ALL_DEVICES:更大的 CUDA device consumer scope。
6.5 CST
CST(Consistency Transaction)解决的是 GET requester-side 的最后一跳:
Local RNIC --PCIe Posted Write(result)--> Local GPU
因为 Posted Write 没有 PCIe Completion,需要用 native ordering 或一个后续有序的 consistency transaction,把"RNIC 已完成"推进到"GPU 可以安全读取 result"。
6.5.1 IBRC:enforce_cst()
当前 CPU Proxy 初始化时缓存两项 CUDA 能力:S4S8
CU_DEVICE_ATTRIBUTE_GPU_DIRECT_RDMA_FLUSH_WRITES_OPTIONS:是否支持 Host 侧cuFlushGPUDirectRDMAWrites();CU_DEVICE_ATTRIBUTE_GPU_DIRECT_RDMA_WRITES_ORDERING:native ordering 覆盖到什么 consumer scope。
enforce_cst() 的主要选择逻辑是:
- 若 native ordering 已经达到
TO_OWNER,本 GPU 读取 GET result 不需要额外 flush; - 若 native ordering 不足、且 CUDA Host flush API 可用,则调用:
cpp
cuFlushGPUDirectRDMAWrites(
CU_FLUSH_GPU_DIRECT_RDMA_WRITES_TARGET_CURRENT_CTX,
CU_FLUSH_GPU_DIRECT_RDMA_WRITES_TO_OWNER);
- 若 CUDA consistency API 不可用,在 x86 IBRC 中转入 transport fallback:
- GDRCopy 可用、目标 mapping 可读且不是 EGM 时,执行一次 GDRCopy read;
- 否则使用专门的 CST endpoint 对本地 GPU memory 发起 loopback
IBV_WR_RDMA_READ,并等待 CQ completion。S4S5
这说明 enforce_cst() 并不是固定的一种事务,而是根据 GPU native ordering、CUDA flush API 能力、GDRCopy/内存类型以及 IBRC transport 能力 选择最低成本的 consistency 方法。
6.5.2 IBGDA:DUMP/CST
IBGDA 的 ibgda_cst_is_required() 先检查 GPU_DIRECT_RDMA_WRITES_ORDERING;若达到 TO_OWNER,可以满足基本 skip 条件。但当前实现还有两个额外约束:S7
device->common_device.data_direct为真时仍要求 CST;- 初始化多个 NIC/device 时关闭 skip-CST 优化。
不能 skip 时,GET 路径使用:
RDMA READ → result Posted writeback → NIC Fence → DUMP/CST Read → PCIe Cpl → CQ completion → GPU consume
DUMP 让 Local RNIC 对 Local GPU memory 发起 read-like transaction。PCIe Read 是 Non-Posted,有 Completion;NIC Fence 保证这个 CST 不越过前面的 READ/result writeback。于是后续 Read Completion 就成为前序 Posted writeback 的 consistency witness。S7
6.6 总结
| Transport | 操作 / 角色 | 路径 | 底层事务 | Response | 关键同步方法 |
|---|---|---|---|---|---|
| IBRC | PUT / Initiator | GPU A → CPU Proxy A → RNIC A → Fabric → RNIC B → GPU B | RDMA WRITE;Target 侧 PCIe Memory Write / Posted | RDMA RC ACK / requester completion;Target PCIe Write 无 Cpl | fence 保序;quiet 等待发送完成 |
| IBRC | PUT / Target | RNIC B → GPU B:payload → signal | payload:PCIe Memory Write / Posted;native AMO signal:PCIe AtomicOp / Non-Posted | payload Write 无 Cpl;AtomicOp 有 Completion | ordered signal/AMO + signal_wait_until() |
| IBRC | GET / Initiator | GPU A → CPU Proxy A → RNIC A → Fabric → RNIC B → GPU B;result 返回 RNIC A → GPU A | RDMA READ;Local RNIC → GPU A 为 PCIe Memory Write / Posted | RDMA Read Response;本地 result Write 无 Cpl | transport quiet + native GDR ordering / CUDA flush / CST |
| IBRC | GET / Target | RNIC B → GPU B | PCIe Memory Read / Non-Posted | CplD | PCIe Read 返回数据后形成 RDMA Read Response |
| IBGDA | PUT / Initiator | GPU A → RNIC A → Fabric → RNIC B → GPU B | RDMA WRITE;Target 侧 PCIe Memory Write / Posted | RDMA RC ACK / requester CQ completion;Target PCIe Write 无 Cpl | WQE ordering / fence;GPU quiet 等待 completion |
| IBGDA | PUT / Target | RNIC B → GPU B:payload → AMO signal | payload:PCIe Memory Write / Posted;signal:PCIe AtomicOp / Non-Posted | payload Write 无 Cpl;AtomicOp 有 Completion | payload WQE → AMO signal WQE → signal_wait_until() |
| IBGDA | GET / Initiator | GPU A → RNIC A → Fabric → RNIC B → GPU B;result 返回 RNIC A → GPU A | RDMA READ;Local RNIC → GPU A 为 PCIe Memory Write / Posted | RDMA Read Response;本地 result Write 无 Cpl | READ completion + native GDR ordering,或 DUMP/CST |
| IBGDA | GET / Target | RNIC B → GPU B | PCIe Memory Read / Non-Posted | CplD | PCIe Read 返回数据后形成 RDMA Read Response |
| PCIe P2P | PUT / Initiator | GPU A → PCIe Switch → Peer GPU | PCIe Memory Write / Posted | 无 PCIe Cpl | peer write + fence;下一阶段要求完成时使用 quiet |
| PCIe P2P | PUT / Target | Peer GPU memory → signal → consumer | peer PCIe Memory Write;signal 根据具体 op/transport 实现 | 普通 peer Write 无 PCIe Cpl | ordered signal + signal_wait_until() |
| PCIe P2P | GET / Initiator | GPU A → PCIe Switch → Peer GPU → GPU A | PCIe Memory Read / Non-Posted | CplD | blocking GET 等待 response/result 就绪 |
| NVLink P2P | PUT / Initiator、Target | GPU A → NVLink / NVSwitch Fabric → Peer GPU | NVLink Write Data | Write ACK / protocol response,可合并 | system-scope fence / quiet;signal/wait 建立发布-消费同步 |
| NVLink P2P | GET / Initiator | GPU A → NVLink / NVSwitch Fabric → Peer GPU → GPU A | NVLink Read Request / Response Data | Response Data | blocking GET 等待 result;TMA 路径等待 async group |
| CST | GET requester-side consistency | Local RNIC → Local GPU | Read-like transaction / Non-Posted | Cpl / CplD | NIC Fence + CST Read completion 建立 consistency point |
7. 参考资料
-
S1 NVIDIA NVSHMEM --- Memory Ordering
-
S2 NVIDIA NVSHMEM --- Signaling Operations
-
S3 NVIDIA NVSHMEM --- Point-To-Point Synchronization
https://docs.nvidia.com/nvshmem/api/latest/gen/api/sync.html
-
S4 NVIDIA NVSHMEM source ---
src/host/proxy/proxy.cpphttps://github.com/NVIDIA/nvshmem/blob/devel/src/host/proxy/proxy.cpp
-
S5 NVIDIA NVSHMEM source --- IBRC transport
ibrc.cpphttps://github.com/NVIDIA/nvshmem/blob/devel/src/modules/transport/ibrc/ibrc.cpp
-
S6 NVIDIA NVSHMEM source --- IB common transport
https://github.com/NVIDIA/nvshmem/blob/devel/src/modules/transport/common/transport_ib_common.cpp
-
S7 NVIDIA NVSHMEM source --- IBGDA
ibgda_device.cuh/ibgda.cpphttps://github.com/NVIDIA/nvshmem/blob/devel/src/include/non_abi/device/pt-to-pt/ibgda_device.cuh
https://github.com/NVIDIA/nvshmem/blob/devel/src/modules/transport/ibgda/ibgda.cpp
-
S8 NVIDIA CUDA Driver API --- GPUDirect RDMA write ordering / flush
https://docs.nvidia.com/cuda/cuda-driver-api/group__CUDA__DEVICE.html
https://docs.nvidia.com/cuda/cuda-driver-api/group__CUDA__TYPES.html
-
S9 NVIDIA NVSHMEM source ---
examples/ring-bcast.cuhttps://github.com/NVIDIA/nvshmem/blob/devel/examples/ring-bcast.cu
-
S10 InfiniBand Trade Association --- InfiniBand Architecture Specification
Atomic Operations / Transaction Ordering(规范下载入口)
-
S11 AMD PCIe IP --- Receive Transaction Ordering / Atomic Operations
https://docs.amd.com/r/en-US/pg213-pcie4-ultrascale-plus/Receive-Transaction-Ordering
-
S12 NVIDIA CUDA Programming Guide --- Multi-GPU P2P / Memory Consistency
https://docs.nvidia.com/cuda/cuda-programming-guide/03-advanced/multi-gpu-systems.html
-
S13 NVIDIA CUDA C++ Programming Guide --- Memory Fence Functions / System Scope
-
S14 NVIDIA NVSHMEM --- Troubleshooting and FAQs (
nvshmem_ptr, P2P accessibility) -
S15 NVIDIA NVSHMEM 3.7.x Release Notes --- PCIe P2P atomics requirement
https://docs.nvidia.com/nvshmem/release-notes-install-guide/release-notes/release-3720.html
-
S16 NVIDIA NVSHMEM 3.7.0 Release Notes --- TMA-backed NVLink PUT/GET
https://docs.nvidia.com/nvshmem/release-notes-install-guide/release-notes/release-3700.html
-
S17 NVIDIA NVSHMEM source ---
changelog(NVLink system-scoped fence/AMO/signal) -
S18 NVIDIA NVSHMEM source --- P2P
fence/quiet/ TMA drain (nvshmemi_common_device.cuh) -
S19 NVIDIA PTX Interoperability --- Atomics ABI (
thread_scope_system→fence.sc.sys)https://docs.nvidia.com/cuda/ptx-writers-guide-to-interoperability/atomic-abi.html
-
S20 NVIDIA CUPTI --- NVLink packet/response traffic
-
S21 NVIDIA Developer Forums --- NVLINK GPU Metrics Breakdown(NVIDIA 工程师对 Write ACK / Read Response traffic 的说明)
https://forums.developer.nvidia.com/t/nvlink-gpu-metrics-breakdown/323733/6