我正在使用OpenCL的C绑定,当我将一个内核排入队列时,我得到一个cl :: Error,它表示clEnqueueNDRangeKernel的-38(CL_INVALID_MEM_OBJECT).
此错误未列为clEnqueueNDRangeKernel的可能错误之一.
notify函数给出了以下输出:
CL_INVALID_MEM_OBJECT error executing CL_COMMAND_NDRANGE_KERNEL on GeForce GTX 560 (Device 0).
我还没有找到展示这种行为的最小例子.
调用此函数时会出现什么样的错误?
使用谷歌我只找到了这个answer.它声明如果已更新,我需要resetKernelArg一个附加的内存对象. (至少这是我对它的解释,并没有详细说明更新的含义.)
但是,我怀疑这是正确的,虽然我无法证明这一点.也许你知道这方面的官方消息来源?
更新
经过一些测试后,我发现向内核添加__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);
}
最佳答案 问题是我将缓冲区附加到内核,假设内核会保留缓冲区.然后我破坏了所有引用cl :: Buffer / Memory对象,导致OpenCL实现删除缓冲区.
在通过valgrind运行我的程序之后,我注意到opencl.so访问了一个先前在cl :: ~Buffer子例程中释放的对象的内存.在clSetKernelArg
读到我注意到:
Users may not rely on a kernel object to retain objects specified as argument values to the kernel.
不确定行为显然是驾驶员进入自由存储区域从而进入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);
}