我有一个启动OpenCL内核的循环(大约10亿次迭代)。每个内核由1个线程执行,并执行非常简单的操作。问题是在执行了几百万次迭代之后,代码冻结(停止)并且程序根本不会终止。它在对clFinish()的调用中冻结。该程序并不总是在同一次迭代中冻结。
如果clFinish()每1000次迭代调用一次而不是在每次迭代中调用,问题就会消失,所以我觉得问题是clFinish()正在等待内核的结束但kernl被杀死了(某种程度上)在调用clFinish()之前。另请注意,当我在循环内插入许多printf()调用时,问题就会消失!
我在CPU设备上执行程序时遇到问题(在我的笔记本电脑上,我使用的是AMD SDK),我在使用Nvidia Fermi GPU的机器上也遇到了问题(Nvidia SDK和驱动程序,还安装了AMD SDK)在那台机器上。)
我在每次OpenCL API调用后检查错误,但未检测到错误。我删除了错误检查以使代码清晰。
我的问题:
下面是否正确使用OpenCL API?
如果同时启动大量OpenCL内核会有任何问题吗?
代码是由我们的一些工具自动生成的,所以请不要问我为什么只用一个线程调用内核(这是另一个问题,我知道这样的代码不利于性能)。我的目标是了解代码中的问题是什么,理论上应该没有任何问题。
主机代码:
/* OpenCL initialization. */
/* ... */
cl_mem dev_acc = clCreateBuffer(context, CL_MEM_READ_WRITE, sizeof(double), NULL, &err);
for (int h0 = 1; h0 <= ni; h0 += 1)
for (int h2 = 0; h2 < nj; h2 += 1)
for (int h5 = 0; h5 < h2 - 1; h5 += 1) {
size_t global_work_size[1] = {1};
size_t block_size[1] = {1};
cl_kernel kernel2 = clCreateKernel(program, "kernel2", &err);
clSetKernelArg(kernel2, 0, sizeof(cl_mem), (void *) &dev_acc);
clEnqueueNDRangeKernel(queue, kernel2, 1, NULL, global_work_size,block_size,0, NULL, NULL);
clFinish(queue);
clReleaseKernel(kernel2);
}
内核代码:
__kernel void kernel2(__global double *acc)
{
*acc = 1;
}
汇编: gcc -O3 -lm -std = gnu99 polybench.c ocl_utilities.c symm_host.c -lOpenCL -lm -I / opt / AMDAPP / include -L / opt / AMDAPP / lib / x86_64
我正在使用Ubuntu 12.04,内核3.2.0-29-通用,X86_64,RAM:2 GB
答案 0 :(得分:4)
好吧,看看你的代码,我甚至不知道从哪里开始...
但是,如果涉及到OpenCL标准,它应该运行良好。如果您正在使用的库的实现能够处理这个问题。
您应该做的第一件事是检查每个OpenCL API调用的错误代码。我认为你“过度填充”了你的命令队列,并从OpenCL libarry得到了无助的尖叫,没有人听到。如果你使用clFinish,队列会不时地清空,可能会阻止这种“过度填充”。
其他一些事情: 单个内核真的是你想要的吗? OpenCL旨在执行SIMD架构,即单个指令多个数据。因此,当大量线程在不同数据上执行相同的代码时,OpenCL的性能最佳。
您不必每次都在循环中创建kerneal:
size_t global_work_size[1] = {1};
size_t block_size[1] = {1};
cl_kernel kernel2 = clCreateKernel(program, "kernel2", &err);
for (int h0 = 1; h0 <= ni; h0 += 1)
for (int h2 = 0; h2 < nj; h2 += 1)
for (int h5 = 0; h5 < h2 - 1; h5 += 1) {
clSetKernelArg(kernel2, 0, sizeof(cl_mem), (void *) &dev_acc);
clEnqueueNDRangeKernel(queue, kernel2, 1, NULL, global_work_size,block_size,0, NULL, NULL);
clFinish(queue);
}
clReleaseKernel(kernel2);
最后一件事是你的执行模式只有一个线程:
如果可能的话尝试类似的东西(我不知道你对记忆的要求等等):
cl_mem dev_acc = clCreateBuffer(context, CL_MEM_READ_WRITE, ni * nj * sizeof(double), NULL, &err);
size_t global_work_size[1];
global_work_size[0] = ni;
global_work_size[1] = nj;
size_t block_size[1] = {1};
cl_kernel kernel2 = clCreateKernel(program, "kernel2", &err);
// some loop
clSetKernelArg(kernel2, 0, sizeof(cl_mem), (void *) &dev_acc);
clSetKernelArg(kernel2, 1, sizeof(int), &h2);
clEnqueueNDRangeKernel(queue, kernel2, 1, NULL, global_work_size,block_size,0, NULL, NULL);
一个看起来像某个内核的内核:
__kernel void kernel2(__global double *acc, int h5)
{
int h0 = get_global_id(0);
int h2 = get_global_id(1);
int ni = get_global_size(0);
int nj = get_global_size(1);
// do stuff with ni, nj, h0, h2
if (h5 < h2)
{
*acc = 1;
}
}
答案 1 :(得分:0)
kronos有一些很好的反馈,只是为了增加更多的输入:
clEnqueueNDRangeKernel(...)
。