clEnqueueNDRangeKernel触发CL_INVALID_MEM_OBJECT(-38)

时间:2013-09-19 18:43:06

标签: c++ opencl nvidia

我正在使用OpenCL的C ++绑定,当我将一个内核排入队列时,我得到cl::Error -38 (CL_INVALID_MEM_OBJECT) clEnqueueNDRangeKernel

此错误未列为clEnqueueNDRangeKernel的可能错误之一。 notify函数给出了以下输出:

  

在GeForce GTX 560(设备0)上执行CL_COMMAND_NDRANGE_KERNEL时发生CL_INVALID_MEM_OBJECT错误。

我还没有找到表现出这种行为的最小例子。

调用此函数时会出现什么样的错误?

使用谷歌我只找到了这个answer。它声明如果已更新,我需要重新setKernelArg附加的内存对象。 (至少这是我对它的解释,并没有详细说明更新的含义。) 但是,我怀疑这是正确的,虽然我无法证明这一点。也许你知道这方面的官方消息来源吗?

更新

经过一些测试后,我发现向内核添加__global const float*参数引入了错误。我还发现只有在clSetKernelArg这个新参数接着另一个(已经存在的)参数后,错误才会发生。如果我在设置其他参数之前这样做,它会每隔一秒运行一次。当然这不是一个选项,因为我需要能够随时设置参数。

更新2

我注意到通过调试逐步执行代码"重新引入"我在另一个之前设置新参数的版本中的错误。 (这意味着每次都会再次出现错误。)

这可能是某种竞争条件吗?我自己不使用多线程,但在调试器中有7个线程可以来自Qt或OpenCL。

最小工作示例

#include <CL/cl.hpp>
#include <vector>
#include <iostream>

#define STRINGIFY(x) #x

std::string kernel = STRINGIFY(
__kernel void apply(__global const float *param1)
{
}
);


template <class T>
cl::Buffer genBuffer(const cl::Context &context, const std::vector<T> &data,
                        cl_mem_flags flags = CL_MEM_READ_ONLY)
{
        return cl::Buffer(context, flags | CL_MEM_COPY_HOST_PTR,
                                data.size() * sizeof(data[0]),
                                const_cast<T*>(&data[0]));
}

int main()
{
        std::vector<cl::Platform> clPlatforms;
        cl::Platform::get(&clPlatforms);
        cl_context_properties props[] = {
                CL_CONTEXT_PLATFORM, (cl_context_properties)clPlatforms[0](),
                0};
        cl::Context clContext = cl::Context(CL_DEVICE_TYPE_GPU, props);
        std::vector<cl::Device> devices = clContext.getInfo<CL_CONTEXT_DEVICES>();
        if(devices.empty())
        {
                std::cerr << "No devices found!\n";
                exit(-1);
        }
        cl::Device clDevice = devices[0];
        cl::CommandQueue clQueue = cl::CommandQueue(clContext, clDevice, 0, 0);
        cl::Program program(clContext, cl::Program::Sources(1,
                                std::make_pair(kernel.c_str(), kernel.size())));
        program.build(devices);
        cl::Kernel kernel(program, "apply");

        //this introduces the error
        kernel.setArg(0, genBuffer(clContext, std::vector<cl_float>(100));
        //the error is triggered here
        clQueue.enqueueNDRangeKernel(kernel, cl::NullRange, cl::NDRange(100), cl::NullRange);
}

1 个答案:

答案 0 :(得分:2)

问题是我将缓冲区附加到内核,假设内核会保留缓冲区。然后我销毁了导致OpenCL实现删除缓冲区的所有引用cl::Buffer / Memory对象。


通过valgrind运行我的程序后,我注意到opencl.so子程序中先前释放的对象的cl::~Buffer访问内存。阅读clSetKernelArg我注意到了:

  

用户可能不依赖内核对象来将指定为参数值的对象保留到内核中。

不确定行为显然是驾驶员进入释放的记忆区域从而进入UB土地的结果。

更正了MWE

#include <CL/cl.hpp>
#include <vector>
#include <iostream>

#define STRINGIFY(x) #x

std::string kernel = STRINGIFY(
__kernel void apply(__global const float *param1)
{
}
);


template <class T>
cl::Buffer genBuffer(const cl::Context &context, const std::vector<T> &data,
                        cl_mem_flags flags = CL_MEM_READ_ONLY)
{
        return cl::Buffer(context, flags | CL_MEM_COPY_HOST_PTR,
                                data.size() * sizeof(data[0]),
                                const_cast<T*>(&data[0]));
}

int main()
{
        std::vector<cl::Platform> clPlatforms;
        cl::Platform::get(&clPlatforms);
        cl_context_properties props[] = {
                CL_CONTEXT_PLATFORM, (cl_context_properties)clPlatforms[0](),
                0};
        cl::Context clContext = cl::Context(CL_DEVICE_TYPE_GPU, props);
        std::vector<cl::Device> devices = clContext.getInfo<CL_CONTEXT_DEVICES>();
        if(devices.empty())
        {
                std::cerr << "No devices found!\n";
                exit(-1);
        }
        cl::Device clDevice = devices[0];
        cl::CommandQueue clQueue = cl::CommandQueue(clContext, clDevice, 0, 0);
        cl::Program program(clContext, cl::Program::Sources(1,
                                std::make_pair(kernel.c_str(), kernel.size())));
        program.build(devices);
        cl::Kernel kernel(program, "apply");

        //this version triggers the error
        //kernel.setArg(0, genBuffer(clContext, std::vector<cl_float>(100));

        //This is how it is done correctly
        cl::Buffer buffer = genBuffer(clContext, std::vector<cl_float>(100));
        kernel.setArg(0, buffer);

        clQueue.enqueueNDRangeKernel(kernel, cl::NullRange, cl::NDRange(100), cl::NullRange);
}