OpenCL调用clCreateKernel返回-45错误,请求问题排查方案
提问:OpenCL错误clCreateKernel (-45)排查
我使用C++结合OpenCL库开发了主机端程序与OpenCL Kernel,尽管花费大量时间排查,仍持续遇到错误:OpenCL error: clCreateKernel (-45)。为排除硬件问题,我测试了示例项目,该项目仅修改一处即可正常运行。算法本身已验证可正常工作,附上我的代码如下:
Kernel代码
const char* kernel2Greedy = R"( __kernel void parallelGreedy2(__global long* denominators, int numDenominators, double numberToExchange, __global char* result) { for (int i = 0; i < numDenominators - 1; i++) { for (int j = i + 1; j < numDenominators; j++) { if (denominators[i] < denominators[j]) { long temp = denominators[i]; denominators[i] = denominators[j]; denominators[j] = temp; } } } result[0] = '\0'; for (int i = 0; i < numDenominators && numberToExchange > 0; i++) { if (numberToExchange >= denominators[i]) { int biggestNumberToExchangeInLoop = (int)(numberToExchange / denominators[i]); char temp[100]; snprintf(temp, sizeof(temp), "%ld cash x%d\n", denominators[i], biggestNumberToExchangeInLoop); int offset = 0; while (result[offset] != '\0') { offset++; } for (int j = 0; temp[j] != '\0'; j++) { result[offset++] = temp[j]; } result[offset] = '\0'; numberToExchange = round(100 * (numberToExchange - (biggestNumberToExchangeInLoop * denominators[i]))) / 100.0; } } } )";
主机端代码
int main() { std::cout << "Type a number of instances you want to create:" << std::endl; int numberOfInstances2; std::cin >> numberOfInstances2; std::vector<long> denominators; std::cout << "How many numbers you would like to add?" << std::endl; int numberToAdd; std::cin >> numberToAdd; for (int i = 0; i < numberToAdd; i++) { std::cout << "Add next number:" << std::endl; long nextNumber; std::cin >> nextNumber; denominators.push_back(nextNumber); } // Setup OpenCL try { // device containers cl::Buffer bufferDenominators; cl::Buffer bufferResult; cl::Buffer bufferNumberToExchange; cl::Buffer bufferNumberOfDenominators; // create a context cl::Context context(CL_DEVICE_TYPE_GPU); // create a program cl::Program program(context, kernel2Greedy); // get the command queue cl::CommandQueue queue(context); // create the kernel cl::Kernel kernel(program, "parallelGreedy2"); // copy the data to the device bufferDenominators = cl::Buffer(context, CL_MEM_READ_ONLY | CL_MEM_COPY_HOST_PTR, sizeof(long) * denominators.size(), denominators.data()); bufferResult = cl::Buffer(context, CL_MEM_WRITE_ONLY, sizeof(char) * 1000 * numberOfInstances2); bufferNumberToExchange = cl::Buffer(context, CL_MEM_READ_ONLY | CL_MEM_COPY_HOST_PTR, sizeof(double), &numberToAdd); bufferNumberOfDenominators = cl::Buffer(context, CL_MEM_READ_ONLY | CL_MEM_COPY_HOST_PTR, sizeof(int), &numberToAdd); // set kernel arguments kernel.setArg(0, bufferDenominators); kernel.setArg(1, bufferNumberOfDenominators); kernel.setArg(2, bufferNumberToExchange); kernel.setArg(3, bufferResult); // run the kernel queue.enqueueNDRangeKernel(kernel, cl::NullRange, cl::NDRange(numberOfInstances2), cl::NullRange); queue.finish(); // copy the data back std::vector<char> result(numberOfInstances2 * 1000); queue.enqueueReadBuffer(bufferResult, CL_TRUE, 0, sizeof(char) * 1000 * numberOfInstances2, result.data()); // Print results for (int i = 0; i < numberOfInstances2; ++i) { std::cout << "Result for instance " << i << ":" << std::endl; std::cout << std::string(result.begin() + (i * 1000), result.begin() + ((i + 1) * 1000)) << std::endl; } } catch (cl::Error& e) { std::cerr << "OpenCL error: " << e.what() << " (" << e.err() << ")" << std::endl; return 1; } return 0; }
请问我遗漏了什么导致该错误?
回答
clCreateKernel (-45)对应OpenCL错误码CL_INVALID_KERNEL_NAME,核心原因是你的OpenCL程序未执行编译步骤,同时代码存在多处逻辑和参数错误,具体解决如下:
1. 修复直接触发错误的问题:添加程序编译步骤
创建cl::Program对象后必须调用build()编译,否则程序未生成可执行代码,无法找到内核函数。修改主机端代码:
// create a program cl::Program program(context, kernel2Greedy); // 新增编译逻辑,同时捕获编译日志便于调试 cl_int buildStatus = program.build(); if (buildStatus != CL_SUCCESS) { std::vector<cl::Device> devices = context.getInfo<CL_CONTEXT_DEVICES>(); std::string buildLog = program.getBuildInfo<CL_PROGRAM_BUILD_LOG>(devices[0]); std::cerr << "编译日志:\n" << buildLog << std::endl; throw cl::Error(buildStatus, "程序编译失败"); }
2. 内核代码的关键问题
2.1 未支持的标准库函数
OpenCL C不兼容全部C标准库函数:
snprintf不是OpenCL内置函数,无法直接使用,可改用手动字符串拼接,或使用设备端打印函数(但不适合写入结果缓冲区)。round函数需要启用cl_khr_fp64扩展(针对双精度),或替换为OpenCL内置的rint函数,同时要确认你的GPU支持双精度计算。
2.2 数据竞争与权限错误
- 你将
denominators声明为__global全局数组,每个工作项都会对其排序,并行执行时会导致严重数据竞争,结果完全不可控。建议在主机端提前排好序,避免内核中修改共享数据;若必须在核内排序,需为每个工作项分配独立的分母数组。 - 主机端创建
bufferDenominators时使用了CL_MEM_READ_ONLY,但内核中修改了数组内容(排序交换),这会触发未定义行为,需改为CL_MEM_READ_WRITE。
3. 主机端参数传递错误
bufferNumberToExchange传递的是&numberToAdd(分母的数量),但内核期望的是要兑换的金额。需新增用户输入兑换金额的逻辑,并修正参数传递:
// 新增输入兑换金额 double numberToExchange; std::cout << "请输入要兑换的金额:" << std::endl; std::cin >> numberToExchange; // 修正缓冲区创建 bufferNumberToExchange = cl::Buffer(context, CL_MEM_READ_ONLY | CL_MEM_COPY_HOST_PTR, sizeof(double), &numberToExchange);
4. 并行逻辑错误
你启动了numberOfInstances2个工作项,但所有工作项都写入result缓冲区的起始位置,会导致内容混乱。需让每个工作项使用独立的缓冲区区域,修改内核代码:
__kernel void parallelGreedy2(__global long* denominators, int numDenominators, double numberToExchange, __global char* result) { // 获取当前工作项ID,计算独立的结果偏移 int globalId = get_global_id(0); char* instanceResult = result + globalId * 1000; // 后续所有对result的操作替换为instanceResult instanceResult[0] = '\0'; // ... 其余排序和计算逻辑 ... // 写入结果时使用instanceResult for (int j = 0; temp[j] != '\0'; j++) { instanceResult[offset++] = temp[j]; } instanceResult[offset] = '\0'; }
总结
先添加程序编译步骤并查看编译日志,这是解决clCreateKernel (-45)的直接方法,再逐步修复内核逻辑、参数传递和并行安全问题。
内容的提问来源于stack exchange,提问作者Dekrate
相关产品推荐
相关产品推荐

