我有以下用于矩阵乘法的代码,为简单起见缩写。我计划使用block_size*block_size
的本地内存来保存一个子矩阵块。在NVIDIA GPU上运行时,我不断在-52
中收到错误代码clEnqueueNDRangeKernel
。经过研究后,我发现NVIDIA gpu的恒定内存大小非常小。
主机:
cl::Buffer a_buf{ context, CL_MEM_READ_ONLY | CL_MEM_COPY_HOST_PTR, a.bytes(), a.data };
cl::Buffer b_buf{ context, CL_MEM_READ_ONLY | CL_MEM_COPY_HOST_PTR, a.bytes(), bT.data };
cl::Buffer result_buf{ context, CL_MEM_READ_WRITE , result.bytes(), nullptr }; //for memory mapping
kernel.setArg(0, a_buf);
kernel.setArg(1, b_buf);
kernel.setArg(2, local_size*local_size* sizeof(float), nullptr);
kernel.setArg(3, local_size*local_size* sizeof(float), nullptr);
kernel.setArg(4, result_buf);
queue.enqueueNDRangeKernel(kernel, { 0,0 }, { a.rows, a.rows }, {local_size, local_size});
// ^ offset ^global work size ^local work size
内核:
__kernel void matrixMul(__constant float* a,
__constant float* b, //storing the original matrix data
__local float* a_local,
__local float* b_local, //storing a sub-matrix block for the work-group
__global float* result)
{...}
使用CL_DEVICE_MAX_CONSTANT_BUFFER_SIZE
,我的 RX580 返回几乎所有可用的VRAM,但是我的 GTX1650 仅返回64KB。当使用__constant
而非__global
时,我的RX580确实获得了显着的性能提升。 我做错了什么吗?还是碰巧是这样,所以我需要准备不同的内核才能在AMD和NVIDIA GPU上运行?
编辑:我在github here上发现了一个相关问题
所以我更改了__constant float* a
-> __global const float* restrict a
,就可以了。
答案 0 :(得分:2)
NVIDIA GPU上的恒定内存大小确实很小,只有64KB(我检查了Titan Xp,GTX 960M,RTX 2080 Ti,Tesla K20c,Tesla K40m)。在AMD Radeon VII上,恒定内存大小要大得多,为14GB。在Intel CPU(i7-8700K,Xeon E5-2680 v2)上,恒定内存大小为128KB。这是驱动程序的限制,解决方法是使用global const float* restrict
(对于类型为restrict
的所有其他内核参数使用float*
)而不是constant float*
,如您所知。
如果AMD GPU的性能差异很大,则可以对AMD和NVIDIA GPU使用不同的内核声明。您可以在编译OpenCL代码之前通过#ifdef
或在运行时通过string
进行切换。这样,您不必在代码中包含整个内核两次,而只需包含内核参数声明的行即可。