NVSHMEM Memory Ordering和Synchronization分析

本文分析 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。

基本过程为:

  1. GPU A 把 GET 请求交给 CPU Proxy A;
  2. Proxy A 提交 RDMA READ;
  3. RNIC B 通过 PCIe Read 从 GPU B 取数,PCIe Read 有 CplD
  4. RDMA Read Response 返回 RNIC A;
  5. RNIC A 通过 PCIe Memory Write 把 result 写入 GPU A;该 Write 是 Posted,没有 Cpl
  6. 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 Ordering6.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 Ordering6.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 Fence6.2 QuietS18
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.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 实现:

  1. GPU A 对 GPU B peer address 发起 Read Request;
  2. Read Request 经 NVLink/NVSwitch 到达 GPU B;
  3. GPU B 返回 Response Data;
  4. Response Data 回到 GPU A,并形成/写入本地 GET result;
  5. 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 所要求的 completionS1

典型用途是:

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:

  1. GPU 线程已经发出对 peer-mapped address 的 store;
  2. quiet 执行 __threadfence_system()
  3. 调用线程在 system-scope fence 退休之前不能继续越过该完成点;
  4. thread/warp/block 版本再执行对应的 threadgroup sync;
  5. quiet 返回后,NVSHMEM 将此前 P2P stores 视为已经达到 API 所要求的 completion/visibility point。S18

NVSHMEM 源码对这一点的注释非常直接:__threadfence_system is required for P2P store visibilityS18

对于 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 fencequiet 在当前 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 P2PSIGNAL_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 GETmay_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

  1. CU_DEVICE_ATTRIBUTE_GPU_DIRECT_RDMA_FLUSH_WRITES_OPTIONS:是否支持 Host 侧 cuFlushGPUDirectRDMAWrites()
  2. CU_DEVICE_ATTRIBUTE_GPU_DIRECT_RDMA_WRITES_ORDERING:native ordering 覆盖到什么 consumer scope。

enforce_cst() 的主要选择逻辑是:

  1. 若 native ordering 已经达到 TO_OWNER,本 GPU 读取 GET result 不需要额外 flush;
  2. 若 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);
  1. 若 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. 参考资料

相关推荐
Java牛马3 天前
Caffeine 缓存及其应用相关总结
java·redis·缓存·caffeine·数据一致性·本地缓存
浦信仿真大讲堂23 天前
从重复操作到自动化闭环:如何让 CST 与 Python 真正协同起来
python·自动化·cst·仿真软件·达索软件
JZC_xiaozhong1 个月前
销售订单如何自动同步多个业务系统?3种方案对比分析
大数据·数据分析·数据一致性·应用集成·数据孤岛解决方案·数据集成与应用集成
思茂信息1 个月前
CST 辐射边界反射过高优化方法
网络·emc·emi·cst·仿真软件·电磁仿真
思茂信息1 个月前
CST 功能介绍:揭秘平面波端口 (PWP) —— 助力复杂线缆辐射抗扰度仿真
emc·emi·cst·电磁仿真·仿真辐射
思茂信息2 个月前
CST如何在EDA仿真中自动添加Port
emc·emi·cst·仿真软件·电磁仿真
思茂信息2 个月前
CST软件基于液态金属开关的方向图可重构天线
服务器·算法·重构·cst·仿真软件·电磁仿真
一只豌豆象3 个月前
第3讲:单端传输线的时域TDR仿真(基于实战的第一次仿真视角切换)
学习·信号完整性·cst·仿真实战
思茂信息3 个月前
CST对一种用于中型无人机 433MHz 通信的宽带共形贴片天线
开发语言·单片机·嵌入式硬件·平面·无人机·cst