c - 当在一个循环中启动大量内核时,OpenCL 程序会卡住

标签 c opencl nvidia

我有一个启动 OpenCL 内核的循环(大约 10 亿次迭代)。每个内核由 1 个线程执行,并执行非常简单的操作。问题在于,在执行几百万次迭代后,代码卡住(停止)并且程序根本不会终止。它在调用 clFinish() 时卡住。程序并不总是在同一次迭代中卡住。

如果每 1000 次迭代调用一次 clFinish() 而不是每次迭代都调用一次,问题就会消失,所以我感觉问题是 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-generic,X86_64,内存:2 GB

最佳答案

好吧,看着你的代码我什至不知道从哪里开始......

但是,如果涉及到 OpenCL 标准,它应该可以正常运行。如果您使用的库的实现能够处理这个问题。

您应该做的第一件事是检查每个 OpenCL API 调用的错误代码。我认为你正在“填满”你的命令队列,并从 OpenCL 库中得到无人听到的无声帮助尖叫。如果您使用 clFinish,队列会不时被清空,这可能会阻止这种“溢出”。

一些其他的东西: 一个内核真的是你想要的吗? OpenCL 旨在在 SIMD 架构上执行,这意味着单指令多数据。因此,当大量线程对不同数据执行相同代码时,OpenCL 性能最佳。

您不必在循环中每次都创建内核:

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;
   }
}

关于c - 当在一个循环中启动大量内核时,OpenCL 程序会卡住,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/19248369/

相关文章:

cuda - 对齐图像卷积 (OpenCL/CUDA) 内核的 GPU 内存访问

具有通用数据类型的 OpenCL 内核

opencl - 是否有任何OpenCL主机具有多个平台?

c++ - CUDA + Visual Studio 2013

c - 在linux内核编译中将.o文件放在单独的文件夹中

c - while 循环不会执行

c - RegEnumValue 和 REG_MULTI_SZ 类型

使用参数调用 obj-c 方法

python - 需要帮助将 cuDF Dataframe 转换为 cupy ndarray

c++ - 为什么我的程序在 nVidia NView 下占用 100% CPU?