【问题标题】:What about this OpenCL kernel is causing the error CL_INVALID_COMMAND_QUEUE这个 OpenCL 内核导致错误 CL_INVALID_COMMAND_QUEUE 怎么样?
【发布时间】:2012-04-17 14:16:05
【问题描述】:

我在使用 JOCL 在 Java 中的 OpenCL 中实现前馈多层感知器时遇到问题。下面是计算阶段的内核代码:

    #pragma OPENCL EXTENSION cl_khr_fp64 : enable
    __kernel void Neuron(__global const double *inputPatterns,
                           __global double *weights,
                           __global const int *numInputs,
                           __global const int *activation,
                           __global const double *bias,
                           __global const int *usingBias,
                           __global double *values,
                           __global const int *maxNumFloats,
                           __global const int *patternIndex,
                           __global const int *inputPatternSize,
                           __global const int *indexOffset,
                           __global const int *isInputNeuron,
                           __global const int *inputs)
    {
       int gid = get_global_id(0);
       double sum = 0.0;
       for(int i = 0; i < numInputs[gid+indexOffset[0]]; i++)
       {
           sum += values[inputs[(gid+indexOffset[0]) * maxNumFloats[0] + i]] *
                   weights[(gid+indexOffset[0]) * maxNumFloats[0] + i];
       }
       if(usingBias[gid+indexOffset[0]])
           sum += bias[gid+indexOffset[0]];
       if(isInputNeuron[gid+indexOffset[0]])
           sum += inputPatterns[gid+indexOffset[0]+(patternIndex[0] * inputPatternSize[0])];
       if(activation[gid+indexOffset[0]] == 1)
           sum = 1.0 / (1.0 + exp(-sum));
       values[gid + indexOffset[0]] = sum;
    }

基本上,我为网络中的每一层运行这个内核。对于第一层,没有“输入”,因此不会执行循环。然而,由于第一层是输入节点层,它确实从输入模式中添加了相关值。这执行得很好,此时我可以读回这些值。

但是,当我尝试运行第二层(它确实有输入,第一层的每个节点)时,对 clFinish() 的调用会返回错误 CL_INVALID_COMMAND_QUEUE。有时,此错误伴随着驱动程序崩溃和恢复。我已经阅读过(例如here),这可能是 TDR 超时的问题,并尝试提高限制,但不确定这是否会产生任何影响。

我正在通过对 clSetKernelArg() 的调用来检查是否有任何愚蠢的东西,但是任何人都可以在代码中发现任何明显的错误吗?由于包含 for 循环,似乎在第二层中引入了错误......如果需要,我可以澄清任何参数,但对于初始帖子来说似乎有点矫枉过正。

另外,我完全知道这段代码可能会冒犯各地有能力的编码人员,但请随意燃烧:P

编辑:主机代码:

    //Calc
    for(int k = 0; k < GPUTickList.length; k++)
    {
        clFlush(clCommandQueue);
        clFinish(clCommandQueue);
        //If input nodes
        if(k == 0)
            //Set index offset to 0
            GPUMapIndexOffset.asIntBuffer().put(0, 0);
        else
            //Update index offset
            GPUMapIndexOffset.asIntBuffer().put(0,
                GPUMapIndexOffset.asIntBuffer().get(0) + GPUTickList[k-1]);
        //Write index offset to GPU buffer
        ret = clEnqueueWriteBuffer(clCommandQueue, memObjects[12], CL_TRUE, 0,
                Sizeof.cl_int, Pointer.to(GPUMapIndexOffset.position(0)), 0, null, null);             
        //Set work size (width of layer)
        global_work_size[0] = GPUTickList[k];
        ret = clEnqueueNDRangeKernel(clCommandQueue, kernel_iterate, 1,
            global_work_offset, global_work_size, local_work_size,
            0, null, null);
    }

编辑 2:我已将完整代码上传到 pastebin

【问题讨论】:

  • 您有一部分主机代码要分享吗?如果内核为第一层神经元运行,我怀疑内核本身是否存在问题。另外,您是否尝试过 clWaitForEvents() 而不是 clFinish()?
  • 好的,添加了这个内核在循环中排队的位,如果需要可以发布更多,只是说什么。

标签: neural-network opencl


【解决方案1】:

解决了。通过使用 [0] 索引的所有内容作为直接内核参数而不是缓冲区来修复错误。显然,硬件不喜欢大量的东西一次访问缓冲区的一个特定元素。

【讨论】:

    【解决方案2】:

    我不确定您在循环上方有什么......您是否使用此循环以外的队列?以下是您可能想尝试的内容。

    //flush + finish if you need to before the loop, otherwise remove these lines
    clFlush(clCommandQueue);
    clFinish(clCommandQueue);
    
    cl_event latestEvent;
    //Calc
    for(int k = 0; k < GPUTickList.length; k++)
    {
        //If input nodes
        if(k == 0)
            //Set index offset to 0
            GPUMapIndexOffset.asIntBuffer().put(0, 0);
        else
            //Update index offset
            GPUMapIndexOffset.asIntBuffer().put(0,
                GPUMapIndexOffset.asIntBuffer().get(0) + GPUTickList[k-1]);
        //Write index offset to GPU buffer
        ret = clEnqueueWriteBuffer(clCommandQueue, memObjects[12], CL_TRUE, 0,
                Sizeof.cl_int, Pointer.to(GPUMapIndexOffset.position(0)), 0, null, null);             
        //Set work size (width of layer)
        global_work_size[0] = GPUTickList[k];
        ret = clEnqueueNDRangeKernel(clCommandQueue, kernel_iterate, 1,
            global_work_offset, global_work_size, local_work_size,
            0, null, &latestEvent);
        clWaitForEvents(1, &latestEvent);
    }
    

    【讨论】:

    • 我确实尝试过事件,但它给我带来了麻烦,所以我坚持阻塞调用和 clFinish(),我现在再试一次,看看效果如何。我也有pastebinned 整个文件,GPUTrain 函数是重要的(它有点单一)。感谢您的意见!
    • 不客气。我没有意识到你正在使用 java+ocl。我希望指针的东西仍然有效。有机会我会再看一下代码。
    • 紧跟在 clEnqueueNDRangeKernel() 调用之后的对 clWaitForEvents() 的调用在第二次传递中中断,并出现 CL_OUT_OF_RESOURCES。这一定是同一问题的症状,可能是内核正在破坏。 PS。是的,我确实在顶部提到过:P 在此实现中通过使用数组得到的指针东西,数组本质上包含 long ,它们是本机指针。
    • CL_OUT_OF_RESOURCES 表明您的第二个神经元层(+权重,偏差...)太大而无法调用内核。您是否在最小的网络大小上尝试过此内核?是否可以在每次调用之前从全局内存中释放/读取旧缓冲区?
    • 本例中的第二个神经元层,只有 10 个神经元。输入层是 35 个神经元。以前的实现确实在每次调用后读回缓冲区的内容,并为下一次传递再次设置它们,从而产生一个没有错误的更清洁的 for 循环。还要记住,如果我使用 clFinish() 而不是 clWaitForEvents(),此时错误将是 CL_INVALID_COMMAND_QUEUE。
    猜你喜欢
    • 1970-01-01
    • 2019-02-19
    • 2012-11-02
    • 2013-09-09
    • 2017-11-02
    • 1970-01-01
    • 1970-01-01
    • 2020-11-18
    • 2017-11-25
    相关资源
    最近更新 更多