4. 性能指标与基准:带宽、延迟、吞吐量、消息速率等关键性能指标及测量方法

在定位 GPU Direct 通信瓶颈之前,必须先建立一套可量化、可复现的度量体系。没有准确的性能指标,任何“优化”都只是猜测。本章聚焦于 GPU Direct 场景下最核心的四类指标:带宽(Bandwidth)、延迟(Latency)、吞吐量(Throughput)与消息速率(Message Rate),并给出对应的测量工具与实验设计方法。

4.1 核心指标定义与物理含义

这四类指标从不同维度刻画通信链路的性能,理解它们的区别是定位问题的前提。

  • 带宽(Bandwidth):单位时间内链路上能够传输的最大数据量,单位通常为 GB/s 或 Gb/s。它反映链路的“容量上限”,受限于 PCIe 通道数、NVLink 版本、网络接口速率(如 HDR 200Gb/s)等因素。在 GPU Direct 场景中,我们更关心有效带宽,即实际传输用户数据(不含协议头、填充字节)的速率。
  • 延迟(Latency):从发送端发起操作(如调用 cudaMemcpyPeerAsync)到接收端完成数据放置之间的时间差,单位通常为微秒(µs)或纳秒(ns)。它包含软件开销(驱动、运行时)、硬件传输时间与排队等待时间。延迟是“小消息”场景(如控制信号、同步原语)的关键指标。
  • 吞吐量(Throughput):在持续负载下,系统整体完成的数据处理速率。与带宽不同,吞吐量考虑了实际应用中的协议开销、中断处理、内存拷贝等额外成本。例如,一条理论带宽 64 GB/s 的 PCIe 4.0 x16 链路,实际吞吐量可能只有 50 GB/s。
  • 消息速率(Message Rate):每秒能够处理的消息(或报文)数量,单位是 Msg/s。它衡量的是“小包处理能力”,受限于硬件描述符处理速度、软件协议栈效率以及 GPU 内核启动开销。对于控制平面或元数据交换频繁的应用,消息速率往往比带宽更先成为瓶颈。
易混淆点:带宽是“水管有多粗”,延迟是“水龙头打开到水流出的时间”,吞吐量是“实际每秒流出多少水”,消息速率是“每秒能开关多少次水龙头”。在 GPU Direct 通信中,大块数据传输看带宽,同步与控制消息看延迟与消息速率。

4.2 测量工具与基准方法

NVIDIA 官方提供了 nvbandwidth 和 perftest(配合 nccl-tests)等工具,分别针对 GPU 本地互连与网络互连场景。以下是常用测量矩阵:

指标 推荐工具 测量对象 关键参数
GPU↔GPU 带宽(同节点) nvbandwidth NVLink / PCIe --mode p2p
GPU↔GPU 延迟(同节点) nvbandwidth NVLink 小消息 --mode p2p_latency
跨节点 GPU 通信 nccl-tests RDMA + GPU Direct allreduce -b 8 -e 128M -f 2
网络消息速率 perftest IB / RoCE 小包 ib_send_bw -s 16 -t 30

以 nccl-tests 中的 allreduce 测试为例,其输出会同时给出总线带宽(Bus Bandwidth)与算法带宽(Alg Bandwidth)。总线带宽将通信量按 2*(n-1)/n 的系数折算,更接近硬件实际负载;算法带宽则直接以数据量除以时间,便于与应用层需求对比。

# 测量 8 卡 A100 同节点 NVLink 全规约性能
nccl-tests/build/all_reduce_perf -b 8 -e 256M -f 2 -g 8

# 输出关键行示例
#  8 bytes:  time 12.5 us  algbw 0.64 GB/s  busbw 1.12 GB/s
# 256M bytes: time 4100 us  algbw 65.5 GB/s  busbw 114.6 GB/s

注意观察:当消息尺寸从 8 字节增长到 256MB 时,总线带宽从 1.12 GB/s 跃升至 114.6 GB/s。这说明小消息受延迟限制,大消息才能逼近硬件带宽上限。因此,测量必须覆盖从最小到最大的完整消息尺寸范围,才能获得完整的性能曲线。

4.3 测量方法流程与实验设计

