概括:
我正在尝试编写一个内存绑定 OpenCL 程序,该程序接近我 GPU 上宣传的内存带宽。实际上,我偏离了约 50 倍。
设置:
我只有一张比较旧的 Polaris Card (RX580),所以我不能使用 CUDA,现在只能选择 OpenCL。我知道这是次优的,我无法让任何调试/性能计数器工作,但这就是我所拥有的。
我是 GPU 计算的新手,想感受一下我可以从 GPU 与 CPU 中获得的一些性能。对我来说首先要做的是内存带宽。
我编写了一个非常小的 OpenCL 内核,它从跨步内存位置读取,我希望波前中的所有工作人员一起在一个大内存段上执行连续内存访问,合并访问。然后内核对加载的数据所做的所有事情就是将这些值相加,并在最后将和写回另一个内存位置。代码(大部分是我从各种来源无耻地复制在一起的)非常简单
__kernel void ThroughputTestKernel(
__global float* vInMemory,
__global float* vOutMemory,
const int iNrOfIterations,
const int iNrOfWorkers
)
{
const int gtid = get_global_id(0);
__private float fAccumulator = 0.0;
for (int k = 0; k < iNrOfIterations; k++) {
fAccumulator += vInMemory[gtid + k * iNrOfWorkers];
}
vOutMemory[gtid] = fAccumulator;
}
我生成iNrOfWorkers
这些内核并测量它们完成处理所需的时间。对于我的测试,我设置iNrOfWorkers = 1024
和iNrOfIterations = 64*1024
. 从处理时间和iMemorySize = iNrOfWorkers * iNrOfIterations * sizeof(float)
我计算出大约 5GByte/s 的内存带宽。
期望:
我的问题是内存访问似乎比我被认为可以使用的 256GByte/s 慢一到两个数量级。
GCN ISA 手册 [1] 让我假设我有 36 个 CU,每个 CU 包含 4 个 SIMD 单元,每个单元处理 16 个元素的向量。因此,我应该有 36 4 16 = 2304 个可用的处理元素。
我产生的数量少于这个数量,即 1024 个全局工作单元(“线程”)。线程按顺序访问内存位置,相隔 1024 个位置,因此在循环的每次迭代中,整个波前访问 1024 个连续元素。因此,我相信 GPU 应该能够产生连续的内存地址访问,并且中间没有中断。
我的猜测是,它只产生很少的线程而不是 1024,可能每个 CU 一个?这样一来,它就必须一遍又一遍地重新读取数据。不过,我不知道如何验证这一点。
[1] http://developer.amd.com/wordpress/media/2013/12/AMD_GCN3_Instruction_Set_Architecture_rev1.1.pdf