对象:arXiv:2510.25213《Platform Architecture for Tight Coupling of
High-Performance Computing with Quantum Processors》第 2.4 节
"Network proof of concept"。
本文先回答"这个实验在物理上和工程上证明了什么、为什么这样设计",
再把每条原理映射到本项目(
nvqlink_echo_poc/)的具体实现。
1. 原理与实操
1.1. 实验要证明的命题
这个 PoC 不是一般意义上的网络性能测试,它要论证的是一个体系结构命题:
商用 GPU 服务器可以通过商用以太网,作为量子控制系统(QSC)的在线
实时协处理器,承担 QEC 解码这类"微秒级反应时间"的关键路径任务。
论文 1.2 节给出了两个硬指标的来源:解码器吞吐 必须跟上 syndrome 数据流
(否则积压指数增长、QPU 停摆),反应时间 (最后一次 syndrome 采集到
前馈动作生效的间隔)直接以"每周期逻辑错误率 × 空闲周期数"的形式侵蚀
量子程序的正确性。因此实验的因变量不是带宽,而是小包端到端往返时延
及其抖动------论文测得 mean=median=3.839 µs、σ=35 ns、max=3.96 µs。
这里有一个容易被忽略的细节:σ=35 ns 与 mean 同等重要 。2.2 节把时间域
划分为 DTD(确定性时间,FPGA 时钟域)与 RTD(实时时间,HPC 域),RTD
能够提供"时延契约"的前提是时延分布有紧的尾部------均值低但抖动大的链路
无法支撑实时调度。所以这个实验本质上测的是确定性,而非速度。
1.2. 时延预算的物理分解
3.96 µs 的往返由哪些物理过程构成?沿数据路径逐段拆解(单程约为一半):
FPGA 组包 → MAC/PCS → 光模块/电缆 → CX-6 Dx 硬件解 RoCE
→ DMA 直写 GPU 显存 → GPU kernel 检测 → 构造 WQE+写门铃
→ NIC 组包 → 电缆 → FPGA MAC → payload 落 FPGA
段 1:链路层(ns 级) 。包生成、SerDes、光电转换、光纤传播,每段都是
几十到几百 ns,且抖动极小(硬件流水线)。这正是论文 2.3 节选择
"NIC + 商用以太网"而非"QSC 做 PCIe 插卡"的关键论据:跨收发器和链路的
额外时延在纳秒量级,而换来的是几乎无限的扩展性(交换机扇出、链路提速
升级路径)。PCIe 插卡方案则受限于槽位数和驱动维护成本。
段 2:NIC 卸载(百 ns 级) 。RoCE 让 NIC 硬件完成传输层全部工作------
包校验、QP 状态机、地址翻译,然后 DMA 直写 GPU 显存。CPU 和主机内存
完全不参与 :没有内核协议栈、没有中断、没有内存拷贝。这是把时延从
传统 socket 路径的几十 µs 压到 µs 级的第一性原理:每消除一个软件层次,
就消除一次上下文切换、一次 cache 污染、一段不可预测的调度延迟。
段 3:PCIe 跳数(决定性变量) 。NIC→GPU 显存的 DMA 写要过 PCIe。
若 GPU 与 NIC 在同一 PCIe switch 下(PIX/PXB 拓扑),是单跳 ;
若跨 CPU socket(SYS 拓扑),要穿 UPI/Infinity Fabric,时延增加数百 ns
且抖动显著增大------因为共享总线上有其他流量竞争。这就是 Phase 0 用
nvidia-smi topo -m 做拓扑检查的原理依据,也是论文特意强调
"dedicated PCIe switch between the NIC and GPU slots"的原因。
段 4:GPU 侧检测机制(本复现的核心设计决策) 。GPU 如何知道"新包到了"?
候选方案的原理对比:
| 机制 | 原理 | 时延/抖动特性 |
|---|---|---|
| CPU 中断 → 通知 GPU | 中断→内核→驱动→用户态→CUDA 事件 | µs~十 µs 级,抖动大,且 CPU 回到关键路径,违背实验目标 |
| GPU 轮询 CQE | GPU 读 NIC 的完成队列(PCIe 读往返) | 每次轮询是一次 PCIe read round-trip(~600 ns--1 µs),且开源 GPUNetIO 不支持双边接收 |
| GPU 轮询显存标志(本方案) | NIC 的 DMA 写把 payload 推进 GPU 显存;GPU 读自己的显存 | 显存读 ~百 ns,无 PCIe 往返;写方向利用 PCIe posted write 的流水特性 |
关键原理是 PCIe 读写方向的不对称性 :NIC→GPU 是 posted write(发射即
完成,流水推进),而 GPU 主动读 NIC 寄存器/CQE 是 non-posted read,必须等
一个完整 PCIe 往返。因此"让数据自己流到 GPU 显存、GPU 轮询本地显存"
是时延最优的检测模式。它与 NVSHMEM 的 put+signal 语义同构------NVSHMEM
的 signal 本质就是"数据写 + 标志写",接收方轮询标志。这也解释了为什么
开源 GPUNetIO 只提供单边 RDMA 数据路径依然足够:单边 write + 显存
标志轮询在原理上就覆盖了到达通知语义 ,代价是把"payload 先于标志可见"
的写序约束下放给发送方(FPGA 单拍交付 + 接收 MR 关 RELAXED_ORDERING)。
段 5:GPU 直发(GDAKI 原理) 。回程不再经 CPU:CUDA kernel 直接在 GPU
显存中按 mlx5 格式构造 WQE,然后 GPU SM 写 NIC 的门铃寄存器(一次 PCIe
posted write 到 NIC BAR 映射页)。这就是 GPUDirect Async Kernel-Initiated
的本质------把网卡的控制结构(SQ/CQ/门铃)变成 GPU 地址空间里的普通
内存对象 ,从而把 CPU 从通信控制面也彻底移除。开源 GPUNetIO 的 CPU
控制路径所做的全部工作(cudaMalloc 队列内存 → mlx5dv_devx_umem_reg
注册显存 → DEVX 建 QP 指定 umem → UAR 映射给 GPU),都是为这一刻做
准备:初始化在 CPU,运行时零 CPU。
1.3. 抖动来源与 warm-up 的原理
论文 Fig.4 观察到的 warm-up 段(头部若干包时延偏高)不是缺陷,而是
冷启动状态填充的必然结果,来源有三:
- NIC 队列元素首次使用 :QP/CQ/WQE 缓存行第一次被访问时的内部
状态建立; - GPU 侧缓存与 TLB :显存页第一次被 NIC DMA 触及时的地址翻译
(ATS/PTE)与缓存分配; - 流水线充填:发送路径上各级 FIFO 的稳态建立。
这些一次性开销完成后,系统进入稳态,σ 收缩到 35 ns。工程含义有两条:
一是实验设计必须显式区分 warm-up 段与稳态段 (本项目分析脚本用滚动
中位数自动切分);二是生产系统可用 dummy 包预热消除暖机------论文明确
指出这一点,我们的 Phase 4 第二轮实验即验证此结论。
稳态抖动(σ=35 ns)的剩余来源主要是:PCIe switch 仲裁的排队抖动、
GPU SM 调度量子、NIC 内部流水线的周期对齐。35 ns 对应 100G MAC 上约
几百字节的传输时间尺度------已逼近硬件流水线的本底,说明软件路径上
已无可观噪声。
1.4. 为什么选"不可靠"连接
直觉上 RC(可靠连接)更保险,论文却刻意选不可靠语义,原理是:
重传机制在低时延场景里是抖动放大器 。一旦发生丢包,超时重传的时延
(µs~ms 级)会打乱后续所有包的时序,且软件无法控制。而在误码率
<10⁻¹⁵ 的工程化链路上发 32B 小包,丢包概率可忽略;真要检测,包内
自带 seq 做连续性检查即可(论文在笔记本端做此检查,我们的 ILA 分析
脚本同样实现)。这是用极低概率的硬失败 交换无尾时延的确定性 ------
实时系统的标准取舍。
本复现的折中:开源 GPUNetIO 的 RC 连接 + 关闭 PFC 的 lossy 配置。
RC 的重传语义仍存在于 NIC 硬件,但在无丢包稳态下不触发,时延行为
与 UD 等价;同时 RC 是开源版唯一支持的连接方式。这是对原论文
"unreliable" 原则在开源工具链约束下的保真近似------保留其抖动收益的
实质(无 PFC、无软件重传、丢包交由 seq 检测),接受连接类型的形式差异。
1.5. 测量学的原理
实验测量设计本身也值得复述,因为它决定了数据的可信度:
单时钟域测往返 。RTT = t_recv − t_send,两个时间戳都来自 FPGA 内
同一个 96-bit PTP 定时器。同一时钟域内做差,时钟同步误差、频率偏差
全部被消去 ------这是比在两端各放一个时钟再做对表严格得多的测量学设计。
代价是 GPU 侧无法直接分解单程时延;我们用 GPU 内 %globaltimer 旁路
日志测"GPU 驻留时间"(t_tx − t_rx)做相对分解 (GPU 驻留占总 RTT
的比例、驻留时间本身的抖动),而不做绝对单程分解------两个时钟不同源,
绝对相减没有意义。文档和代码中都明确标注了这一边界。
观测手段不扰动被测对象 。FPGA 侧用 ILA 经 JTAG 把 {seq, t_send,
t_recv} 送到独立笔记本------观测通道与被测的 100G 数据通道物理隔离。
GPU 侧同理:旁路日志写 GPU 显存、事后批量取回,数据路径上不插任何
printf/拷贝。这是微秒级测量的基本操守:任何在线观测手段本身的时延
都可能超过被测量。
统计口径 。mean/median 报告中心趋势,σ 报告确定性,sample max
报告尾时延 ------实时系统的验收看尾部不看均值。论文给 max=3.96 µs 而非
p99.9,是因为样本内最坏值直接对应"QEC 反应时间的最坏承诺"。
1.6. 原理到实操的映射总表
| 原理 | 论文做法 | 本复现实现 |
|---|---|---|
| CPU 出局(数据面) | RDMA + GPUNetIO 持久 kernel | nvqlink_echo_kernel.cu:轮询显存 seq + device 端 WQE/门铃 |
| CPU 出局(控制面移交) | CPU 只做枚举/初始化/启动 kernel | nvqlink_echo_sample.cpp:建 QP、注册显存、启动后退出数据路径 |
| PCIe 单跳 | NIC/GPU 共 PCIe switch | Phase 0:nvidia-smi topo -m 验 PIX/PXB |
| 显存标志轮询 | (论文用 CQE 语义) | 开源版无双边数据路径 → 用单边 write + seq 轮询,原理等价 |
| 写序保证 | 隐含(HSB 单包原子写) | FPGA 单拍交付 32B + MR 关 RELAXED_ORDERING |
| 不可靠连接 | 关重传、seq 检测丢包 | RC + 关 PFC + seq 连续性检查(实质等价) |
| 单一时钟域 | FPGA PTP 96-bit | 沿用;ILA 导出 CSV 为 ground truth |
| 旁路观测 | ILA/JTAG 独立通道 | ILA + GPU globaltimer 日志事后取回 |
| warm-up 研究 | Fig.4/Fig.5 对照 | 分析脚本自动切分 + dummy 包预热第二轮 |
| 验收 | 3.839 µs / 35 ns / 3.96 µs | CX-6 Dx 上 mean<5 µs、σ<100 ns 为达标 |
1.7. 复现的边际与诚实声明
- CX-7→CX-6 Dx、RTX PRO 6000→实际 GPU 的差异会带来几百 ns 量级的
系统性偏移,达标线因此放宽;这不影响实验命题的验证(命题是"µs 级
确定性闭环可行",不是"复刻 3.96"这个具体数字)。 - 开源 GPUNetIO 的能力边界 (无双边数据路径、无 Ethernet/DMA GPU 路径)
迫使接收检测改为显存轮询。这在原理上反而更干净(少一次 CQE 交互),
但与论文原文的实现路径不同------属于"语义等价的异构实现",引用时应注明。 - NVSHMEM 不适用 的根本原因是 PGAS 模型要求两端都运行其 runtime,
FPGA 无法满足;其 IBGDA 传输层与本方案的 GPU 直发机制同源,可作
原理参照但不可作实现路径。
2. FPGA / HSB 侧配合说明
主机侧程序(nvqlink_echo)复现论文 2.4 节的 RTH 回环角色;FPGA 侧对应论文中
RFSoC + HSB IP 的 PPU 角色。
2.0. 源码核实结论(对 holoscan-sensor-bridge 仓库的核查)
HSB IP 中有现成的测试数据发生器,但没有现成的论文 payload 生成配置:
- 现成可用 :
fpga/nv_hsb_ip/data_gen/data_gen.sv------编译期定义
SIF_RX_DATA_GEN后,每个 Sensor RX 端口挂一个 data_gen 模块,APB 寄存器
运行时可控:data_gen_ena(启停)、mode(PRBS / counter 计数)、
data_gen_size(帧长)、output_rate(速率分频)。host 侧直接
hololink.write_uint32(...)即可配置(见 docs/user_guide/hardware_debug.mdx)。
这正是论文所述"control signals to set the time between packets, as well as
start or stop the data stream"。 - 不存在 :data_gen 的 payload 只能是 PRBS 随机序列或 16-bit 递增计数序列;
packetizer / roce / sensor_tx 路径中没有任何时间戳插入逻辑。没有任何现成
配置能生成论文布局{96b PTP 时间戳, 16b seq, 18B 零}的 payload。 - 可利用 :PTP 定时器在顶层对外输出
o_ptp_sec[47:0]+o_ptp_nanosec[31:0]
(top/HOLOLINK_top.sv),可直接接到你自己的用户逻辑。
因此 FPGA 侧需要一小段自定义 RTL(约几十行 SystemVerilog):实现自己的
AXIS 源,接到 Sensor RX 接口(数据_gen mux 之前的传感器输入位置),按
论文布局组帧。
2.1. payload 生成逻辑(替代传感器数据通路)
每个 RoCE 包携带恰好 32 字节 payload(对齐论文):
| 字节偏移 | 内容 | 说明 |
|---|---|---|
[0:12) |
PTP 时间戳,96 bit | 发送时刻,ns 级 |
[12:14) |
包序号 seq,16 bit | 网络字节序(大端),逐包递增 |
[14:32) |
0 填充,18 B |
关键约束:seq 必须最后落地
主机侧 kernel 以显存中的 seq 字段作为"新包到达"标志轮询。因此必须保证
在一个 RDMA Write 消息内,payload 的其余字节先于 seq 两字节的 PCIe 写完成。
实现方式(推荐):自定义 AXIS 源在单拍 内交付整个 32B payload
(512-bit 位宽下 tkeep=0x00000000FFFFFFFF,一拍一帧),32B 小于 PCIe MPS,
将以单个 TLP 落盘,原子可见,天然满足写序;主机端 MR 不开
RELAXED_ORDERING 即可。
实施路径(两步走)
- 链路冒烟测试(零 RTL 改动) :先用现成 data_gen 的 counter 模式 +
data_gen_size=32+output_rate低速率发流,配合 ILA 验证
FPGA→HSB→CX6Dx→GPU 显存的完整链路可达(主机侧 kernel 会把 seq 字段
当作计数序列的一部分------此时只为验证链路,不做时延测量); - 论文 payload(小改 RTL) :写一个 AXIS 源模块,读取顶层的
o_ptp_sec/o_ptp_nanosec拼成 96-bit 时间戳(论文 96 bit ≈ 48b 秒 +
32b 纳秒 + 16b 分数/对齐填充),加上 16-bit 自增 seq 和 18B 零,
接到 Sensor RX 接口输入;发送节奏可由 host APB 寄存器或本地寄存器控制。
2.2. QP / 连接参数(RC + RoCE v2)
HSB 默认面向 Hostmem/DataChannel 语义,本复现需要一个 RC QP、RoCE v2 连接,
与主机侧 connect_verbs_qp 的状态机对齐:
- QP 类型:Reliable Connection (RC)
- RoCE v2,MTU 任意(32B 小包,256B MTU 即可)
- PSN:两端都从 0 开始(主机侧
doca_verbs_qp_attr_set_sq_psn/rq_psn(..., 0)) - 发送方向:纯 RDMA Write,不带 immediate(主机侧不做双边接收,seq 即信号)
- FPGA 侧需要为主机回写准备一个接收目标:一块可被远端写的地址区间
{fpga_raddr, fpga_rkey},回写到达后与 ILA 联动记录到达时刻
参数交换流程(无 socket OOB,人工/脚本两步)
- 先运行主机侧程序,它会打印本端参数:
local_qpn / local_gid / rx_buf_addr / rx_rkey; - 把以上四个值通过 HSB 配置通道(寄存器写或你们的配置软件)设给 FPGA 侧 QP;
- 从 HSB/FPGA 侧读出
fpga_qpn / fpga_gid / fpga_raddr / fpga_rkey,
作为命令行参数传给主机侧程序完成 RC 连接。
注意顺序:主机侧程序在 RC 连接建立后立即 启动 kernel 等待收包,
所以应先完成主机侧参数打印 → FPGA 配置 → 再以完整参数启动主机侧程序
(或先启动主机侧程序、后配置 FPGA,kernel 会一直自旋等待首包)。
2.3. 发包节奏与 ILA 测量(对齐论文)
- 发包间隔用 HSB 控制寄存器配置,建议两组:
(a) 间隔 ≥ 20 µs 的稀疏流------隔离单包 RTT,对标论文 Fig.4/5;
(b) 1 MHz 周期------模拟 QEC cycle 速率,观察背靠背压力下的尾时延; - ILA 抓取:每包记录
{seq, t_send(出 MAC 前), t_recv(回写 payload 落 FPGA 后)},
导出 CSV:seq,t_send_ns,t_recv_ns; - 丢包检测:检查 seq 连续性(论文同样在笔记本端做连续性检查);
- warm-up 段不要 在 FPGA 侧规避------先发一轮原始数据复现论文 Fig.4 的
暖机高时延,随后数据即为 Fig.5 稳态。
2.4. 与论文的对应关系
| 论文组件 | 本复现 |
|---|---|
| HSB IP 产生带 PTP 时间戳的 RoCE 包 | 同上(你已有,改 payload 生成逻辑) |
| CPU 枚举 FPGA、初始化连接、下配置 | nvqlink_echo 启动阶段 + 打印参数 |
| GPU 持久 kernel 收包回环 | nvqlink_echo_kernel.cu(轮询 seq + RDMA Write 回写) |
| ILA + 笔记本离线统计 | ILA CSV + tools/analyze_latency.py |
| 验收:mean=median≈3.84 µs, σ≈35 ns, max<3.96 µs | CX-6 Dx 上预期略高,mean<5 µs、σ<100 ns 为达标 |
3. NVQLink 论文 2.4 节实验复现(基于开源 GPUNetIO)
复现目标:arXiv:2510.25213《Platform Architecture for Tight Coupling of HPC with
Quantum Processors》2.4 节的网络概念验证------FPGA(HSB IP)经 RoCE 发 32B 时间戳包,
主机侧 GPU 持久 kernel 回环,FPGA 测端到端 RTT。论文结果:mean=median=3.839 µs,
σ=35 ns,max=3.96 µs。
技术路线:开源 DOCA GPUNetIO (github.com/NVIDIA-DOCA/gpunetio,BSD-3-Clause)。
开源版 GPU 数据路径只支持 RDMA 单边语义,因此回环设计为
「轮询显存中的 seq 标志 + RDMA Write 回写」,不依赖双边接收/CQE 到达通知。
NVSHMEM 不适用:它要求通信两端都跑 NVSHMEM runtime,FPGA 无法满足。
Phase 0 --- 环境与拓扑验证(半天)
bash
# 0.1 拓扑:GPU 与 CX-6 Dx 必须在同一 PCIe switch 下(PIX/PXB)
nvidia-smi topo -m
# 若为 SYS(跨 NUMA):换插槽,否则时延显著劣化
# 0.2 GPUDirect RDMA:CX-6 Dx 不支持 dmabuf,必须走 nvidia-peermem
lsmod | grep nvidia_peermem || sudo modprobe nvidia_peermem
# 0.3 驱动与 CUDA
ofed_info -s # MLNX_OFED
nvcc --version # CUDA >= 12.2
# 0.4 网口:直连 FPGA、静态 IP、RoCE lossy(关 PFC,对齐论文"不可靠连接")
sudo mlxfwmanager --query # 确认 CX-6 Dx 固件为最新 GA
sudo ethtool -i <ifname>
# 关闭 PFC:mlxlink / mstconfig 或交换机侧配置,点到点直连下确保无 PFC 帧
# 0.5 CPU 隔离(初始化线程不干扰测量;数据路径本就无 CPU)
# grub: isolcpus=<n-1> nohz_full=<n-1>,运行程序时 taskset 到隔离核
验收 :nvidia-smi topo -m 显示 GPU↔CX6Dx 为 PIX/PXB;ibv_devices 可见 mlx5_X。
Phase 1 --- 构建(半小时)
bash
# 1.1 开源 GPUNetIO
git clone https://github.com/NVIDIA-DOCA/gpunetio.git
cd gpunetio && make -j && cd ..
# 1.2 本项目(nvqlink_echo_poc)
cd nvqlink_echo_poc
make GPUNETIO_DIR=../gpunetio CUDA_ARCH=90 # CUDA_ARCH 按实际 GPU:80=A100, 90=H100, 100=B200
# 1.3 自检:用上游 write_lat 双机 ping-pong 验证环境正确(可选但强烈建议)
cd ../gpunetio/examples/gpunetio_verbs_write_lat
# 两台主机(或单机两个端口)各跑 server/client,确认 GDAKI 链路本身工作
验收 :./nvqlink_echo 打印用法;write_lat 自测能出往返时延数字。
Phase 2 --- FPGA 侧配置(按 fpga/FPGA侧配合说明.md)
- 链路冒烟:先用 HSB 内置
data_gen(SIF_RX_DATA_GEN编译宏 + APB 寄存器,
counter 模式、size=32)验证 FPGA→GPU 显存链路,零 RTL 改动 ;
随后按fpga/FPGA侧配合说明.md写自定义 AXIS 源生成论文 payload
{96b PTP 时间戳, 16b seq(大端), 18B 零},单拍交付保证 seq 最后落地; - FPGA 侧 QP:RC + RoCE v2,PSN=0,纯 RDMA Write(不带 immediate);
- 准备 FPGA 侧回写接收区
{fpga_raddr, fpga_rkey},与 ILA 联动记录到达时刻; - 发包间隔先设 ≥20 µs(稀疏流对标 Fig.4/5)。
Phase 3 --- 运行(两步握手)
bash
# 3.1 第一步:以占位参数启动一次,取得本端打印参数(或直接读代码路径);
# 更简单的做法:先注释 connect 前的 printf 不可行------直接看程序输出:
# 程序在 connect_verbs_qp 之前打印 local_qpn/local_gid/rx_buf_addr/rx_rkey,
# 因此需要先给一组"占位"FPGA 参数让程序跑到打印点:
./nvqlink_echo -d mlx5_0 -g 8a:00.0 -l 3 -n 100000 \
--fpga-qpn 0 --fpga-gid :: --fpga-raddr 0 --fpga-rkey 0
# (会在打印本端参数后于 QP 连接处报错退出------属预期,抄下四个本端参数)
# 3.2 手工交换 QP 参数(RC 是点对点静态连接,两端必须互知对端身份):
#
# (a) 把 3.1 打印的主机侧 4 个参数配置到 FPGA/HSB,供 FPGA 发包使用:
# local_qpn → FPGA 侧 QP 的 dest_qp_num(不匹配则 NIC 丢包)
# local_gid → RoCE 包的目的 IP(由主机网口 IP 派生)
# rx_buf_addr → FPGA RDMA Write 的目标地址(GPU 显存 32B 接收槽)
# rx_rkey → 写包 RETH 中的 rkey(NIC 校验写权限)
#
# (b) 从 FPGA 侧取得 4 个参数,3.3 作为命令行参数传给主机,供回程 write:
# fpga_qpn → FPGA 侧 QP 编号(你建 QP 时分配的值)
# fpga_gid → 枚举时给 FPGA 配的 IP 派生的 GID
# fpga_raddr → HSB 接收路径(host→FPGA 方向)配置的写终结地址
# fpga_rkey → HSB roce/rx 路径校验回程写所用的 key
# 3.3 第二步:正式运行(GPU_SM_DB 模式,GPU 直写门铃)
DOCA_GPUNETIO_LOG=6 ./nvqlink_echo -d mlx5_0 -g 8a:00.0 -l 3 -p 2 -n 100000 \
--fpga-qpn 0x<fpga_qpn> --fpga-gid <fpga_gid> \
--fpga-raddr 0x<fpga_raddr> --fpga-rkey 0x<fpga_rkey> \
-o gpu_log.csv
# 3.4 kernel 开始自旋后,在 FPGA 侧启动数据流(先不做 dummy 预热 → 复现 Fig.4)
# 3.5 跑满 100000 包后 kernel 自动退出,GPU 日志写入 gpu_log.csv
验收:程序正常结束;gpu_log.csv 有 100000 行;ILA 导出 ila.csv。
Phase 4 --- 测量与分析(对齐论文口径)
bash
python3 tools/analyze_latency.py --ila ila.csv --gpu gpu_log.csv
- 输出
fig_full.png(全程,含 warm-up ------ 对应论文 Fig.4)与
fig_steady.png(稳态 ------ 对应论文 Fig.5); - 统计口径:mean / median / std / max;
- warm-up 实验 :第一轮不预热(复现 NIC 队列首次填充、缓存分配造成的高时延段);
第二轮在 FPGA 侧先发 N≥1000 个 dummy 包再进入正式测量,验证暖机段消失; - 加压实验:发包间隔改为 1 µs(1 MHz,模拟 QEC cycle rate),观察尾时延。
达标判据 (论文为 CX-7 + RTX PRO 6000;本复现为 CX-6 Dx,预期略高):
稳态 mean < 5 µs、std < 100 ns、max < 6 µs;GPU 驻留(t_tx−t_rx)应稳定在 1~2 µs 内。
代码结构
javascript
nvqlink_echo_poc/
├── Makefile # 构建(依赖上游 gpunetio 仓库)
├── README.md # 本文档
├── src/
│ ├── nvqlink_echo.h # payload 布局 / 日志结构 / FPGA 对端信息
│ ├── nvqlink_echo_kernel.cu # 回环持久 kernel(数据路径,GPU-only)
│ ├── nvqlink_echo_sample.cpp # CPU 控制路径(建 QP、注册显存、启动 kernel)
│ └── nvqlink_echo_main.cpp # 参数解析
├── fpga/
│ └── FPGA侧配合说明.md # HSB payload 布局、写序约束、QP 参数、ILA 配置
└── tools/
└── analyze_latency.py # 时延统计与 Fig.4/Fig.5 复现绘图
数据路径要点(kernel 内,每包):
volatile轮询rx_buf+12的 seq ------ FPGA RDMA Write 落地即新包到达;doca_gpu_dev_verbs_fence_acquire<SYS>()------ 保证 payload 可见性;doca_gpu_dev_verbs_wqe_prepare_write()构造回写 WQE(源=rx_buf 原样回写);doca_gpu_dev_verbs_submit_bf/submit()------ GPU SM 直写 NIC 门铃;doca_gpu_dev_verbs_poll_cq_collapsed_at()------ 收割 CQE 并记 TX 时间戳。
已知边界与风险
| 项 | 说明 |
|---|---|
| 双边接收不可用 | 开源版无 GPU 数据路径 send/recv;本设计用 seq 轮询规避,HSB 侧须不发 immediate |
| 写序保证 | RX MR 不能开 RELAXED_ORDERING;FPGA 须单拍交付 payload |
| 两钟不同源 | GPU globaltimer 与 FPGA PTP 不同源,RTT 以 ILA 为准,GPU 日志只看驻留分解 |
| CX-6 Dx | GDAKI 最低支持档;无 dmabuf(peermem)、无 GPU_SM_BF 时回退 -p 2 (SM_DB) |
| 包序号回绕 | 16-bit seq 每 65536 包回绕,分析脚本按排序处理,连续性检查不受影响 |
| warm-up | 首轮必须保留,属论文明确观察到的现象,不是 bug |