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

没有检索到摘要

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

我的项目中所有的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函数中获取的字符串类型的数据,都有这个问题。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

10km

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值