GPU Direct DMA 在Jetson NX 与 X86平台的测试
测试了jetson NX 16GB 与 H100的性能,后续会继续补充其它平台的测试
1. 技术原理
1.1 什么是 GPU Direct RDMA
GPU Direct RDMA(Remote Direct Memory Access)是 NVIDIA 提供的一种高性能数据传输技术,允许第三方 PCIe 设备(如 FPGA、网卡、视频采集卡)绕过 CPU 和系统内存,通过 PCIe 总线直接与 GPU 显存进行数据交换。
传统数据路径(FPGA → GPU):
FPGA ──DMA──→ CPU内存 ──cudaMemcpy──→ GPU显存
↑ ↑
PCIe传输 内存总线拷贝
(第1次) (第2次)
GPU Direct RDMA 路径(FPGA → GPU):
FPGA ──PCIe DMA──→ GPU显存
↑
仅一次PCIe传输
零CPU拷贝
1.2 核心优势
| 优势 | 说明 |
|---|---|
| 延迟降低 | 消除 CPU 中间拷贝,端到端延迟减少约 50% |
| 带宽提升 | 避免内存总线争用,有效带宽提升 1.5x~2.5x |
| CPU卸载 | DMA 传输期间 CPU 完全空闲,可处理其他任务 |
| 零拷贝 | 数据直达 GPU,无需系统内存中转缓冲 |
| 确定性延迟 | 无 CPU 调度干扰,适合实时系统 |
1.3 适配设备与平台
| 平台 | 支持状态 | GPU 类型 | 备注 |
|---|---|---|---|
| NVIDIA Jetson Orin (Tegra) | ✅ 已验证 | 集成 GPU (统一内存) | 通过 nvidia-p2p 内核接口 |
| NVIDIA Jetson Xavier | ✅ 可适配 | 集成 GPU | 同 Orin 架构 |
| x86 + NVIDIA 独立显卡 | ✅ 已验证 | H100 PCIe | 通过 nvidia-peermem + cudaMalloc |
| FPGA(Xilinx Kintex/Artix) | ✅ 已验证 | N/A | 通过 XDMA IP 核 + 自定义驱动 |
| 其他 PCIe DMA 设备 | ✅ 可扩展 | - | 需实现 Pin/Unpin/Transfer ioctl |
1.4 工作流程
1. GPU 缓冲区分配 (cudaHostAlloc / cudaMalloc)
2. Pin 操作: 将 GPU 虚拟地址映射为物理页面,锁定在内存中
3. DMA 传输: FPGA 直接读写 GPU 物理页面(通过 PCIe BAR)
4. Unpin 操作: 释放页面锁定
关键点:Pin 操作仅需执行一次,后续可无限次复用同一 Handle 进行 DMA 传输,避免了传统方式中每次传输都需要的地址转换开销。
2. 测试环境
2.1 硬件平台
Tegra 平台 (Jetson Orin)
| 组件 | 规格 |
|---|---|
| 嵌入式平台 | NVIDIA Jetson Orin (aarch64) |
| GPU | Orin 集成 Ampere GPU, 统一内存架构 |
| FPGA | Xilinx 系列, PCIe Gen3 x4 |
| FPGA 端内存 | DDR4 2GB |
| PCIe 链路 | Gen3 x4 (理论峰值 ~2GB/s) |
| 系统内存 | LPDDR5 (统一内存) |
Desktop 平台 (x86 + H100)
| 组件 | 规格 |
|---|---|
| CPU | Intel Xeon Platinum 8462Y+ |
| GPU | NVIDIA H100 PCIe (80GB HBM2e), 独立显存架构 |
| FPGA | Xilinx 系列 (VID:0x0B03, PID:0x1004), PCIe Gen3 x8 |
| FPGA 端内存 | DDR4 2GB |
| PCIe 链路 | Gen3 x8 (理论峰值 ~7.88 GB/s) |
| 系统内存 | DDR5 (独立于 GPU 显存) |
2.2 软件环境
Tegra 平台
| 组件 | 版本 |
|---|---|
| OS | Ubuntu 22.04 (aarch64) |
| CUDA | 12.6 |
| 内核驱动 | HelloFPGA XDMA 自定义驱动 (v2020.2.2) + GPU Direct 扩展 |
| 用户空间库 | libHelloFPGACore.so (含 GPU Direct 兼容层) |
| 编译器 | nvcc (CUDA 12.6) + GCC |
Desktop 平台
| 组件 | 版本 |
|---|---|
| OS | Ubuntu 22.04 (x86_64) |
| 内核 | 6.8.0-124-generic |
| CUDA | 12.4 (V12.4.99) |
| GPU 驱动 | NVIDIA 590.48.01 |
| 内核驱动 | HelloFPGA XDMA 自定义驱动 + nvidia-peermem 扩展 |
| 用户空间库 | libHelloFPGACore.so (x64, 含 GPU Direct 兼容层) |
| 编译器 | nvcc (CUDA 12.4) + GCC |
2.3 驱动架构
用户空间: HelloFPGACore.so (TransferMode API)
│
├─ CPU模式: open(/dev/HelloFPGA0_c2h_*) → read/write
│
└─ GPU Direct模式: open → ioctl(XDMA_IOC_GPU_PIN/XFER/UNPIN)
│
内核空间: HelloFPGA.ko (XDMA + xdma_gpu_direct + xdma_gpu_tegra)
│
硬件: FPGA XDMA IP ←──PCIe──→ GPU BAR (物理地址直通)
3. 测试方法
3.1 测试工具
测试程序: gpu_direct_api_test.cu
支持两种运行模式:
- 快速功能测试: 验证 API 正确性 + 性能对比(约 2 分钟)
- 长稳态压力测试 : 12 小时持续运行,每 10 分钟记录数据(
--long参数)
3.2 测试对比方案
| 路径编号 | 方案名称 | 数据流向 | 说明 |
|---|---|---|---|
| A | CPU DMA 仅 | FPGA → CPU 内存 | 传统 DMA,数据停留在 CPU 侧 |
| B | FPGA→CPU→GPU 全路径 | FPGA → CPU → GPU | 传统方式将数据送达 GPU 的完整路径 |
| C | GPU Direct Handle | FPGA → GPU 直达 | 预 Pin + DMA 直传,无 CPU 中转 |
3.3 测试项目
| 测试项 | 内容 |
|---|---|
| API 功能验证 | GetStatus / Pin / ReadC2H / WriteH2C / Unpin |
| 数据正确性 | 写入 pattern → 回读 → 逐字节比对 |
| 多尺寸性能 | 4KB ~ 8MB,6 种尺寸全覆盖 |
| 2GB 地址空间 | 遍历 FPGA 0~2GB 全范围,验证无地址死角 |
| 多缓冲区轮转 | 4 帧 GPU 缓冲区循环采集,对比单缓冲区 |
| TransferMode 兼容 | 老接口零修改,内部自动路由至 GPU Direct |
| 12 小时稳定性 | 72 次采样,功耗/性能/抖动全记录 |
3.4 关键参数
- 传输块大小: 4MB(长测试) / 4KB~8MB(快速测试)
- FPGA 地址步进: 64MB(遍历 0~2GB,32 个测试点/轮)
- 采样频率: 每 10 分钟(长测试)
- 每采样点迭代: 5 次取平均
- 功耗采集: INA3221 传感器 (VDD_IN 通道)
4. 测试结果
4.1 多尺寸性能对比(快速测试)
时延对比 (μs,越小越好)
| 数据大小 | CPU DMA 仅 | FPGA→CPU→GPU 全路径 | GPU Direct | 加速比 (vs CPU) | 加速比 (vs 全路径) |
|---|---|---|---|---|---|
| 4KB | 82.6 / 58.4 | 232.8 / 86.2 | 54.4 / 45.4 | 1.52x / 1.29x | 4.28x / 1.90x |
| 64KB | 87.8 / 80.4 | 122.4 / 195.2 | 70.2 / 74.4 | 1.25x / 1.08x | 1.74x / 2.62x |
| 512KB | 341.8 / 301.6 | 635.2 / 740.2 | 292.0 / 243.4 | 1.17x / 1.24x | 2.18x / 3.04x |
| 1MB | 638.4 / 574.6 | 1016.0 / 1219.2 | 521.2 / 412.6 | 1.22x / 1.39x | 1.95x / 2.95x |
| 4MB | 2398.0 / 2102.8 | 3417.6 / 3522.2 | 1773.6 / 1487.0 | 1.35x / 1.41x | 1.93x / 2.37x |
| 8MB | 4888.2 / 4250.0 | 6564.8 / 6507.6 | 3557.4 / 2951.2 | 1.37x / 1.44x | 1.85x / 2.21x |
表格格式: Read / Write
带宽对比 (GB/s,越大越好)
| 数据大小 | CPU DMA 仅 | FPGA→CPU→GPU | GPU Direct |
|---|---|---|---|
| 512KB | 1.53 / 1.74 | 0.83 / 0.71 | 1.80 / 2.15 |
| 1MB | 1.64 / 1.82 | 1.03 / 0.86 | 2.01 / 2.54 |
| 4MB | 1.75 / 1.99 | 1.23 / 1.19 | 2.36 / 2.82 |
| 8MB | 1.72 / 1.97 | 1.28 / 1.29 | 2.36 / 2.84 |
GPU Direct 峰值带宽达 2.84 GB/s,逼近 PCIe Gen3 x4 理论极限
4.2 TransferMode 兼容模式性能
用户代码零修改 (仍调用 HelloFPGA_DMA_MM_*),仅通过 2 行配置切换模式:
| 数据大小 | CPU 模式 (μs) | GPU_PINNED 模式 (μs) | 加速比 |
|---|---|---|---|
| 4KB | 79.6 / 65.4 | 57.8 / 51.8 | 1.38x / 1.26x |
| 64KB | 140.4 / 123.2 | 69.8 / 75.0 | 2.01x / 1.64x |
| 256KB | 198.2 / 210.6 | 141.4 / 155.2 | 1.40x / 1.36x |
| 1MB | 607.0 / 677.8 | 417.2 / 487.6 | 1.45x / 1.39x |
| 4MB | 2183.2 / 2516.2 | 1491.8 / 1781.4 | 1.46x / 1.41x |
| 8MB | 4279.4 / 4977.4 | 2954.4 / 3541.0 | 1.45x / 1.41x |
格式: Write / Read
4.3 多缓冲区 vs 单缓冲区对比
模拟实际图像采集场景:4 个 GPU 缓冲区轮转 vs 单缓冲区重复读写(100 次迭代,1MB/帧)
| 指标 | 多缓冲区 (4帧轮转) | 单缓冲区 | 差异 |
|---|---|---|---|
| 平均时延 | 501.7 μs | 491.9 μs | - |
| 最小时延 | 460.0 μs | 455.0 μs | - |
| 最大时延 | 1124.0 μs | 660.0 μs | - |
| 平均带宽 | 2.09 GB/s | 2.13 GB/s | - |
| 等效帧率 | 1993 fps | 2033 fps | - |
| 比值 | - | - | 0.98x (基本一致) |
结论:多缓冲区地址查表匹配开销可忽略不计,不影响性能。
4.4 12 小时长稳态测试
- 测试时长: 12.00 小时
- 采样点数: 72 次(每 10 分钟)
- 传输块大小: 4MB
- 地址遍历: 0 ~ 2GB(完成 2 轮完整遍历)
性能统计
| 指标 | CPU DMA 仅 | FPGA→CPU→GPU | GPU Direct |
|---|---|---|---|
| 平均 Read (μs) | 2711.9 | 3518.3 | 1785.1 |
| 平均 Write (μs) | 2276.2 | 3701.2 | 1489.0 |
| 平均带宽 Read | 1.55 GB/s | 1.19 GB/s | 2.35 GB/s |
| 平均带宽 Write | 1.84 GB/s | 1.13 GB/s | 2.82 GB/s |
加速比
| 对比基准 | Read | Write |
|---|---|---|
| GPU Direct vs CPU DMA | 1.52x | 1.53x |
| GPU Direct vs 传统全路径 | 1.97x | 2.49x |
性能稳定性
| 指标 | GPU Direct Read | GPU Direct Write |
|---|---|---|
| 最小时延 | 1749.6 μs | 1461.2 μs |
| 最大时延 | 2008.0 μs | 1554.6 μs |
| 抖动 (max-min) | 258.4 μs | 93.4 μs |
| 相对波动 | ±7.2% | ±3.1% |
时间趋势(无退化)
| 时段 | GPU Read 平均 | GPU Write 平均 | 功耗 |
|---|---|---|---|
| 0 ~ 4h | 1782 μs | 1483 μs | 18.38 W |
| 4 ~ 8h | 1784 μs | 1490 μs | 18.65 W |
| 8 ~ 12h | 1786 μs | 1488 μs | 18.78 W |
功耗统计
| 指标 | 数值 |
|---|---|
| 平均功耗 | 18.57 W |
| 最小功耗 | 18.23 W |
| 最大功耗 | 18.91 W |
| 波动范围 | 0.69 W (±1.8%) |
地址空间一致性
FPGA 全2GB 地址范围(0MB ~ 1984MB,64MB 步进)的 GPU Direct 传输时延无显著差异(标准差 < 15μs),表明 FPGA 端 DDR 控制器对全地址空间性能一致。
4.5 x86 Desktop 平台 (H100) 测试结果
测试日期: 2025-07-05
平台: Intel Xeon 8462Y+ / NVIDIA H100 PCIe / FPGA PCIe Gen3 x8
测试结果: 15 项全部通过
4.5.1 API 功能验证
| 测试项 | 结果 | 说明 |
|---|---|---|
| GetStatus | ✅ PASS | platform=2 (Desktop), supported=1, alignment=65536 |
| Pin | ✅ PASS | 使用 cudaMalloc 分配设备显存, 耗时 45.0 μs |
| ReadC2H (批量) | ✅ PASS | 1MB, 160.0 μs, 6.55 GB/s |
| WriteH2C (批量) | ✅ PASS | 1MB, 164.0 μs, 6.39 GB/s |
| Unpin | ✅ PASS | 耗时 17.0 μs |
| 数据正确性 | ✅ PASS | 262144 int32 全部匹配 |
| 批量模式 10×ReadC2H | ✅ PASS | 10MB, 1503.0 μs, 6.98 GB/s |
| 2GB地址空间 7点 | ✅ PASS | 全部数据匹配 |
| 2GB压力测试 6偏移 | ✅ PASS | 全部通过 |
| 2GB边界测试 6点 | ✅ PASS | 全部通过 |
4.5.2 多尺寸性能对比
时延对比 (μs,越小越好)
| 数据大小 | CPU DMA 仅 (Read/Write) | FPGA→CPU→GPU 全路径 | GPU Direct Handle | 加速比 (vs CPU) | 加速比 (vs 全路径) |
|---|---|---|---|---|---|
| 4KB | 15.2 / 13.2 | 29.8 / 37.6 | 12.8 / 13.4 | 1.19x / 0.99x | 2.33x / 2.81x |
| 64KB | 22.2 / 21.8 | 33.0 / 36.8 | 20.4 / 22.0 | 1.09x / 0.99x | 1.62x / 1.67x |
| 512KB | 94.2 / 93.6 | 118.8 / 146.2 | 82.2 / 86.0 | 1.15x / 1.09x | 1.45x / 1.70x |
| 1MB | 175.0 / 176.4 | 220.8 / 270.0 | 152.2 / 159.2 | 1.15x / 1.11x | 1.45x / 1.70x |
| 4MB | 672.8 / 664.8 | 912.0 / 924.0 | 574.8 / 600.8 | 1.17x / 1.11x | 1.59x / 1.54x |
| 8MB | 1330.2 / 1332.0 | 1739.4 / 1784.2 | 1138.8 / 1191.8 | 1.17x / 1.12x | 1.53x / 1.50x |
带宽对比 (GB/s,越大越好)
| 数据大小 | CPU DMA 仅 | FPGA→CPU→GPU | GPU Direct Handle | GPU Direct MM (auto) |
|---|---|---|---|---|
| 512KB | 5.57 / 5.60 | 4.41 / 3.59 | 6.38 / 6.10 | 5.04 / 5.01 |
| 1MB | 5.99 / 5.94 | 4.75 / 3.88 | 6.89 / 6.59 | 5.95 / 5.67 |
| 4MB | 6.23 / 6.31 | 4.60 / 4.54 | 7.30 / 6.98 | 6.62 / 6.41 |
| 8MB | 6.31 / 6.30 | 4.82 / 4.70 | 7.37 / 7.04 | 6.79 / 6.51 |
GPU Direct Handle 峰值带宽达 7.37 GB/s,逼近 PCIe Gen3 x8 理论极限 (~7.88 GB/s)
4.5.3 TransferMode 兼容模式性能 (Desktop)
用户代码零修改 (仍调用 HelloFPGA_DMA_MM_*),仅通过配置切换模式:
| 数据大小 | CPU 模式 (Write/Read μs) | GPU_PINNED 模式 (μs) | 加速比 (Wr/Rd) |
|---|---|---|---|
| 4KB | 13.2 / 13.2 | 12.2 / 10.6 | 1.08x / 1.25x |
| 64KB | 21.8 / 21.2 | 20.0 / 18.4 | 1.09x / 1.15x |
| 256KB | 52.2 / 51.6 | 47.4 / 45.0 | 1.10x / 1.15x |
| 1MB | 174.0 / 173.0 | 157.6 / 151.8 | 1.10x / 1.14x |
| 4MB | 661.4 / 655.8 | 596.6 / 571.2 | 1.11x / 1.15x |
| 8MB | 1307.4 / 1295.8 | 1183.0 / 1133.2 | 1.11x / 1.14x |
Desktop 平台加速比 ~1.1-1.15x,因 PCIe Gen3 x8 带宽充裕,CPU DMA 本身已接近峰值
4.5.4 多缓冲区 vs 单缓冲区 (Desktop)
4 个 GPU 缓冲区轮转 vs 单缓冲区重复读写(100 次迭代,1MB/帧)
| 指标 | 多缓冲区 (4帧轮转) | 单缓冲区 | 差异 |
|---|---|---|---|
| 平均时延 | 150.1 μs | 150.2 μs | - |
| 最小时延 | 149.0 μs | 149.0 μs | - |
| 最大时延 | 159.0 μs | 159.0 μs | - |
| 波动范围 | 10.0 μs | 10.0 μs | - |
| 平均带宽 | 6.985 GB/s | 6.981 GB/s | - |
| 等效帧率 | 6661 fps | 6658 fps | - |
| 比值 | - | - | 1.0005x (基本一致) |
结论:多缓冲区地址查表开销可忽略不计,帧率达 6600+ fps。
4.5.5 12 小时长稳态测试 (Desktop H100)
测试日期: 2026-07-05 12:05 ~ 23:55
测试时长: 12.00 小时
采样点数: 72 次(每 10 分钟)
传输块大小: 4MB
地址遍历: 0 ~ 2GB(完成 2 轮完整遍历)
原始数据:
gpu_direct_long_test_20260705_120520.csv
性能统计
| 指标 | CPU DMA 仅 | FPGA→CPU→GPU 全路径 | GPU Direct |
|---|---|---|---|
| 平均 Read (μs) | 948.4 | 1671.6 | 601.0 |
| 平均 Write (μs) | 869.8 | 1967.1 | 620.4 |
| 平均带宽 Read | 4.42 GB/s | 2.51 GB/s | 6.98 GB/s |
| 平均带宽 Write | 4.82 GB/s | 2.13 GB/s | 6.76 GB/s |
加速比
| 对比基准 | Read | Write |
|---|---|---|
| GPU Direct vs CPU DMA | 1.58x | 1.40x |
| GPU Direct vs 传统全路径 | 2.78x | 3.17x |
性能稳定性
| 指标 | GPU Direct Read | GPU Direct Write |
|---|---|---|
| 最小时延 | 596.8 μs | 615.8 μs |
| 最大时延 | 711.2 μs | 736.8 μs |
| 抖动 (max-min) | 114.4 μs | 121.0 μs |
| 相对波动 | ±8.8% | ±9.3% |
注:出现 2 个异常点(640min Read=711.2μs, 660min Write=736.8μs),其余 70 个采样点均在 ±5% 范围内,整体稳定性良好。
时间趋势(无退化)
| 时段 | GPU Read 平均 | GPU Write 平均 | 备注 |
|---|---|---|---|
| 0 ~ 4h | 598.8 μs | 617.6 μs | 稳定 |
| 4 ~ 8h | 600.2 μs | 619.8 μs | 稳定 |
| 8 ~ 12h | 601.0 μs | 621.2 μs | 稳定 |
功耗统计
| 指标 | 数值 |
|---|---|
| 功耗采集 | 未接入 INA3221 传感器 |
| 备注 | 后续测试将补充功耗数据 |
地址空间一致性
FPGA 全 2GB 地址范围(0MB ~ 1984MB,64MB 步进)的 GPU Direct 传输时延无显著差异(标准差 < 8μs),表明 FPGA 端 DDR 控制器对全地址空间性能一致。
4.5.6 平台对比汇总 (Tegra vs Desktop)
| 指标 | Tegra (Orin, Gen2 x4) | Desktop (H100, Gen3 x8) | 提升倍数 |
|---|---|---|---|
| GPU Direct Handle 峰值带宽 | 2.84 GB/s | 7.37 GB/s | 2.6x |
| GPU Direct Handle 1MB Read | 521.2 μs | 152.2 μs | 3.4x |
| Pin 操作时延 | ~53 μs | ~45 μs | - |
| Unpin 操作时延 | ~17 μs | ~17 μs | 相当 |
| 多缓冲区帧率 (1MB) | 1993 fps | 6661 fps | 3.3x |
| 数据正确性 | ✅ | ✅ | - |
| 2GB地址空间覆盖 | ✅ | ✅ | - |
性能提升主要来源于 PCIe 链路升级 (Gen3 x4 → Gen3 x8,理论带宽 2x)
5. 结论
5.1 性能结论
-
GPU Direct 相比传统全路径 (FPGA→CPU→GPU) 加速 ~1.5x~3.2x
- Tegra: Read 加速 1.97x,Write 加速 2.49x(4MB 块,12h 平均)
- Desktop: Read 加速 2.78x,Write 加速 3.17x(4MB 块,12h 平均)
- 消除 cudaMemcpy 中间拷贝是主要收益来源
-
GPU Direct 相比 CPU DMA 仅加速 ~1.1x~1.6x
- Tegra: ~1.5x(PCIe Gen3 x4 带宽受限,GPU Direct 优势明显)
- Desktop: Read 1.58x / Write 1.40x(PCIe Gen3 x8 带宽充裕,GPU Direct 仍有明显优势)
-
峰值带宽
- Tegra (Gen2 x4): 2.84 GB/s,接近理论极限
- Desktop (Gen3 x8): 7.37 GB/s,逼近理论极限 (~7.88 GB/s,达 93.5%)
- Desktop 长稳态 Read 平均带宽: 6.98 GB/s ,Write 平均带宽: 6.76 GB/s
-
Desktop 平台帧率达 6600+ fps (1MB/帧),适合高帧率图像采集场景
-
Desktop 12 小时长稳态:GPU Direct Read 平均 601.0 μs,Write 平均 620.4 μs,性能无退化
5.2 稳定性结论
- Tegra 12 小时无性能退化:前 4h 与后 4h 性能差异 < 0.3%
- Desktop 12 小时无性能退化:0~4h / 4~8h / 8~12h 三时段 Read/Write 时延波动 < 0.4%
- 无内存泄漏:Pin/Unpin 生命周期管理正确
- 无热节流:Tegra 功耗稳定在 18.2~18.9W,无过热降频
- 2GB 全地址空间一致:无 FPGA 端 DDR 热点(两平台均验证)
5.3 兼容性结论
- 完全向后兼容:老代码无需任何修改即可继续使用
- 最小侵入接入:仅需新增 2 行代码(SetGPUBuffer + SetTransferMode)
- 多缓冲区无开销:4 帧轮转性能与单帧一致,适合图像流应用
- 跨平台透明 :同一套测试代码同时兼容 Tegra (cudaHostAlloc) 和 Desktop (cudaMalloc),通过
GetStatus.platform自动适配 - 双平台验证通过:Tegra (Orin) 和 Desktop (H100) 均 15/15 测试全通过
5.4 适用场景建议
| 场景 | 推荐方案 | 预期加速 |
|---|---|---|
| FPGA 图像采集 → GPU 推理 | GPU Direct (多缓冲) | 2.0x |
| FPGA 信号处理 → GPU 计算 | GPU Direct (单缓冲) | 1.5x~2.0x |
| FPGA ↔ CPU 数据交换(不涉及GPU) | 传统 CPU DMA | 无需切换 |
| 小数据包 (< 4KB) | 传统 CPU DMA | GPU Direct 优势不明显 |
5.5 建议与限制
- 最小传输块建议 ≥ 64KB:小数据包中 DMA 建立开销占比大,GPU Direct 优势有限
- Pin 操作仅需一次:应在初始化阶段完成,避免传输循环中频繁 Pin/Unpin
- 4K 对齐要求:GPU 缓冲地址和大小必须 4096 字节对齐
- 需 root 权限:GPU Direct ioctl 需要特权访问设备文件
6. 与 NVIDIA jetson-rdma-picoevb 开源方案对比
NVIDIA/jetson-rdma-picoevb 是 NVIDIA 官方提供的最小化 GPU Direct RDMA 硬件演示项目,使用 PicoEVB (Xilinx Artix-7) FPGA 板卡在 Jetson AGX Xavier 上验证 GPUDirect RDMA 功能。下面从多个维度与 HelloFPGA GPU Direct 方案进行对比。
6.1 架构对比
| 维度 | NVIDIA picoevb | HelloFPGA GPU Direct |
|---|---|---|
| 定位 | 最小化演示/参考实现 | 生产级驱动框架 |
| FPGA | PicoEVB (Artix-7) / HTG-K800 (Kintex UltraScale) | Xilinx Kintex/Artix 系列 (XDMA IP) |
| 内核模块 | picoevb-rdma.ko (独立专用驱动) |
HelloFPGA.ko (XDMA 基础 + GPU Direct 扩展模块) |
| 用户空间 | 独立测试程序直接 ioctl | libHelloFPGACore.so 封装库 + TransferMode 兼容层 |
| 设备文件 | /dev/picoevb (单设备) |
/dev/HelloFPGA0_c2h_*, /dev/HelloFPGA0_h2c_* (多通道) |
| 多设备支持 | 不支持 | 支持多 FPGA 板卡 (slot 编号) |
6.2 内核层实现对比
| 特性 | picoevb | HelloFPGA |
|---|---|---|
| Pin 接口 | nvidia_p2p_get_pages() (Tegra简化版,无 token) |
nvidia_p2p_get_pages() (同, xdma_gpu_tegra 适配层) |
| Unpin 接口 | nvidia_p2p_put_pages() |
nvidia_p2p_put_pages() |
| DMA 映射 | nvidia_p2p_dma_map_pages() → 手动操作 BAR |
nvidia_p2p_dma_map_pages() → XDMA SG-DMA 引擎 |
| 传输方式 | FPGA BAR 直接读写 (MMIO/PIO), 64KB 分块 | XDMA Scatter-Gather DMA 引擎,支持大块连续传输 |
| 中断 | 无 (轮询) | XDMA 中断/轮询可选 |
| DMA 引擎 | FPGA 自定义简单引擎 (64KB BRAM 中转) | Xilinx XDMA IP 硬核 (支持 MM/ST 模式) |
| Pin 缓存 | 无 | 有 (cache_hits/cache_misses 统计) |
| 地址对齐 | 64KB (Desktop) / 4KB (Tegra) | 4KB (Tegra) / 64KB (Desktop) |
6.3 用户空间接口对比
picoevb 方案 (裸 ioctl):
c
// 1. 分配 CUDA 内存
cudaHostAlloc(&buf, size, cudaHostAllocDefault); // Tegra 必须用 cudaHostAlloc
cuPointerSetAttribute(&flag, CU_POINTER_ATTRIBUTE_SYNC_MEMOPS, (CUdeviceptr)buf);
// 2. Pin
ioctl(fd, PICOEVB_IOC_PIN_CUDA, &pin_params);
// 3. DMA 传输
ioctl(fd, PICOEVB_IOC_DMA_C2H, &xfer_params); // 每次传输都需要单独 ioctl
// 4. Unpin
ioctl(fd, PICOEVB_IOC_UNPIN_CUDA, &unpin_params);
HelloFPGA 方案 (封装 API + TransferMode 兼容):
c
// 方式一:显式 GPU Direct API
HelloFPGA_GPUDirect_Pin(hDev, gpuAddr, size, &handle);
HelloFPGA_GPUDirect_ReadC2H(hDev, ch, handle, ep_addr, size, timeout);
HelloFPGA_GPUDirect_Unpin(hDev, handle);
// 方式二:TransferMode 兼容(零修改老代码)
HelloFPGA_SetGPUBuffer(hDev, gpuAddr, size); // Pin 一次
HelloFPGA_SetTransferMode(hDev, HELLOFPGA_XFER_MODE_GPU_PINNED);
HelloFPGA_DMA_MM_ReadC2H(hDev, ch, buf, offset, len, &actual); // 内部自动路由
HelloFPGA_ReleaseGPUBuffer(hDev); // 清理
6.4 传输性能机制对比
| 特性 | picoevb | HelloFPGA |
|---|---|---|
| DMA 类型 | FPGA 自定义引擎 (BAR-based) | Xilinx XDMA IP (SG-DMA) |
| 最大单次传输 | 64KB (受 FPGA BRAM 限制) | 无限制 (SG 链表) |
| 大数据传输 | 应用层分 64KB 块循环 | 驱动层自动 SG 分片 |
| 中断/完成通知 | 轮询 BAR 状态寄存器 | XDMA 完成中断 + 事件通知 |
| 零拷贝路径 | ✅ GPU 页面 → FPGA BAR → GPU 页面 | ✅ GPU 页面 → PCIe DMA → FPGA DDR |
| 吞吐量瓶颈 | FPGA BRAM 64KB 中转 + 轮询延迟 | PCIe 链路带宽 (无额外中转) |
6.5 关键差异总结
| 对比项 | picoevb 优势 | HelloFPGA 优势 |
|---|---|---|
| 代码复杂度 | ✅ 极简 (~800行内核代码) | 功能丰富但复杂 |
| 学习参考价值 | ✅ 适合理解 RDMA 原理 | 适合生产部署 |
| 传输效率 | 64KB 分块,吞吐受限 | ✅ SG-DMA 大块传输,带宽接近 PCIe 极限 |
| API 易用性 | 裸 ioctl,需手动管理 | ✅ 高层 API + 向后兼容 |
| 多缓冲区 | 不支持 | ✅ 多 GPU buffer 注册 + 地址查表路由 |
| 生产级特性 | 无 | ✅ 进程互斥锁、错误恢复、多设备、DB 记录 |
| 平台支持 | Xavier/PC | ✅ Xavier/Orin/PC |
| 内存分配 | Tegra 必须 cudaHostAlloc |
✅ cudaHostAlloc 或 cudaMalloc 均可 |
| TransferMode | 无 | ✅ 老代码零修改切换 GPU Direct |
6.6 技术路线差异分析
picoevb 的设计哲学:最小化验证 GPU Direct RDMA 的可行性。FPGA 只有一块 64KB BRAM,内核模块直接操作 BAR 空间做 PIO 读写,传输逻辑由应用层驱动。适合学习和原型验证。
HelloFPGA 的设计哲学:生产级高性能框架。利用 Xilinx XDMA IP 提供的硬件 SG-DMA 引擎,在驱动层完成地址翻译和 DMA 调度,对用户层暴露高层 API,并通过 TransferMode 机制实现零侵入式升级。适合实际产品部署。
核心技术差异:
-
DMA 引擎层面:picoevb 使用 FPGA 自定义的简单引擎(BAR 读写),受限于 BRAM 大小;HelloFPGA 使用 Xilinx XDMA IP 核的硬件 SG-DMA,可一次发起 MB 级传输。
-
Pin 管理策略:picoevb 每次操作都是独立的 Pin→Transfer→Unpin 流程;HelloFPGA 支持 "Pin Once, Use Many" 模式,Pin 操作在初始化阶段完成一次,后续传输复用 Handle,消除了 Pin/Unpin 开销。
-
兼容层设计 :picoevb 无兼容层概念,应用需要直接感知 RDMA;HelloFPGA 通过 TransferMode 路由,让已有的
DMA_MM_Read/Write接口在设置模式后自动切换底层路径,实现了零代码改造升级。 -
内存分配差异 :picoevb 在 Tegra 平台强制 使用
cudaHostAlloc()(因为 Tegra 版nvidia_p2p_get_pages不支持cudaMalloc的地址);HelloFPGA 通过 xdma_gpu_tegra 适配层统一处理,对用户透明。