为了获得可信的基准数据,需要遵循以下流程,避免将噪声当作信号:

  1. 环境固定:锁定 GPU 时钟频率(nvidia-smi -lgc 1410),关闭动态超频;使用 taskset 将测试进程绑定到特定 CPU 核心,避免调度抖动。
  2. 预热与重复:每个消息尺寸至少重复 100 次,丢弃前 10 次结果(用于建立连接、分配缓存)。取中位数而非平均值,以排除偶发调度延迟的干扰。
  3. 双向与单向分离:单向带宽测试(如 ib_write_bw)与双向带宽测试(ib_write_bw --bidirectional)结果差异可能巨大。GPU Direct 场景中,若双向带宽远低于单向带宽的两倍,可能存在 PCIe 交换机端口拥塞。
  4. CPU 参与度控制:使用 cudaMemcpyPeerAsync 配合 CUDA 事件计时,确保测量的是 GPU 引擎之间的传输时间,而非 CPU 启动开销。若使用 cudaMemcpy(同步版本),则测得的延迟包含 CPU 等待时间,不适合作为硬件延迟基准。
实操示例:测量 P2P 延迟的 CUDA 代码片段
cudaEvent_t start, stop;
cudaEventCreate(&start); cudaEventCreate(&stop);
float ms = 0.0f;

// 预热
for (int i = 0; i < 10; i++) {
    cudaMemcpyPeerAsync(dst, dev2, src, dev1, size, stream);
}
cudaDeviceSynchronize();

// 正式测量 100 次
for (int i = 0; i < 100; i++) {
    cudaEventRecord(start, stream);
    cudaMemcpyPeerAsync(dst, dev2, src, dev1, size, stream);
    cudaEventRecord(stop, stream);
    cudaEventSynchronize(stop);
    cudaEventElapsedTime(&ms, start, stop);
    total_ms += ms;
}
printf("P2P latency (size=%zu): %.2f us\n", size, total_ms / 100 * 1000);

4.4 案例:一次“带宽虚高”的陷阱复盘

某团队报告其 GPU Direct RDMA 带宽达到 185 GB/s,远超理论预期。但实际应用性能并未提升。复查后发现:

  • 测试使用了 cudaMallocPitch 分配的内存,行对齐填充导致实际传输数据量比逻辑数据量多 15%。
  • 工具报告的是链路层带宽(包含 CRC、填充位),而非有效载荷带宽。
  • 未关闭 GPU 的 ECC 功能,ECC 校验位占用额外带宽。

修正方法:使用 cudaMalloc 分配连续内存,并在 ib_write_bw 中指定 --report_gbits 以区分 Gb/s 与 GB/s。最终测得有效带宽为 152 GB/s,与理论值吻合。这个案例说明:必须明确指标的口径(有效载荷 vs 链路层),否则基准数据会误导优化方向。

4.5 常见误区与复盘清单

误区 1:只测最大消息尺寸。许多工程师只运行 1MB 以上的测试,得出“带宽已满”的结论。但实际应用可能 80% 的消息都小于 4KB,此时延迟与消息速率才是瓶颈。务必测量 8B 到 64MB 的完整曲线。
误区 2:忽略 NUMA 亲和性。GPU 与网卡可能挂在不同的 PCIe 交换机下。若 CPU 访问远端 NUMA 节点的内存来发起 DMA,延迟会增加 30% 以上。测量前应使用 nvidia-smi topo -m 检查拓扑,并绑定正确的 NUMA 节点。
误区 3:将 CPU 到 GPU 的拷贝时间混入 P2P 测量。如果使用 cudaMemcpy(同步版本)测量 P2P 延迟,实际测得的是“CPU 发起→GPU 执行→CPU 轮询完成”的完整往返时间,包含了 PCIe 的 CPU 侧开销。正确做法是使用 cudaMemcpyPeerAsync 配合 CUDA 事件,或使用 nvbandwidth 的专用延迟测试模式。

最后,建议每次调优操作前后都执行同一套基准脚本,并将结果记录为 CSV 格式以便对比。以下是一个最小化的基准清单,可作为团队内部的标准动作:

# 1. 同节点 P2P 带宽(NVLink)
nvbandwidth --mode p2p --size 1M 8M 64M 256M
# 2. 同节点 P2P 延迟(4B 到 64B)
nvbandwidth --mode p2p_latency --size 4 8 16 32 64
# 3. 跨节点 AllReduce(8 卡)
nccl-tests/build/all_reduce_perf -b 8 -e 64M -f 2 -g 8
# 4. 跨节点单向写带宽(GPU Direct RDMA)
ib_write_bw -d mlx5_0 --use_cuda=0 --cuda_gpu=0 -s 1M -n 1000