【发布时间】:2016-04-02 22:29:52
【问题描述】:
我用 C 语言编写了一个 OpenCL 程序,以便利用我的 GPU 进行并行处理,但我遇到了一个问题,即在运行我的一个内核时,显示驱动程序在某些调用条件下崩溃。我创建了一个新的精简程序来演示相同的行为。
基本上,我在 GPU 上分配一个线性数组,然后启动一个内核,其中每个线程将根据其全局线程 ID 递增固定大小数组的单个不重叠“行”中的每个值。
我有一个包装这个任务的 for 循环,这会导致它重复多次 - 但是,每次重复,我都会将指向内存的指针重置为相同的起始值,因此内部循环应该执行完全相同的任务外循环的每次迭代。
奇怪的行为是,当外部循环重复 1 到 958 次时,程序运行时没有明显错误(并且输出看起来正确)。但是,如果此数字增加到 958 以上,则显示驱动程序会崩溃并恢复。奇怪的是,这不会导致 clEnqueueNDRangeKernel() 或随后的 clFinish() 返回错误。
这是有问题的内核:
__kernel void testKernel(__global unsigned int* arr)
{
// OVERRIDE ARGS
unsigned int numReps = 958;
unsigned int numRows = 1000;
unsigned int rowLength = 676;
// Make sure thread index is in-bounds
if( get_global_id(0) < numRows )
{
__global unsigned int* arrPtr;
__global unsigned int* arrInitPtr = arr + (get_global_id(0) * rowLength);
unsigned int i, j;
unsigned int tmp;
for( i = 0; i < numReps; ++i )
{
// Reset the array pointer to the first element in this thread's row
arrPtr = arrInitPtr;
for( j = 0; j < rowLength; ++j )
{
// Increment value in the row
tmp = *arrPtr;
*arrPtr = tmp + 1;
// Advance pointer to the next value
++arrPtr;
}
}
}
}
我对行数和行长进行了硬编码,以避免在参数传递中出现任何可能的错误并进一步简化操作。
我分配缓冲区(以arr 传递给内核)并将内核排入队列,如下所示:
size_t numThreads = 1000;
unsigned int rowLength = 676;
size_t arrLength = rowLength * numThreads;
cl_mem arr_d = clCreateBuffer(gpuContext, CL_MEM_READ_WRITE, arrLength * sizeof(unsigned int), NULL, &clErr);
if( clErr != CL_SUCCESS )
{
printf("Error: Failed to allocate buffer on device.\n");
exit(2);
}
clSetKernelArg(testKernel, 0, sizeof(cl_mem), &arr_d);
clErr = clEnqueueNDRangeKernel(gpuCmdQueue, testKernel, 1, NULL, &numThreads, &numThreads, 0, NULL, NULL);
我的第一直觉当然是 arrPtr 正在递增超出数组的边界 - 但是,我认为这不应该基于 for 循环条件以及当我在复制后检查内存时发生数组返回主机,数组之外的值似乎没有被修改。为清楚起见,在我的原始程序中,我预先将数组中的每个值都初始化为零,但我将其排除在此示例程序之外,因为它似乎与我的问题无关。
我很肯定对arrPtr 的内存访问在某种程度上是越界的——我看不出有任何其他方式会导致崩溃。但是,我的数组足够大,并且我在进行任何访问之前都会检查全局线程 ID,因此即使我的线程池大小太大,也不应该成为问题。
我假设失败的具体边界 (958 - 959) 是相当随意的,因为它们不直接对应于我的任何参数。添加的重复必须暴露潜在的索引问题。但是,在这种情况下,这些值是如此可重复,这很奇怪。我还尝试从各种参数中减少一个以查找非一错误,但无济于事。
作为参考,我在 Windows 7 64 位下使用 nVidia 的 64 位 OpenCL(CUDA 6.0 驱动程序)和 GeForce 770 实现。
感谢您的任何回复!我试图具体一点,但不想让它变得太长 - 如果您有任何问题或想查看我的完整 OpenCL 设置代码,请告诉我。
【问题讨论】:
-
听起来你正在经历这个:stackoverflow.com/questions/17939843/…
-
我认为你是对的 - 我在搜索中没有遇到这个问题,但这听起来是完美的解释。我不确定这里的礼仪是什么,但我很乐意接受它作为答案或将其删除为重复或你有什么。谢谢!
-
好吧,因为他现在一年左右没有回答,应该被接受,所以我创建了一个答案。