版权声明:本文为博主原创文章,转载请注明源地址。 https://cloud.tencent.com/developer/article/1433793
我的项目中所有的kernel在程序初始化时就被编译生成了,存放在一个std::unordered_map<std::string, cl::Kernel>
类型的map表中(kernel name为key),以后程序需要调用的时候,就通过kernel name来获取指定的cl::Kernel
对象。
建这个表的时候,要创建cl::Kernel
。常用的创建cl::Kernel
的途径有两个:
opencl C++接口(cl.hpp)中的cl::Program::createKernels
成员函数封装了clCreateKernelsInProgram函数,可以返回cl::Program
中所有的cl::Kernel
对象,当一个cl::Program
中有多个kernel函数的时候,用它可以一次性得到所有的cl::Kernel
对象,挺方便的。
下面是它的源码:
cl_int createKernels(VECTOR_CLASS<Kernel>* kernels)
{
cl_uint numKernels;
cl_int err = ::clCreateKernelsInProgram(object_, 0, NULL, &numKernels);
if (err != CL_SUCCESS) {
return detail::errHandler(err, __CREATE_KERNELS_IN_PROGRAM_ERR);
}
Kernel* value = (Kernel*) alloca(numKernels * sizeof(Kernel));
err = ::clCreateKernelsInProgram(
object_, numKernels, (cl_kernel*) value, NULL);
if (err != CL_SUCCESS) {
return detail::errHandler(err, __CREATE_KERNELS_IN_PROGRAM_ERR);
}
kernels->assign(&value[0], &value[numKernels]);
return CL_SUCCESS;
}
从cl::Program
中创建cl::Kernel
还有一个方法就是使用cl::Kernel
的构造函数,指定kernel name(下面代码中的name
参数)就可以创建指定的cl::Kernel
:
inline Kernel::Kernel(const Program& program, const char* name, cl_int* err)
{
cl_int error;
object_ = ::clCreateKernel(program(), name, &error);
detail::errHandler(error, __CREATE_KERNEL_ERR);
if (err != NULL) {
*err = error;
}
}
我本来使用的是第二种方法,一切正常,昨天我想改变一下代码结构使用第一种方法来创建cl::Kernel
。但是发现了问题:
/* 通过param提供的源码创建一组cl::Kernel,并将cl::Kernel命名为name加入kernels映射表中 */
static std::unordered_map<std::string, cl::Kernel>
createKernels(const build_param& param,
const std::vector<std::string>& kernel_names) {
auto program = buildExecutableProgram(param);//编译kernel源码生成可执行的cl::Program对象
std::unordered_map<std::string, cl::Kernel> map;// name->kernel映射表
std::vector<cl::Kernel> kernels;
program.createKernels(std::addressof(kernels));//获取cl::Program中所有的cl::Kernel对象
for (auto k:kernels) {
auto name=k.getInfo<CL_KERNEL_FUNCTION_NAME>();// 调用clGetKernelInfo获取的kernel名字
cout << "name from kernel:"<<name <<" size="<<name.size()<< endl;
map.insert({ name, k });//将kernel以name为key加入map
std::string original_name = "image_scaling";// 实际的kernel name
cout << "original name:" <<original_name <<" size="<<original_name .size()<< endl;
if (original_name != name) {
cout << "not equal" << endl;
}
// 用original_name在map中查找指定的cl::Kernel
if (map.find(original_name ) == map.end()) {
cout << "not found:"<<a << endl;
}
}
return map;
}
下面是程序的输出,尼玛,它居然找不到image_scaling
!!!
其实上面的程序输出也指明了找不到的原因,original_name
与name
虽然打印出来看着是一模一样,但它们俩的长度却不一样
下面是original_name
在内存中的数据
下面是name 在内存中的数据
也就是说clGetKernelInfo
取出来的kernel name字符串比original_name
多了一个结尾’\0’…
找到原因了,解决问题办法也就有了:
在执行map.insert()
函数将cl::Kernel
加入std::unordered_map
时不能直接用
auto name=k.getInfo<CL_KERNEL_FUNCTION_NAME>()
得到的std::string
对象为key,要把name
中最后那个多出来的’\0’去掉,才是个正常的std:string
只需要修改下面这行代码:
map.insert({ name, k });//将kernel以name为key加入map
改为:
map.insert({ std::string(name.data()), k });//将kernel以name为key加入map
问题解决。
cl::Kernel::getInfo<CL_KERNEL_FUNCTION_NAME>()
获取的std::string
对象不是一个正常的std:string
,需要改造将结尾处多余的’\0’去掉,才是一个我们通常意义上的string。
其实不仅获取kernel name有这个坑,而是所有clgetXXXInfo
函数中获取的字符串类型的数据,都有这个问题。