opencl C++接口: 关于CL_KERNEL_FUNCTION_NAME的一个坑

来源:互联网 发布:淘宝互踩 编辑:程序博客网 时间:2024/05/29 13:35

我的项目中所有的kernel在程序初始化时就被编译生成了,存放在一个std::unordered_map<std::string, cl::Kernel>类型的map表中(kernel name为key),以后程序需要调用的时候,就通过kernel name来获取指定的cl::Kernel对象。
建这个表的时候,要创建cl::Kernel。常用的创建cl::Kernel的途径有两个:

cl::Program::createKernels

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::Kernel构造函数

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;    }}

找不到kernel的问题

我本来使用的是第二种方法,一切正常,昨天我想改变一下代码结构使用第一种方法来创建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_namename虽然打印出来看着是一模一样,但它们俩的长度却不一样
下面是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函数中获取的字符串类型的数据,都有这个问题。

0 0
原创粉丝点击