OpenCL(贰):浅析CL内核程序接口函数
目录
1.前言
2.获取平台信息
1.cl_int类型
2.cl_platform_id类型
3.clGetPlatformIDs():查询系统OpenCL平台数量或获取具体的平台信息
4.clGetPlatformInfo():查询指定OpenCL平台的信息,例如平台名称、供应商、版本等
3.设置OpenCL上下文Context属性
1.cl_context_properties类型
4.创建上下文
1.cl_context类型
2.clCreateContextFromType():用于根据设备类型创建上下文的函数
5.寻找OpenCL设备(GPU)
1.cl_device_id类型
2.clGetContextInfo():用于查询上下文cl_context的相关信息
6.创建CL内核函数
7.装载内核程序
1.cl_program类型
2.clCreateProgramWithSource():用于创建一个程序对象,该程序对象包含OpenCL C源代码
8.生成CL程序并获取实例句柄
1.cl_kernel类型
2.clBuildProgram():用于编译OpenCL程序对象,生成针对目标设备的执行代码
9.创建命令队列和缓冲区
1.cl_command_queue类型
2.cl_mem类型
3.clCreateCommandQueue():创建一个命令队列,用于在指定的 OpenCL 设备上执行命令
4.clCreateBuffer():创建一个 OpenCL 缓冲区内存对象,用于在 OpenCL 设备上存储数据
10.将参数传入内核程序并传入命令队列
1.clSetKernelArg(): 用于为 OpenCL 内核的特定参数设置参数值。在执行内核之前,必须为内核的所有参数设置值
2.clEnqueueNDRangeKernel() :用于将内核执行命令排队到命令队列中
11.执行命令队列并保存至主机
1.clFinish() :用于确保命令队列中的所有命令都已执行完毕。会阻塞主机线程,直到命令队列中的所有命令都完成执行
2.clEnqueueReadBuffer():用于将数据从设备的缓冲区读取到主机内存中。它可以是阻塞或非阻塞的,具体取决于blocking_read参数
12.释放资源
1.clReleaseKernel():减少指定内核对象的引用计数。当引用计数降为零时,内核对象将被释放
2.clReleaseProgram():减少指定程序对象的引用计数。当引用计数降为零时,程序对象及其构建的对象(如内核)将被释放
3.clReleaseMemObject():减少指定内存对象的引用计数。当引用计数降为零时,内存对象将被释放
4.clReleaseCommandQueue():减少指定命令队列的引用计数。当引用计数降为零时,命令队列将被释放
5.clReleaseContext():减少指定上下文对象的引用计数。当引用计数降为零时,上下文及其关联的所有对象(如命令队列、内存对象、程序对象和内核对象)都将被释放
前言
紧接文章《OpenCL(壹):了解OpenCL模型到编写第一个CL内核程序》的内容,本篇文章将针对上一篇提到的第一个CL内核程序中的函数接口进行刨析,解释每一个接口文档的作用。当然这或许比不上官方的文档,但是详细的解析有利于后续开发的翻阅,只有做好归纳总结才能为己所用,在开发中遇到的问题也会迎刃而解。附官方文档链接:
OpenCL2.1接口https://registry.khronos.org/OpenCL/sdk/2.1/docs/man/xhtml/OpenCL2.1接口.pdf
https://registry.khronos.org/OpenCL/sdk/2.1/docs/OpenCL-2.1-refcard.pdf
获取平台信息
根据前文的程序,我们在main函数中第一步操作是获取平台的详细,代码如下:
cl_int status = 0; //记录函数状态
size_t deviceListSize; //记录设备个数
cl_uint numPlatforms; //可用平台数量
cl_platform_id platform = NULL; //存储平台ID
status = clGetPlatformIDs(0, NULL, &numPlatforms); //获取平台个数
if (status != CL_SUCCESS) { std::cout << "错误:获取设备失败" << std::endl;return EXIT_FAILURE;
}if (numPlatforms > 0) { //个数大于0则便利平台信息cl_platform_id* platforms = (cl_platform_id*)malloc(numPlatforms * sizeof(cl_platform_id)); //创建内存存储平台信息status = clGetPlatformIDs(numPlatforms, platforms, NULL); //获取平台IDif (status != CL_SUCCESS) {std::cout << "错误:获取平台ID失败" << std::endl;return -1;}for (unsigned int i = 0; i < numPlatforms; ++i) { //遍历平台列表获取平台信息char pbuff[100];status = clGetPlatformInfo(platforms[i], CL_PLATFORM_VENDOR, sizeof(pbuff), pbuff, NULL); //获取平台信息platform = platforms[i];if (!strcmp(pbuff, "GPU")) { break;}}delete platforms;
}
在这段代码中分别提到了几种OpenCL中的类型和函数,以下是具体的解释:
类型名称 | 含义 | 64位平台大小 |
cl_int类型 | 一个 32 位的有符号整数,用于返回状态码或作为参数的整型变量 | 4字节 |
cl_platform_id类型 | 句柄类型,用于标识一个特定的OpenCL平台 | 8字节 |
表1.OpenCL中的特定类型
clGetPlatformIDs():查询系统OpenCL平台数量或获取具体的平台信息
cl_int clGetPlatformIDs(cl_uint num_entries, //指定存储的平台数量的对象cl_platform_id *platforms, //指定存储平台ID的对象cl_uint *num_platforms //指定存储返回平台总数的对象
);返回值类型:cl_int1.CL_SUCCESS:函数成功执行。2.CL_INVALID_VALUE:传递的参数值无效。例如num_entries为0而platforms不为NULL3.CL_OUT_OF_HOST_MEMORY:主机内存不足,无法完成操作
clGetPlatformInfo():查询指定OpenCL平台的信息,例如平台名称、供应商、版本等
cl_int clGetPlatformInfo(cl_platform_id platform, //指定要查询的OpenCL平台cl_platform_info param_name, //指定要查询的平台属性size_t param_value_size, //指定param_value缓存的大小void *param_value, //指向用于存储返回值的内存地址size_t *param_value_size_ret //存储返回实际写入param_value的字节数
);返回值类型:cl_int1.CL_SUCCESS:函数成功执行。2.CL_INVALID_VALUE:传递的参数值无效。例如num_entries为0而platforms不为NULL3.CL_OUT_OF_HOST_MEMORY:主机内存不足,无法完成操作
针对该接口函数中存在的cl_platform_info类型作以下补充:
枚举值 | 含义 |
CL_PLATFORM_PROFILE | 返回平台的配置文件 |
CL_PLATFORM_VERSION | 返回平台的OpenCL版本 |
CL_PLATFORM_NAME | 返回台的名称 |
CL_PLATFORM_VENDOR | 返回平台的供应商名称 |
CL_PLATFORM_EXTENSIONS | 返回平台支持的扩展列表 |
CL_PLATFORM_HOST_TIMER_RESOLUTION | 返回主机定时器的分辨率/纳秒(2.1版本) |
CL_PLATFORM_NUMERIC_VERSION | 返回平台的OpenCL版本号(3.1版本) |
CL_PLATFORM_EXTENSIONS_WITH_VERSION | 返回支持的扩展及其版本(3.0版本) |
表2.l_platform_info类型的补充
设置OpenCL上下文Context属性
后续的小节都会根据编写的OpenCL程序代码进行讲解,就不再对其进行赘述
cl_context_properties cps[3] = {CL_CONTEXT_PLATFORM, (cl_context_properties)platform, 0 };
cl_context_properties* cprops = (NULL == platform) ? NULL : cps;
//Tip:此处的NULL == platform写法可以预防错误的代码编写导致赋值操作
类型名称 | 含义 | 64位平台大小 |
cl_context_properties类型 | 上下文属性类型,用于在创建上下文时配置额外的信息 | 8字节 |
表3.OpenCL中的cl_context_properties类型
补充以下针对cl_context_properties中的枚举类描述:
枚举量 | 含义 |
CL_CONTEXT_PLATFORM | 指定上下文关联的平台 |
CL_GL_CONTEXT_KHR | 与OpenGL上下文共享的OpenGL上下文句柄 |
CL_EGL_DISPLAY_KHR | 指定 EGL 显示的句柄,用于OpenCL和EGL的互操作 |
CL_CONTEXT_INTEROP_USER_SYNC | 用于设置用户同步标志,指定 OpenCL 和其他图形 API 是否需要手动同步(布尔值) |
表4.cl_context_properties中的枚举类描述
创建上下文
cl_context context = clCreateContextFromType(cprops, CL_DEVICE_TYPE_GPU, NULL, NULL, &status);
if (status != CL_SUCCESS) {std::cout << "错误:为GPU生成上下文失败" << std::endl;return EXIT_FAILURE;
}
针对此段代码中提到类型cl_context做以下解释:
类型名称 | 含义 | 64位平台大小 |
cl_context类型 | OpenCL中上下文类型,用于管理设备、内存、程序和命令队列等资源 | 8字节 |
表5.cl_context类型的补充
clCreateContextFromType():用于根据设备类型创建上下文的函数
cl_context clCreateContextFromType(const cl_context_properties *properties, //指定上下文的属性列表,通常用于指定平台cl_device_type device_type, //指定要包含的设备类型void (CL_CALLBACK *pfn_notify)(const char *errinfo, const void *private_info, size_t cb, void *user_data), //错误通知回调函数,当上下文在运行时发生错误时调用void *user_data, //传递给回调函数的用户数据,可用于上下文特定的操作cl_int *errcode_ret //返回错误代码的指针。如果为NULL,错误代码将被忽略
);返回值:cl_context
成功时返回创建的OpenCL上下文,失败时返回 NULL
在函数中存在一个cl_device_type类型,具体解释如下:
枚举值 | 含义 |
CL_DEVICE_TYPE_DEFAULT | 默认设备类型(由平台决定) |
CL_DEVICE_TYPE_CPU | CPU设备 |
CL_DEVICE_TYPE_GPU | GPU设备 |
CL_DEVICE_TYPE_ACCELERATOR | 专用加速器设备 |
CL_DEVICE_TYPE_CUSTOM | 自定义设备 |
CL_DEVICE_TYPE_ALL | 所有可用设备 |
表6.cl_device_type类型
寻找OpenCL设备(GPU)
//获取设备个数
status = clGetContextInfo(context, CL_CONTEXT_DEVICES, 0, NULL, &deviceListSize);
if (status != CL_SUCCESS) {std::cout << "错误:寻找可用的设备个数失败" << std::endl;return EXIT_FAILURE;
}
cl_device_id* devices = (cl_device_id*)malloc(deviceListSize);
if (devices == 0) {std::cout << "错误:当前可用设备数为0" << std::endl;return EXIT_FAILURE;
}//获取设备列表
status = clGetContextInfo(context, CL_CONTEXT_DEVICES, deviceListSize, devices, NULL);
if (status != CL_SUCCESS) {std::cout << "错误:获取设备信息失败" << std::endl;return EXIT_FAILURE;
}
针对代码中的cl_device_id类型作以下解释:
类型名称 | 含义 | 64位平台大小 |
cl_device_id类型 | 句柄类型,用于表示OpenCL设备的唯一标识符 | 8字节 |
表7.cl_device_id类型
clGetContextInfo():用于查询上下文cl_context的相关信息
cl_int clGetContextInfo(cl_context context, //指定要查询的OpenCL上下文cl_context_info param_name, //指定要查询的信息类型size_t param_value_size, //指定param_value缓冲区的大小void *param_value, //存储返回的参数值size_t *param_value_size_ret //返回实际写入的字节数
);返回值类型:cl_int1.CL_SUCCESS:函数成功执行。2.CL_INVALID_VALUE:传递的参数值无效。例如num_entries为0而platforms不为NULL3.CL_OUT_OF_HOST_MEMORY:主机内存不足,无法完成操作
在函数中存在cl_context_info枚举类型,指定该枚举类型主要用于设置返回值param_value的类型,对此作以下解释:
枚举值 | 含义 | param_value的类型 |
CL_CONTEXT_REFERENCE_COUNT | 上下文的引用计数,表示当前引用该上下文的对象数量 | cl_uint |
CL_CONTEXT_DEVICES | 与上下文关联的设备列表 | cl_device_id[] |
CL_CONTEXT_PROPERTIES | 上下文的属性列表,包括平台信息或其他属性 | cl_context_properties[] |
CL_CONTEXT_NUM_DEVICES | 上下文中设备的数量 | cl_uint |
表8.cl_context_info枚举类型
创建CL内核函数
#define KERNEL(...)#__VA_ARGS__ //使用#__VA_ARGS_宏将传入KERNEL宏的实参转为字符串//编写内核函数,该内核函数将转换为字符串并由指针kernelSourseCode指向
const char* kernelSourceCode = KERNEL(__kernel void wildpointer(__global uint * buffer) { //__kernel用于声明OpenCL内核函数size_t gidx = get_global_id(0);size_t gidy = get_global_id(1);size_t lidx = get_local_id(0);buffer[gidx + 4 * gidy] = (1 << gidx) | (0x10 << gidy);}
);
这段代码主要是将我们需要使用GPU执行的函数转换为字符串,后续通过将函数和参数传入内核程序来指定运行内容,其中要注意的是我们编写的CL内核函数为wildpointer,这在后续我们获取内核实例的句柄需要保持一致。我们也可以不使用这种方式编写内核函数,我们可以编写一个cl文件在需要载入内核程序时进行调用
装载内核程序
//装载内核程序,编译CL程序,生成OpenCL内核实例
size_t sourceSize[] = { strlen(kernelSourceCode) }; //获取函数字符串长度
cl_program program = clCreateProgramWithSource(context, 1, &kernelSourceCode, sourceSize, &status);
if (status != CL_SUCCESS) {std::cout << "错误:将二进制文件装载到内核程序失败" << std::endl;return EXIT_FAILURE;
}
在这段代码中,存在一个cl_program类型,具体解释如下:
类型名称 | 含义 | 64位平台大小 |
cl_program类型 | 用于表示一个OpenCL程序对象,并可通过编译、链接生成可执行的OpenCL内核 | 8字节 |
表9.cl_program类型
clCreateProgramWithSource():用于创建一个程序对象,该程序对象包含OpenCL C源代码
cl_program clCreateProgramWithSource(cl_context context, //程序对象的上下文cl_uint count, //函数的字符长度const char **strings, //指向包含OpenCL C源代码的指针const size_t *lengths, //每个字符串的长度数组cl_int *errcode_ret //返回函数的错误码
);返回值类型:cl_program
成功时返回创建的程序对象,失败时返回NULL
生成CL程序并获取实例句柄
//为指定的OpenCL设备生成CL程序
status = clBuildProgram(program, 1, devices, NULL, NULL, NULL);
if (status != CL_SUCCESS) {std::cout << "错误:编译CL程序失败" << std::endl;return EXIT_FAILURE;
}//获取内核实例的句柄
cl_kernel kernel = clCreateKernel(program, "wildpointer", &status);
if (status != CL_SUCCESS) {std::cout << "错误:在程序上初始化内核程序失败" << std::endl;return EXIT_FAILURE;
}
在代码中存在一个cl_kernel类型,具体解释如下:
类型名称 | 含义 | 64位平台大小 |
cl_kernel类型 | 表示一个内核对象的句柄类型。内核是 OpenCL 程序的基本执行单位,通常是一个通过编译后生成的函数,用来在设备上执行计算任务 | 8字节 |
表10.cl_kernel类型
clBuildProgram():用于编译OpenCL程序对象,生成针对目标设备的执行代码
cl_int clBuildProgram(cl_program program, //要编译的OpenCL程序对象cl_uint num_devices, //编译程序的设备个数const cl_device_id *device_list, //设备列表const char *options, //编译时的选项字符串,例如优化选项、定义宏等void (*pfn_notify)(cl_program, void *user_data), //回调函数指针,在编译后调用void *user_data //传递给回调函数的参数
);返回值类型:cl_int1.CL_SUCCESS:函数成功执行2.CL_INVALID_PROGRAM:无效的程序对象3.CL_INVALID_DEVICE:无效的词语4.CL_INVALID_VALUE:传递的参数值无效。例如num_entries为0而platforms不为NULL5.CL_OUT_OF_HOST_MEMORY:主机内存不足,无法完成操作
参数options的选项见下表:
选项 | 含义 |
-cl-fast-relaxed-math | 快速浮点计算,用于性能优先的浮点计算 |
-cl-no-signed-zeros | 简化符号的处理逻辑,无法使用+0和-0 |
-cl-mad-enable | 实现硬件级别乘加指令优化 |
-I<dir> | 包含路径,用于指定头文件的搜索目录 |
-Werror | 将警告视为错误,确保编译过程无警告 |
-D<macro>=<value> | 定义宏变量,用于在源代码中进行条件编译 |
表11.参数options
创建命令队列和缓冲区
//创建OpenCL命令队列
cl_command_queue commandQueue = clCreateCommandQueue(context, devices[0], 0, &status);
if (status != CL_SUCCESS) {std::cout << "错误:初始化命令队列失败" << std::endl;return EXIT_FAILURE;
}//创建OpenCL缓冲区
unsigned int* outbuffer = new unsigned int[4 * 4];
memset(outbuffer, 0, 4 * 4 * 4);
cl_mem outputBuffer = clCreateBuffer(context, CL_MEM_ALLOC_HOST_PTR, 4 * 4 * 4, NULL, &status);
if (status != CL_SUCCESS) {std::cout << "错误:初始化OpenCL缓冲区失败" << std::endl;return EXIT_FAILURE;
}
在代码中存在一个cl_command_queue和cl_mem类型,具体解释如下:
类型名称 | 含义 | 64位平台大小 |
cl_command_queue类型 | 命令队列类型,用于在OpenCL设备上执行命令,确保命令按顺序执行,如内核执行、内存传输等 | 8 字节 |
cl_mem类型 | 内存对象类型,用于在OpenCL设备上存储数据,允许数据在主机和设备之间进行传输,可以是缓冲区、图像等 | 8 字节 |
表12.cl_command_queue和cl_mem类型
clCreateCommandQueue():创建一个命令队列,用于在指定的 OpenCL 设备上执行命令
cl_command_queue clCreateCommandQueue(cl_context context, //OpenCL上下文,指定命令队列将要使用的设备和环境cl_device_id device, //指定命令队列将要执行命令的设备cl_command_queue_properties properties, //命令队列的属性,用于指定命令队列的行为cl_int *errcode_ret //返回函数的错误码
);返回值类型:cl_command_queue
返回一个 cl_command_queue 类型的指针,指向创建的命令队列对象。如果创建失败,返回 NULL
参数cl_command_queue_properties的选项见下表:
选项 | 含义 |
CL_QUEUE_OUT_OF_ORDER_EXCE_MODE_ENABLE | 无序执行命令 |
CL_QUEUE_PROFILING_ENABLE | 命令队列中的命令将启用性能分析 |
CL_QUEUE_ON_DEVICE | 指示这是一个设备队列 |
CL_QUEUE_ON_DEVICE_DEFAULT | 默认的设备队列 |
clCreateBuffer():创建一个 OpenCL 缓冲区内存对象,用于在 OpenCL 设备上存储数据
cl_mem clCreateBuffer(cl_context context, //OpenCL上下文,指定命令队列将要使用的设备和环境cl_mem_flags flags, //内存对象的标志,用于指定内存对象的访问权限和行为size_t size, //内存对象的大小void *host_ptr, //指向主机内存的指针,NULL则在设备上分配内存;非NULL则使用主机内存cl_int *errcode_ret //返回函数的错误码
);
参数cl_mem_flags的选项见下表:
选项 | 含义 |
CL_MEM_READ_WRITE | 默认标志,指定内存对象可以被内核读写 |
CL_MEM_WRITE_ONLY | 指定内存对象只能被内核写入,不能读取 |
CL_MEM_READ_ONLY | 指定内存对象只能被内核读取,不能写入 |
CL_MEM_USE_HOST_PTR | 只有在host_ptr不为NULL时有效。指示OpenCL实现使用host_ptr指向的内存作为内存对象的存储 |
CL_MEM_ALLOC_HOST_PTR | 指示OpenCL实现从主机可访问的内存中分配内存 |
CL_MEM_COPY_HOST_PTR | 只有在host_ptr不为NULL时有效。指示OpenCL实现为内存对象分配内存,并从host_ptr指向的内存中复制数据 |
将参数传入内核程序并传入命令队列
//将参数传入内核程序
status = clSetKernelArg(kernel, 0, sizeof(cl_mem), (void*)&outputBuffer);
if (status != CL_SUCCESS) {std::cout << "错误:设置内核参数失败" << std::endl;return EXIT_FAILURE;
}//将内核程序插入命令队列
size_t globalThreads[] = {4, 4};
size_t localThread[] = {2, 2};
status = clEnqueueNDRangeKernel(commandQueue, kernel, 2, NULL, globalThreads, localThread, 0, NULL, NULL);
if (status != CL_SUCCESS) {std::cout << "错误:内核程序插入命令队列失败" << std::endl;return EXIT_FAILURE;
}
clSetKernelArg(): 用于为 OpenCL 内核的特定参数设置参数值。在执行内核之前,必须为内核的所有参数设置值
cl_int clSetKernelArg(cl_kernel kernel, //内核程序对象cl_uint arg_index, //参数的索引,从0开始,表示内核参数列表中的位置size_t arg_size, //参数值的大小const void *arg_value //指向参数值的指针
);返回值类型:cl_int1.CL_SUCCESS:函数成功执行2.CL_INVALID_PROGRAM:无效的程序对象3.CL_INVALID_DEVICE:无效的词语4.CL_INVALID_VALUE:传递的参数值无效。例如num_entries为0而platforms不为NULL5.CL_OUT_OF_HOST_MEMORY:主机内存不足,无法完成操作
clEnqueueNDRangeKernel() :用于将内核执行命令排队到命令队列中
cl_int clEnqueueNDRangeKernel(cl_command_queue command_queue, //命令队列对象cl_kernel kernel, //执行的内核程序对象cl_uint work_dim, //工作项的维度,范围为1到3const size_t *global_work_offset, //全局工作项的起始偏移量const size_t *global_work_size, //每个维度的全局工作项数量const size_t *local_work_size, //每个工作组中的工作项数量cl_uint num_events_in_wait_list, //等待事件列表中的事件数量const cl_event *event_wait_list, //事件对象列表,用于等待之前的一些事件完成cl_event *event //用于异步传输的事件对象
);返回值类型:cl_int1.CL_SUCCESS:函数成功执行2.CL_INVALID_PROGRAM:无效的程序对象3.CL_INVALID_DEVICE:无效的词语4.CL_INVALID_VALUE:传递的参数值无效。例如num_entries为0而platforms不为NULL5.CL_OUT_OF_HOST_MEMORY:主机内存不足,无法完成操作
执行命令队列并保存至主机
//执行命令队列中的命令
status = clFinish(commandQueue);
if (status != CL_SUCCESS) {std::cout << "错误:完全命令队列" << std::endl;return EXIT_FAILURE;
}//将设备的缓冲区中的数据保存至主机
status = clEnqueueReadBuffer(commandQueue, outputBuffer, CL_TRUE, 0, 4 * 4 * 4, outbuffer, 0, NULL, NULL);
if (status != CL_SUCCESS) {std::cout << "错误:读取缓冲区队列失败" << std::endl;return EXIT_FAILURE;
}
clFinish() :用于确保命令队列中的所有命令都已执行完毕。会阻塞主机线程,直到命令队列中的所有命令都完成执行
cl_int clFinish(cl_command_queue command_queue //命令队列对象,指定了要等待其命令完成的队列
);返回值类型:cl_int1.CL_SUCCESS:函数成功执行2.CL_INVALID_PROGRAM:无效的程序对象3.CL_INVALID_DEVICE:无效的词语4.CL_INVALID_VALUE:传递的参数值无效。例如num_entries为0而platforms不为NULL5.CL_OUT_OF_HOST_MEMORY:主机内存不足,无法完成操作
clEnqueueReadBuffer():用于将数据从设备的缓冲区读取到主机内存中。它可以是阻塞或非阻塞的,具体取决于blocking_read
参数
cl_int clEnqueueReadBuffer(cl_command_queue command_queue, //命令队列对象cl_mem buffer, //数据缓冲区对象cl_bool blocking_read, //指示读取操作是否阻塞size_t offset, //缓冲区中的偏移量size_t size, //读取的数据大小void *ptr, //指向主机内存的指针,用于存储读取的数据cl_uint num_events_in_wait_list, //等待事件列表中的事件数量const cl_event *event_wait_list, //事件对象列表,用于等待这些事件完成后再执行读取命令cl_event *event //事件对象,用于查询读取命令的执行状态
);返回值类型:cl_int1.CL_SUCCESS:函数成功执行2.CL_INVALID_PROGRAM:无效的程序对象3.CL_INVALID_DEVICE:无效的词语4.CL_INVALID_VALUE:传递的参数值无效。例如num_entries为0而platforms不为NULL5.CL_OUT_OF_HOST_MEMORY:主机内存不足,无法完成操作
释放资源
status = clReleaseKernel(kernel);
status = clReleaseProgram(program);
status = clReleaseMemObject(outputBuffer);
status = clReleaseCommandQueue(commandQueue);
status = clReleaseContext(context);
clReleaseKernel():减少指定内核对象的引用计数。当引用计数降为零时,内核对象将被释放
cl_int clReleaseKernel(cl_kernel kernel //要释放的内核对象
);
clReleaseProgram():减少指定程序对象的引用计数。当引用计数降为零时,程序对象及其构建的对象(如内核)将被释放
cl_int clReleaseProgram(cl_program program //要释放的程序对象
);
clReleaseMemObject():减少指定内存对象的引用计数。当引用计数降为零时,内存对象将被释放
cl_int clReleaseMemObject(cl_mem memobj //要释放的内存对象,例如缓冲区或图像
);
clReleaseCommandQueue():减少指定命令队列的引用计数。当引用计数降为零时,命令队列将被释放
cl_int clReleaseCommandQueue(cl_command_queue command_queue //要释放的命令队列
);
clReleaseContext():减少指定上下文对象的引用计数。当引用计数降为零时,上下文及其关联的所有对象(如命令队列、内存对象、程序对象和内核对象)都将被释放
cl_int clReleaseContext(cl_context context //要释放的上下文对象
);
相关文章:

OpenCL(贰):浅析CL内核程序接口函数
目录 1.前言 2.获取平台信息 1.cl_int类型 2.cl_platform_id类型 3.clGetPlatformIDs():查询系统OpenCL平台数量或获取具体的平台信息 4.clGetPlatformInfo():查询指定OpenCL平台的信息,例如平台名称、供应商、版本等 3.设置OpenCL上下文…...
Leetcode 3407. Substring Matching Pattern
Leetcode 3407. Substring Matching Pattern 1. 解题思路2. 代码实现 题目链接:3407. Substring Matching Pattern 1. 解题思路 这一题是一道leetcode easy的题目,照说应该没啥的,不过实际我做的时候在这里卡了一下,所以还是拿…...

学英语学压测:02jmeter组件-测试计划和线程组ramp-up参数的作用
📢📢📢:先看关键单词,再看英文,最后看中文总结,再回头看一遍英文原文,效果更佳!! 关键词 Functional Testing功能测试[ˈfʌŋkʃənəl ˈtɛstɪŋ]Sample样…...

Vue笔记-001-声明式渲染
https://cn.vuejs.org/tutorial/#step-2https://cn.vuejs.org/tutorial/#step-2 Vue 单文件组件 (Single-File Component,缩写为 SFC) 单文件组件是一种可复用的代码组织形式,它将从属于同一个组件的 HTML、CSS 和 JavaScript 封装在使用 .vue 后缀的文件…...
26考研资料分享 百度网盘
26考研资料分享考研资料合集 百度网盘(仅供参考学习) 基础班: 通过网盘分享的文件:2026【考研英语】等3个文件 链接: https://pan.baidu.com/s/1Q6rvKop3sWiL9zBHs87kAQ?pwd5qnn 提取码: 5qnn --来自百度网盘超级会员v3的分享…...
.NET 8 + Ocelot + Consul 实现代理网关、服务发现
.NET 8 Ocelot Consul 实现代理网关、服务发现 本文环境:.NET 8 Ocelot 23.4.2 Consul 1.7.14.6 1 实现网关 分别创建3个WebApi工程:OcelotGw、TestGwAService、TestGwBService;在OcelotGw工程中安装Ocelot包:Install-Packag…...
使用 Nginx 轻松处理跨域请求(CORS)
使用 Nginx 轻松处理跨域请求(CORS) 在现代 Web 开发中,跨域资源共享(CORS)是一种重要的机制,用于解决浏览器的同源策略限制。CORS 允许服务器声明哪些来源可以访问其资源,从而确保安全性与可用…...

【LeetCode Hot100 二分查找】搜索插入位置、搜索二维矩阵、搜索旋转排序数组、寻找两个正序数组的中位数
二分查找 搜索插入位置搜索二维矩阵在排序数组中查找元素的第一个和最后一个位置寻找旋转排序数组中的最小值搜索旋转排序数组寻找两个正序数组的中位数(hard) 搜索插入位置 给定一个排序数组和一个目标值,在数组中找到目标值,并…...
使用MediaPipe Face Mesh 面部动作检测
一、技术选型 OpenCV(Open Source Computer Vision Library) 用于视频流捕捉、图像预处理和基本图像处理操作。 MediaPipe 提供高效的人脸检测与关键点提取功能(Face Mesh)。 Python 作为后端开发语言,整合上述库进行…...
【Vue】<script setup>和 <script>区别是什么?在使用时的写法区别?
<script setup> 是 Vue 3 引入的一种新的脚本语法,它提供了一种更简洁和声明式的方式来编写组件逻辑。它是为了解决传统 <script> 标签在 Vue 单文件组件(SFC)中的一些局限性而设计的。 <script setup> 与 <script>…...

微服务框架,Http异步编程中,如何保证数据的最终一致性
一、背景 在微服务框架下,跨服务之间的调用,当遇到操作耗时或者量大的情况,我们一般会采用异步编程实现。 本文出现的问题是:异步回调过来时,却未查询到数据库中的任务,导致未能正常处理回调。 下面是当…...

vue3-dom-diff算法
vue3diff算法 什么是vue3diff算法 Vue3中的diff算法是一种用于比较虚拟DOM树之间差异的算法,其目的是为了高效地更新真实DOM,减少不必要的重渲染 主要过程 整个过程主要分为以下五步 前置预处理后置预处理仅处理新增仅处理后置处理包含新增、卸载、…...

年会抽奖Html
在这里插入图片描述 <!-- <video id"backgroundMusic" src"file:///D:/background.mp3" loop autoplay></video> --> <divstyle"width: 290px; height: 580px; margin-left: 20px; margin-top: 20px; background: url(D:/nianhu…...
ubuntu16 重启之后lvm信息丢失故障恢复
一、背景 1、问题背景 业务有一台物理开发服务器,文件系统有损坏;由于重启时没有检查,导致重启卡住。后面通过断电重新启动之后,无法进入系统;进入救援模式,注释数据盘挂载。重启之后进入系统,…...
【华为OD-E卷 - 热点网站统计 100分(python、java、c++、js、c)】
【华为OD-E卷 - 热点网站统计 100分(python、java、c、js、c)】 题目 企业路由器的统计页面,有一个功能需要动态统计公司访问最多的网页URL top N。请设计一个算法,可以高效动态统计Top N的页面 输入描述 每一行都是一个URL或…...
Ubuntu下安装Android Sdk
下载android sdk命令行工具 https://developer.android.com/studio?hlzh-cn#command-tools mkdir android-sdk cd android-sdk unzip commandlinetools-linux-11076708_latest.zip 添加环境变量到~/.bashrc export ANDROID_HOME$HOME/android-sdk export PATH$PATH:$ANDRO…...

【JVM】总结篇-类的加载篇之 类的加载器 和ClassLoader分析
文章目录 类的加载器ClassLoader自定义类加载器双亲委派机制概念源码分析优势劣势如何打破Tomcat 沙箱安全机制JDK9 双亲委派机制变化 类的加载器 获得当前类的ClassLoader clazz.getClassLoader() 获得当前线程上下文的ClassLoader Thread.currentThread().getContextClassLoa…...

怎样修改el-table主题样式
起因:el-table有主题样式,部分需要单独设置 环境:ideanodejs插件谷歌浏览器 第一步:找到scss文件: 谷歌浏览器打开表格页面,ctrlshifti打开开发者工具,点击后鼠标移动到表格单元格上单击一下…...

MySQL(二)MySQL DDL数据库定义语言
1. MySQL DDL数据库定义语言 1.1. MySQL定义语言 进入MySQL mysql -u root -p(回车后输入密码,即可进入mysq1)1.1.1. 数据库操作 (1)查看数据库 mysql>show databases;注:MySQL语句分隔符为“;” mysql库很重要它里面有…...
Spring Boot 项目启动报 NoClassDefFoundError 异常的原因分析与解决方案 - jackson 版本不一致
目录 报错: 问题分析: 解决方案: 方案 1:对 Jackson 版本进行统一 方案 2:升级 Springfox 版本 方案 3:替换 Springfox 为 springdoc-openapi(推荐) 方案 4:排除冲突的 Jack…...

sqlsugar WhereIF条件的大于等于和等于查出来的坑
一、如下图所示,当我用 .WhereIF(input.Plancontroltype > 0, u > u.Plancontroltype (DnjqPlancontroltype)input.Plancontroltype) 这里面用等于的时候,返回结果一条数据都没有。 上图中生成的SQL如下: SELECT id AS Id ,code AS …...
PHP:Web 开发的强大基石与未来展望
在当今数字化时代,Web 开发技术日新月异,各种编程语言和框架层出不穷。然而,PHP 作为一种历史悠久且广泛应用的服务器端脚本语言,依然在 Web 开发领域占据着重要地位。 PHP 的历史与现状 PHP(Hypertext Preprocessor…...
华为OD机试_2025 B卷_数组去重和排序(Python,100分)(附详细解题思路)
题目描述 给定一个乱序的数组,删除所有的重复元素,使得每个元素只出现一次,并且按照出现的次数从高到低进行排序,相同出现次数按照第一次出现顺序进行先后排序。 输入描述 一个数组 输出描述 去重排序后的数组 用例 输入1,3,…...

《PMBOK® 指南》第八版草案重大变革:6 大原则重构项目管理体系
项目管理领域的权威指南迎来关键升级!PMI 最新发布的《PMBOK 指南》第八版草案引发行业广泛关注,此次修订首次将项目管理原则浓缩为 6 大黄金法则,重构 7 大绩效域,并首度公开过程组与绩效域的映射关系。本文将全面解析新版核心变…...
ajax学习手册
Ajax 通俗易懂学习手册 目录 Ajax 基础概念XMLHttpRequest 详解Fetch API (现代方式)处理不同数据格式错误处理和状态码Ajax 高级技巧实战项目案例最佳实践 Ajax 基础概念 什么是 Ajax? Ajax Asynchronous JavaScript And XML 通俗解释: Ajax 就像…...

Python异步爬虫与代理完美结合
为了编写一个高性能的异步爬虫,并使用代理IP,我们可以使用以下技术栈:aiohttp (用于异步HTTP请求)、asyncio (用于异步编程)、代理IP可以使用一个代理池,我们从文件中读取或者从API获…...

小白如何在cursor中使用mcp服务——以使用notion的api为例
1. 首先安装node.js,在这一步的时候不要勾选不要勾选 2. 安装完之后,前往notion页面 我的创作者个人资料 | Notion 前往集成页面,添加新集成,自己输入名字,选择内部 新建完之后,进入选择只读 复制密匙 然后前往cursor页面 新建…...

黑马Java面试笔记之 微服务篇(SpringCloud)
一. SpringCloud 5大组件 SpringCloud 5大组件有哪些? 总结 五大件分别有: Eureka:注册中心Ribbon:负载均衡Feign:远程调用Hystrix:服务熔断Zuul/Gateway:网关 如果项目用到了阿里巴巴ÿ…...

Elasticsearch最新入门教程
文章目录 Elasticsearch最新入门教程1.Elasticsearch安装2.Kibana安装3.Elasticsearch关键概念4.SpringBoot整合Elasticsearch4.1 导入Elasticsearch数据4.2 创建SpringBoot项目4.3 修改pom.xml文件4.4 创建es实体类4.5 创建es的查询接口 5.DSL语句5.1 无条件查询5.2 指定返回的…...
CMake 为 Debug 版本的库或可执行文件添加 d 后缀
在使用 CMake 构建项目时,我们经常需要区分 Debug 和 Release 构建版本。一个常见的做法是为 Debug 版本的库或可执行文件添加后缀(如 d),例如 libmylibd.so 或 myappd.exe。 本文将介绍几种在 CMake 中实现为 Debug 版本自动添加 d 后缀的方法。 方法一:使用 CMAKE_DEBU…...