2. 带宽测试工具介绍
做GPU性能优化,第一件事是什么?
我个人习惯,先摸清显存带宽的底牌。你想想看,如果连硬件能跑多快都不清楚,后面再怎么调优都是盲人摸象。这一章,我就带大家认识几款常用的带宽测试工具——从官方工具到我们自己写的微基准测试,挨个过一遍。
2.1 nvidia-smi:最基础的带宽观测工具
nvidia-smi 大家应该都不陌生。平时看显存占用、温度、功耗,都用它。但很多人不知道,它也能看带宽相关的指标。
关键参数:
- Memory-Usage:显存使用量,单位 MiB。这个能看出当前显存压力。
- GPU-Util:计算单元利用率,不是带宽利用率。
- Volatile GPU-Util:这个值如果飙到 100%,不一定代表带宽跑满了——可能是计算瓶颈。
嗯,这里要注意:nvidia-smi 不直接显示带宽利用率。它只能给你一个侧面参考。我在项目中遇到过好几次,有人看到 GPU-Util 100% 就以为带宽吃满了,结果一测实际带宽才用了 60%。
那怎么看带宽利用率呢?可以用 nvidia-smi dmon 或者 nvidia-smi pmon,它们能显示更细粒度的指标。比如 dmon 里的 mem_util 字段,就是显存控制器利用率——这个才接近真实的带宽占用。
# 查看实时显存控制器利用率
nvidia-smi dmon -s pucvmet -d 1
输出大概长这样:
# gpu pwr gtemp mtemp sm mem enc dec mclk pclk
# Idx W C C % % % % MHz MHz
0 250 75 72 98 85 0 0 5000 1350
看到 mem 那列了吗?85% 说明显存控制器已经比较忙了。但注意,这也不是精确的带宽利用率——它反映的是控制器繁忙程度,不是实际数据传输量。
小技巧: 我习惯用 nvidia-smi --query-gpu=memory.bandwidth --format=csv 查看理论带宽。虽然它只显示硬件规格,不是实时值,但至少能让你知道这张卡的上限在哪。
2.2 bandwidthTest:官方带宽测试程序
NVIDIA 官方提供了 bandwidthTest,在 CUDA 安装目录的 samples/1_Utilities/bandwidthTest 下。这个工具简单粗暴,直接测主机到设备、设备到主机、设备到设备三种传输模式的带宽。
我个人觉得,这是入门带宽测试最好的起点。为什么?因为它帮你屏蔽了底层细节,你只需要编译运行,就能拿到一个可信的基准值。
# 编译
cd /path/to/cuda-samples/Samples/1_Utilities/bandwidthTest
make
# 运行
./bandwidthTest
输出示例:
[CUDA Bandwidth Test] - Starting...
Running on...
Device 0: Tesla V100-SXM2-32GB
Quick Mode
Host to Device Bandwidth, 1 Device(s)
PINNED Memory Transfers
Transfer Size (Bytes) Bandwidth(GB/s)
33554432 12.45
Device to Host Bandwidth, 1 Device(s)
PINNED Memory Transfers
Transfer Size (Bytes) Bandwidth(GB/s)
33554432 13.21
Device to Device Bandwidth, 1 Device(s)
PINNED Memory Transfers
Transfer Size (Bytes) Bandwidth(GB/s)
33554432 898.76
看到没?Device to Device 的带宽是 898 GB/s,接近 V100 的理论带宽 900 GB/s。这说明显存本身没问题。但 Host to Device 只有 12 GB/s——这是 PCIe 3.0 x16 的瓶颈。
避坑指南: 我曾经遇到过有人用 bandwidthTest 测出的带宽只有理论值的一半,急得团团转。后来发现他用的默认模式是 pageable 内存,不是 pinned 内存。记住:一定要用 --memory=pinned 参数,否则带宽会低一个数量级。
常用的参数组合:
| 参数 | 说明 | 示例 |
|---|---|---|
--memory=pinned |
使用页锁定内存 | 必须加,否则结果不准 |
--mode=shmoo |
测试不同传输大小的带宽 | 看带宽随传输量变化 |
--device=0 |
指定 GPU 设备 | 多卡环境必用 |
--htod / --dtoh |
只测单向传输 | 节省时间 |
2.3 自定义CUDA微基准测试
官方工具虽好,但有时候不够灵活。比如我想测特定数据布局下的带宽,或者想看看不同访存模式对带宽的影响——这时候就得自己写微基准测试了。
说白了,自定义测试的核心就是:分配显存 → 启动 kernel → 计时 → 算带宽。我给大家一个最简版本:
#include <cuda_runtime.h>
#include <stdio.h>
__global__ void bandwidth_kernel(float *in, float *out, int N) {
int idx = blockIdx.x * blockDim.x + threadIdx.x;
if (idx < N) {
out[idx] = in[idx]; // 简单的拷贝
}
}
int main() {
int N = 1 << 28; // 256M 个 float
size_t bytes = N * sizeof(float);
float *d_in, *d_out;
cudaMalloc(&d_in, bytes);
cudaMalloc(&d_out, bytes);
// 预热
bandwidth_kernel<<<(N+255)/256, 256>>>(d_in, d_out, N);
cudaDeviceSynchronize();
// 计时
cudaEvent_t start, stop;
cudaEventCreate(&start);
cudaEventCreate(&stop);
cudaEventRecord(start);
bandwidth_kernel<<<(N+255)/256, 256>>>(d_in, d_out, N);
cudaEventRecord(stop);
cudaEventSynchronize(stop);
float ms;
cudaEventElapsedTime(&ms, start, stop);
double bw = (bytes * 2) / (ms / 1000.0) / 1e9; // 读+写 = 2倍
printf("Bandwidth: %.2f GB/s\n", bw);
cudaFree(d_in);
cudaFree(d_out);
return 0;
}
这个代码很简单,就是每个线程读一个 float,写一个 float。但注意:实际带宽要算读+写,所以是 bytes * 2。我刚开始写的时候忘了乘 2,结果带宽只有理论值的一半,查了半天才发现。
进阶玩法: 你可以修改 kernel 来测试不同访存模式:
- 连续访问:每个线程读相邻地址——带宽最高
- 跨步访问:每个线程跳过 N 个元素——带宽下降
- 随机访问:用随机索引——带宽惨不忍睹
举个例子,跨步访问的 kernel 长这样:
__global__ void stride_kernel(float *in, float *out, int N, int stride) {
int idx = (blockIdx.x * blockDim.x + threadIdx.x) * stride;
if (idx < N) {
out[idx] = in[idx];
}
}
你猜 stride=2 时带宽会掉多少?我实测过,在 V100 上 stride=2 时带宽降到连续访问的 50% 左右。stride=4 时只剩 25%。这就是 合并访问 的重要性——GPU 喜欢连续的数据,不喜欢跳着读。
2.4 工具对比与选择建议
说了这么多,到底该用哪个?我整理了一张表:
| 工具 | 适用场景 | 优点 | 缺点 |
|---|---|---|---|
| nvidia-smi | 快速查看显存状态 | 无需安装,实时监控 | 不直接显示带宽 |
| bandwidthTest | 获取标准带宽基准 | 官方可靠,一键运行 | 不能自定义访存模式 |
| 自定义微基准 | 深入分析访存行为 | 完全可控,灵活 | 需要编程,容易出错 |
我的建议是:先用 bandwidthTest 拿到基线,再用自定义测试深挖细节。比如你发现程序性能差,先用 bandwidthTest 确认硬件带宽没问题,然后写个微基准测试,看看是不是访存模式导致的。
个人经验: 我一般会在项目初期跑一次 bandwidthTest,把结果记下来。后面优化时,如果发现实际带宽远低于这个值,就知道问题出在访存模式上。省了很多排查时间。
2.5 本章小结
这一章我们聊了三类工具:
- nvidia-smi:日常监控,看个大概
- bandwidthTest:官方基准,快速定位硬件问题
- 自定义微基准:深入分析,找到访存瓶颈
记住一句话:没有万能工具,只有合适的场景。下一章我们会深入带宽测试的具体方法,包括如何设计测试用例、如何解读结果。但在此之前,我建议你先动手跑一遍 bandwidthTest,看看你的 GPU 到底能跑多快。
嗯,就到这里。有问题随时交流。