【问题标题】:Why does repeating the exact same task within an OpenCL kernel more times cause it to crash?为什么在 OpenCL 内核中多次重复完全相同的任务会导致它崩溃?
【发布时间】: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/…
  • 我认为你是对的 - 我在搜索中没有遇到这个问题,但这听起来是完美的解释。我不确定这里的礼仪是什么,但我很乐意接受它作为答案或将其删除为重复或你有什么。谢谢!
  • 好吧,因为他现在一年左右没有回答,应该被接受,所以我创建了一个答案。

标签: c arrays opencl


【解决方案1】:

我知道它很旧,但无论如何......来自评论:

Windows 有一个看门狗计时器机制,如果显示驱动程序似乎变得无响应,它会重新启动它。我发现如果我的内核运行超过几秒钟,计时器就会跳闸并重新启动显示驱动程序。我所知道的唯一解决方案是将内核执行分解为一到两秒的片段,然后按顺序运行它们。 (我有点这个错误,所以这似乎仍然是真的)

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2019-01-02
    • 1970-01-01
    • 2021-06-28
    • 1970-01-01
    • 2015-03-03
    • 2019-03-11
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多