CUDA中GPU使用率低

时间:2016-09-25 14:07:53

标签: performance cuda load gpu gpgpu

我实现了一个程序,该程序使用来自不同CPU线程的不同CUDA流。使用这些流通过cudaMemcpyAsync实现内存复制。内核启动也在使用这些流。该程序正在进行双精度计算(我怀疑这是罪魁祸首,但是,对于double s的矩阵乘法,cuBlas达到75-85%的CPU使用率。还有还原操作,但它们是通过if(threadIdx.x < s)实现的,s每次迭代减少2次,因此其他块可以使用停滞的warp。该应用程序是GPU和CPU密集型的,它从上一次完成后开始另一项工作。所以我希望它能达到CPU或GPU的100%。

问题在于,如果信任GPU-Z 1.9.0,我的程序会产生30-40%的GPU负载(约占CPU负载的50%)。内存控制器负载为9-10%,总线接口负载为6%。这是因为CPU线程数等于CPU核心数。如果我将CPU线程数加倍,则负载大致保持不变(包括CPU负载)。

那为什么呢?瓶颈在哪里?

我使用的是GeForce GTX 560 Ti,CUDA 8RC,MSVC ++ 2013,Windows 10.

我的猜测是Windows 10应用了一些积极的节能功能,即使GPU和CPU温度很低,电源计划设置为“高性能”,电源为700W,同时功耗最高CPU和GPU TDP约为550W。

另一个猜测是,双精度速度是单精度速度的1/12,因为我的卡上每12个单精度CUDA核心有1个双精度CUDA核心,GPU-Z占100%使用所有单精度和双精度内核时的情况。但是,这些数字并不完全匹配。

1 个答案:

答案 0 :(得分:2)

显然原因是由于CUDA线程默认使用太多寄存器而导致占用率低。要告诉编译器每个线程的寄存器数量限制,可以使用__launch_bounds__,如here所述。因此,为了能够在560 Ti中启动所有1536个线程,对于块大小256,可以指定以下内容:

_global__ void __launch_bounds__(256, 6) MyKernel(...) { ... }

在限制每个CUDA线程的寄存器数量后,GPU使用率已提高到60%。

顺便说一句,NSight v5.1 for Visual Studio仍然支持5xx系列卡。来自It can be downloadedarchive

编辑:在使用来自多个CPU线程的多个GPU流的应用程序中,以下标志进一步将GPU使用率提高到70%:

cudaSetDeviceFlags(cudaDeviceScheduleYield | cudaDeviceMapHost | cudaDeviceLmemResizeToMax);
  • cudaDeviceScheduleYield允许其他线程在CPU执行时执行 线程正在等待GPU操作,而不是为GPU旋转 结果
  • cudaDeviceLmemResizeToMax,正如我所理解的那样,制造内核 启动自己异步并避免过多的本地内存 分配&安培;解除分配