目录
创建内核函数
OpenCL 中,使用 cl_kernel 数据结构来表示内核对象,OpenCL 使用如下函数来创建内核对象
c
cl_kernel clCreateKernel(
cl_program program, // 程序对象
const char *kernel_name, // 内核函数名,CL代码中要执行的函数名 如上例子,使用的内核函数名就是 "image_filter"
cl_int *errcode_ret); // 输出错误码,如果成功返回 0
函数 clCreateKernel,参数 program 为 OpenCL 编译的 Program 对象中构建的程序对象,里面包含了多个内核对象
参数 kernel_name 为程序对象中用 __kernel 地址限定符的内核对象名称,参数 errcode_ret 是函数的执行返回状态
c
clCreateKernelsInProgram(cl_program /* program */,
cl_uint /* num_kernels */,
cl_kernel * /* kernels */,
cl_uint * /* num_kernels_ret */) ;
函数 clCreateKernelsInProgram,参数 program 是构建的程序对象,参数 kernels 为返回的内核对象列表,参数 num_kernels 为内核对象列表中内核的个数,参数 num_kernels_ret 为程序对象中内核个数,如果为 NULL 就忽略这个参数
如果程序中有多个对象,使用 clCreateKernelsInProgram 创建内核对象,但是注意内核列表中的内核函数名称顺序并不是根据 cl 文件中的内核书写顺序,而是依赖于实现
设置内核参数
为了执行一个具体的内核,需要向内核函数传递参数,OpenCL 使用下面函数来设置内核参数:
c
clSetKernelArg(cl_kernel /* kernel */,
cl_uint /* arg_index */,
size_t /* arg_size */,
const void * /* arg_value */);
参数 kernel 为内核对象,参数 arg_index 为内核函数参数的索引,按照从左到右的顺序,第一个(最左边的)参数索引为 0, 第二个参数索引为 1,以此类推:
参数 arg_size 表示参数的大小,分为下面几种情况:
- 如果参数为内存对象,那么
arg_size为内存对象类型的大小(例如:sizeof(cl_mem)) - 如果参数用
local修饰符声明,则arg_size用来存储参数的缓冲区所需的字节数 - 如果参数为
sampler_t类型,则arg_size大小必须扽与sizeof(cl_sampler) - 如果参数为
queue_t类型,则arg_size大小必须等于sizeof(cl_command_queue) - 如果参数为其他类型,
arg_size大小为该类型参数大小(例如: 对一个cl_int类型参数,大小为sizeof(cl_int))
参数的 arg_value 为传入内核参数的一个指针:
- 如果参数为内存对象(
cl_mem),则指针指向合适的缓冲、管道、图像或者图像数组对象 - 如果参数用
local修饰符声明,arg_value必须为NULL - 如果参数为
sampler_t,arg_value必须指向采样器对象 - 如果参数为
queue类型,arg_value必须指向设备队列对象
内核参数设置举例
比如一个内核函数实现如下:
c
__kernel void image_filter(
const __global uchar* in_img_data,
const int pxl_bytes,
const int img_line_bytes,
const __global double* in_coeff,
const int coeff_wnd_size,
__global uchar* out_img_data )
设置内核函数参数,有下面的对应关系:
-
对于
cl代码中,形如char、float、int等基本类型的参数,在C语言中有cl_char、cl_int、cl_float等基本的数据类型对应,设置参数时使用这些基本的数据类型即可 -
对于
cl代码中,形如const __global uchar* const__global double*等指针类型的参数,在C语言中统一使用cl_mem缓冲区对象来传递下去 -
对于
cl代码中,形如image2d_t srcImage,sampler_t sampler等图像对象、采样器对象的参数,在 C 语言中统一使用cl_mem、cl_sampler等对象传递下去
所以对上述的 imagefilter 核函数设置参数:
c
// 这里的 image_filter 是内核函数名称
kernel_filter_ = clCreateKernel(program_, "image_filter", &err_code);
CHK_CLERR(err_code);
// 给 image_filter 中各个参数设置参数值,从索引 0 开始
err_code = clSetKernelArg(kernel_filter_, 0, sizeof(cl_mem), (void*)&mem_in_img);
CHK_CLERR(err_code);
err_code = clSetKernelArg(kernel_filter_, 1, sizeof(cl_int), (void*)&pxl_bytes);
CHK_CLERR(err_code);
err_code = clSetKernelArg(kernel_filter_, 2, sizeof(cl_int), (void*)&line_bytes);
CHK_CLERR(err_code);
err_code = clSetKernelArg(kernel_filter_, 3, sizeof(cl_mem), (void*)&mem_in_coeff);
CHK_CLERR(err_code);
err_code = clSetKernelArg(kernel_filter_, 4, sizeof(cl_int), (void*)&coeff_wnd_size);
CHK_CLERR(err_code);
err_code = clSetKernelArg(kernel_filter_, 5, sizeof(cl_mem), (void*)&mem_out_img);
CHK_CLERR(err_code);
clReleaseKernel(kernel);
查询内核信息
一旦创建了内核对象,开发人员如果想知道内核对象的一些属性信息,可以使用如下函数查询内核对象的属性信息
c
clGetKernelInfo(cl_kernel /* kernel */,
cl_kernel_info /* param_name */,
size_t /* param_value_size */,
void * /* param_value */,
size_t * /* param_value_size_ret */);
参数 param_name 为查询内核对象的属性名称,可以接受的参数如下表,参数 param_value 为存储结果的位置指针,
参数 param_value_size 为参数 param_value 的字节数,参数 param_value_size_ret 为实际写入的字节数
cl_kernel_work_group_info |
返回值 | 描述 |
|---|---|---|
CL_KERNEL_GLOBAL_WORK_SIZE |
size_t[3] | 指定设备上执行内核最大可用的工作项大小 |
CL_KERNEL_WORK_GROUP_SIZE |
size_t | 指定设备上执行内核最大可用工作组大小 |
CL_KERNEL_COMPILE_WORK_SIZE |
size_t[3] | TBD |
CL_KERNEL_LOCAL_MEM_SIZE |
cl_ulong | 返回内核函数使用的局部内存量 |
CL_KERNEL_PERFERRED_WORK_GROUP_SIZE_MULTIPLE |
size_t | 给出一个最优工作组大小倍数 |
CL_KERNEL_PERFERRED_PRIVATE_MEM_SIZE |
cl_ulong | 返回各工作项使用的最小私有内存量 |
函数 clGetkernelInfo 不能查询特定于某个设备的内核对象信息,例如:如何将一个并行负载分派到执行内核的一个或者多个不同设备上,那么 该如何处理?
在 OpenCL 中提供了如下函数可以查询特定于某个设备的内核对象属性信息
c
clGetKernelWorkGroupInfo(cl_kernel /* kernel */,
cl_device_id /* device */,
cl_kernel_work_group_info /* param_name */,
size_t /* param_value_size */,
void * /* param_value */,
size_t * /* param_value_size_ret */);
参数 device 为内核对象关联的设备列表中的一个特定设备,参数 param_name 为查询内核工作组的属性信息,可以接受参数如下表,其他参数
类似于 clGetKernelInfo

下面例子用来查询工作组信息:
TBD