OpenCL入门示例clFinish调用阻塞无返回问题求助
OpenCL Hello World 程序阻塞问题排查
问题描述
刚学习OpenCL,尝试用Hello World示例验证环境,但程序在调用clFinish时阻塞无法返回。设备是Ryzen 5 3400G,系统为Linux,gcc版本10.2.1-6。
原代码
#include <math.h> #ifdef __APPLE__ #include <OpenCL/opencl.h> #else #include <CL/cl.h> #endif const char *KernelSource = "\n" \ "__kernel void hello( \n" \ " __global char* a, \n" \ " __global char* b, \n" \ " __global char* c, \n" \ " const unsigned int count) \n" \ "{ \n" \ " int i = get_global_id(0); \n" \ " if(i < count) \n" \ " c[i] = a[i] + b[i]; \n" \ "} \n" \ "\n"; #define DATA_SIZE (16) int main(int argc, char** argv) { int err; // error code returned from api calls cl_device_id device_id; // compute device id cl_context context; // compute context cl_command_queue commands; // compute command queue cl_program program; // compute program cl_kernel kernel; // compute kernel cl_mem input; // device memory used for the input array cl_mem input2; // device memory used for the input array cl_mem output; // device memory used for the output array size_t global; // global domain size for our calculation size_t local; // local domain size for our calculation int i; unsigned int count = DATA_SIZE; // Input data char a[DATA_SIZE] = "Hello \0\0\0\0\0\0"; char b[DATA_SIZE] = {15, 10, 6, 0, -11, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; // Output data char c[DATA_SIZE]; cl_platform_id platform; unsigned int no_plat; err = clGetPlatformIDs(1,&platform,&no_plat); // Where to run err = clGetDeviceIDs(platform, CL_DEVICE_TYPE_GPU, 1, &device_id, NULL); if (err != CL_SUCCESS) return -1; context = clCreateContext(0, 1, &device_id, NULL, NULL, &err); if (!context) return -1; commands = clCreateCommandQueue(context, device_id, 0, &err); if (!commands) return -1; // What to run program = clCreateProgramWithSource(context, 1, (const char **) & KernelSource, NULL, &err); if (!program) return -1; err = clBuildProgram(program, 0, NULL, NULL, NULL, NULL); if (err != CL_SUCCESS) return -1; kernel = clCreateKernel(program, "hello", &err); if (!kernel || err != CL_SUCCESS) return -1; // Create space for data and copy a and b to device (note that we could also use clEnqueueWriteBuffer to upload) input = clCreateBuffer(context, CL_MEM_READ_ONLY | CL_MEM_USE_HOST_PTR, sizeof(char) * DATA_SIZE, a, NULL); input2 = clCreateBuffer(context, CL_MEM_READ_ONLY | CL_MEM_USE_HOST_PTR, sizeof(char) * DATA_SIZE, b, NULL); output = clCreateBuffer(context, CL_MEM_WRITE_ONLY, sizeof(char) * DATA_SIZE, NULL, NULL); if (!input || !output) return -1; // Send data err = clSetKernelArg(kernel, 0, sizeof(cl_mem), &input); err |= clSetKernelArg(kernel, 1, sizeof(cl_mem), &input2); err |= clSetKernelArg(kernel, 2, sizeof(cl_mem), &output); err |= clSetKernelArg(kernel, 3, sizeof(unsigned int), &count); if (err != CL_SUCCESS) return -1; local = DATA_SIZE; // Run kernel! global = DATA_SIZE; // count; err = clEnqueueNDRangeKernel(commands, kernel, 1, NULL, &global, &local, 0, NULL, NULL); if (err != CL_SUCCESS) return -1; clFinish(commands); // Read result err = clEnqueueReadBuffer( commands, output, CL_TRUE, 0, sizeof(char) * count, c, 0, NULL, NULL ); if (err != CL_SUCCESS) return -1; printf("%s\n", c); // Clean up clReleaseMemObject(input); clReleaseMemObject(output); clReleaseProgram(program); clReleaseKernel(kernel); clReleaseCommandQueue(commands); clReleaseContext(context); sleep(1); // Leopard pty bug workaround. return 0; }
编译警告
from world.c:11: /usr/include/CL/cl_version.h:22:9: note: ‘#pragma message: cl_version.h: CL_TARGET_OPENCL_VERSION is not defined. Defaulting to 300 (OpenCL 3.0)’ 22 | #pragma message("cl_version.h: CL_TARGET_OPENCL_VERSION is not defined. Defaulting to 300 (OpenCL 3.0)") | ^~~~~~~ world.c: In function ‘main’: world.c:61:2: warning: ‘clCreateCommandQueue’ is deprecated [-Wdeprecated-declarations] 61 | commands = clCreateCommandQueue(context, device_id, 0, &err); | ^~~~~~~~ In file included from world.c:11: /usr/include/CL/cl.h:1906:1: note: declared here 1906 | clCreateCommandQueue(cl_context context, | ^~~~~~~~~~~~~~~~~~~~
clinfo输出
Number of platforms 1 Platform Name Clover Platform Vendor Mesa Platform Version OpenCL 1.1 Mesa 20.3.5 Platform Profile FULL_PROFILE Platform Extensions cl_khr_icd Platform Extensions function suffix MESA Platform Name Clover Number of devices 1 Device Name AMD Radeon(TM) Vega 11 Graphics (RAVEN, DRM 3.40.0, 4.19.0-12-amd64, LLVM 11.0.1) Device Vendor AMD Device Vendor ID 0x1002 Device Version OpenCL 1.1 Mesa 20.3.5 Driver Version 20.3.5 Device OpenCL C Version OpenCL C 1.1 Device Type GPU Device Profile FULL_PROFILE Device Available Yes Compiler Available Yes Max compute units 11 Max clock frequency 1400MHz Max work item dimensions 3 Max work item sizes 256x256x256 Max work group size 256 Preferred work group size multiple (kernel) 64 Preferred / native vector sizes char 16 / 16 short 8 / 8 int 4 / 4 long 2 / 2 half 0 / 0 (n/a) float 4 / 4 double 2 / 2 (cl_khr_fp64) Half-precision Floating-point support (n/a) Single-precision Floating-point support (core) Denormals No Infinity and NANs Yes Round to nearest Yes Round to zero No Round to infinity No IEEE754-2008 fused multiply-add No Support is emulated in software No Correctly-rounded divide and sqrt operations No Double-precision Floating-point support (cl_khr_fp64) Denormals Yes Infinity and NANs Yes Round to nearest Yes Round to zero Yes Round to infinity Yes IEEE754-2008 fused multiply-add Yes Support is emulated in software No Address bits 64, Little-Endian Global memory size 31611129856 (29.44GiB) Error Correction support No Max memory allocation 22127790899 (20.61GiB) Unified memory for Host and Device No Minimum alignment for any data type 128 bytes Alignment of base address 32768 bits (4096 bytes) Global Memory cache type None Image support No Local memory type Local Local memory size 32768 (32KiB) Max number of constant args 16 Max constant buffer size 67108864 (64MiB) Max size of kernel argument 1024 Queue properties Out-of-order execution No Profiling Yes Profiling timer resolution 0ns Execution capabilities Run OpenCL kernels Yes Run native kernels No Device Extensions cl_khr_byte_addressable_store cl_khr_global_int32_base_atomics cl_khr_global_int32_extended_atomics cl_khr_local_int32_base_atomics cl_khr_local_int32_extended_atomics cl_khr_int64_base_atomics cl_khr_int64_extended_atomics cl_khr_fp64 NULL platform behavior clGetPlatformInfo(NULL, CL_PLATFORM_NAME, ...) No platform clGetDeviceIDs(NULL, CL_DEVICE_TYPE_ALL, ...) No platform clCreateContext(NULL, ...) [default] No platform clCreateContext(NULL, ...) [other] Success [MESA] clCreateContextFromType(NULL, CL_DEVICE_TYPE_DEFAULT) Success (1) Platform Name Clover Device Name AMD Radeon(TM) Vega 11 Graphics (RAVEN, DRM 3.40.0, 4.19.0-12-amd64, LLVM 11.0.1) clCreateContextFromType(NULL, CL_DEVICE_TYPE_CPU) No devices found in platform clCreateContextFromType(NULL, CL_DEVICE_TYPE_GPU) Success (1) Platform Name Clover Device Name AMD Radeon(TM) Vega 11 Graphics (RAVEN, DRM 3.40.0, 4.19.0-12-amd64, LLVM 11.0.1) clCreateContextFromType(NULL, CL_DEVICE_TYPE_ACCELERATOR) No devices found in platform clCreateContextFromType(NULL, CL_DEVICE_TYPE_CUSTOM) No devices found in platform clCreateContextFromType(NULL, CL_DEVICE_TYPE_ALL) Success (1) Platform Name Clover Device Name AMD Radeon(TM) Vega 11 Graphics (RAVEN, DRM 3.40.0, 4.19.0-12-amd64, LLVM 11.0.1)
问题原因分析
- 本地工作组大小不兼容:从clinfo输出看,设备的
Preferred work group size multiple (kernel)是64,代码中设置local = 16,虽然小于最大工作组大小256,但Clover平台对非推荐倍数的工作组大小兼容性较差,导致内核无法调度执行,进而阻塞clFinish。 - API版本不匹配:默认使用OpenCL 3.0,但设备仅支持OpenCL 1.1,部分API调用存在兼容性问题。
- 使用废弃API:
clCreateCommandQueue是OpenCL 1.2及以后废弃的接口,在旧版本平台上可能引发异常。 - 错误检查不完整:部分API调用未检查错误,无法定位早期问题。
修改后的可运行代码
#include <math.h> #include <stdio.h> #include <stdlib.h> #ifdef __APPLE__ #include <OpenCL/opencl.h> #else #include <CL/cl.h> #endif // 明确指定目标OpenCL版本为1.1,匹配设备支持版本 #define CL_TARGET_OPENCL_VERSION 110 const char *KernelSource = "\n" \ "__kernel void hello( \n" \ " __global char* a, \n" \ " __global char* b, \n" \ " __global char* c, \n" \ " const unsigned int count) \n" \ "{ \n" \ " int i = get_global_id(0); \n" \ " if(i < count) \n" \ " c[i] = a[i] + b[i]; \n" \ "} \n" \ "\n"; #define DATA_SIZE (16) // 辅助函数:打印OpenCL错误信息 void print_cl_error(const char* func_name, cl_int err) { fprintf(stderr, "Error in %s: %d\n", func_name, err); } int main(int argc, char** argv) { cl_int err; cl_device_id device_id; cl_context context; cl_command_queue commands; cl_program program; cl_kernel kernel; cl_mem input, input2, output; size_t global, local; unsigned int count = DATA_SIZE; char a[DATA_SIZE] = "Hello \0\0\0\0\0\0"; char b[DATA_SIZE] = {15, 10, 6, 0, -11, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; char c[DATA_SIZE]; cl_platform_id platform; unsigned int no_plat; err = clGetPlatformIDs(1, &platform, &no_plat); if (err != CL_SUCCESS) { print_cl_error("clGetPlatformIDs", err); return -1; } err = clGetDeviceIDs(platform, CL_DEVICE_TYPE_GPU, 1, &device_id, NULL); if (err != CL_SUCCESS) { print_cl_error("clGetDeviceIDs", err); return -1; } context = clCreateContext(0, 1, &device_id, NULL, NULL, &err); if (!context || err != CL_SUCCESS) { print_cl_error("clCreateContext", err); return -1; } // 使用新版API创建命令队列 cl_queue_properties props[] = {CL_QUEUE_PROPERTIES, CL_QUEUE_PROFILING_ENABLE, 0}; commands = clCreateCommandQueueWithProperties(context, device_id, props, &err); if (!commands || err != CL_SUCCESS) { print_cl_error("clCreateCommandQueueWithProperties", err); clReleaseContext(context); return -1; } program = clCreateProgramWithSource(context, 1, (const char **)&KernelSource, NULL, &err); if (!program || err != CL_SUCCESS) { print_cl_error("clCreateProgramWithSource", err); clReleaseCommandQueue(commands); clReleaseContext(context); return -1; } err = clBuildProgram(program, 0, NULL, NULL, NULL, NULL); if (err != CL_SUCCESS) { // 打印编译错误日志 size_t log_size; clGetProgramBuildInfo(program, device_id, CL_PROGRAM_BUILD_LOG, 0, NULL, &log_size); char* log = (char*)malloc(log_size); clGetProgramBuildInfo(program, device_id, CL_PROGRAM_BUILD_LOG, log_size, log, NULL); fprintf(stderr, "Program build log:\n%s\n", log); free(log); print_cl_error("clBuildProgram", err); clReleaseProgram(program); clReleaseCommandQueue(commands); clReleaseContext(context); return -1; } kernel = clCreateKernel(program, "hello", &err); if (!kernel || err != CL_SUCCESS) { print_cl_error("clCreateKernel", err); clReleaseProgram(program); clReleaseCommandQueue(commands); clReleaseContext(context); return -1; } input = clCreateBuffer(context, CL_MEM_READ_ONLY | CL_MEM_USE_HOST_PTR, sizeof(char)*DATA_SIZE, a, &err); if (err != CL_SUCCESS) { print_cl_error("clCreateBuffer(input)", err); goto cleanup; } input2 = clCreateBuffer(context, CL
相关产品推荐
相关产品推荐

