Opencl API解释(一)

首先注明:我用的AMD的opencl,它有很多sample代码,结合代码来解释这些API

Opencl 常用的API 汇总总结:

信息查询函数

1.

cl_int clGetDeviceInfo(cl_device_id device,  

                              cl_device_info param_name,  

                              size_t param_value_size,

                              void * param_value,

                              size_t *param_value_size_ret ) 

参数说明

此函数用来查询OpenCL设备信息。首先介绍其参数:
第一个参数device是clGetDeviceIDs的返回值。
第二个参数param_name是一个枚举常量,标识要查询的设备信息,具体有哪些信息稍后详述。
第三个参数param_value_size声明下一个参数param_value所指向的存储空间的字节大小。这个大小要>=查询参数大小。
第四个参数param_value指向要查询参数返回到的存储空间的地址。NULL时表示忽略。
第五个参数返回查询到的参数的实际的大小。设为NULL则忽略。 
下面我们来具体介绍一下可以查询的几个常用参数。
CL_DEVICE_TYPE:OpenCL设备类型。目前支持CL_DEVICE_TYPE_CPU,CL_DEVICE_TYPE_GPU, CL_DEVICE_TYPE_ACCELERATOR, CL_DEVICE_TYPE_DEFAULT或以上联合。
CL_DEVICE_VENDOR_ID:一个唯一的供应商识别码。一个唯一设备识别码的例子,PCIe ID。
CL_DEVICE_MAX_COMPUTE_UNITS:OpenCL设备上并行计算单元数目。一个work-group只在一个compute unit上执行。该参数最小为1.
CL_DEVICE_MAX_WORK_ITEM_DEMENSIONS:数据并行执行模块用来声明global和localwork-item IDS的最大维度。该参数最小为3。参考clEnqueueNDRangeKernel,该函数第三个参数work_dim是声明global work-item和work-group中的work-items(specify the global work-items and work items in the work-group.)中使用的维度的数目。work-item应该比0大,且<=CL_DEVICE_MAX_WORK_ITEM_DIMENSIONS.
CL_DEVICE_MAX_WORK_ITEM_SIZES:数组类型,指能被在work-group的每一个维度声明的work-item的最大数目。最小值(1,1,1)。
CL_DEVICE_MAX_WORK_GROUP_SIZE:在一个computeunit中执行一个kernel的work-group中work-item的最大数目。最小值为1。Maximum number of work-items in a work-group executing a kernel ona single compute unit, using the data parallelexecution model. (Refer to clEnqueueNDRangeKernel ). The minimum value is1.  

举例:

//Get max compute units
status = clGetDeviceInfo(
deviceId,
CL_DEVICE_MAX_COMPUTE_UNITS,
sizeof(cl_uint),
&maxComputeUnits,
NULL);
CHECK_OPENCL_ERROR(status, "clGetDeviceIDs(CL_DEVICE_MAX_COMPUTE_UNITS) failed");


2.
cl_int clGetKernelWorkGroupInfo(cl_kernelkernel ,  
                                     cl_device_iddevice ,
                                     cl_kernel_work_group_info  param_name,  
                                     size_t  param_value_size,
                                     void* param_value,
                                     size_t*param_value_size_ret )

 参数说明:

此函数返回指定到某一device上的kernel对象信息。同样,先来看参数:

第一个参数要查询的kernel对象。

第二个参数指定与kernel绑定的设备列表中的某一个设备。这个列表就是与kernel绑定的context对应的kernel列表。如果列表中只有一个device,此处device参数可以为NULL。

第三个参数指定要查询参数名称,这也是个枚举值。

第三个参数是要指定的要查询的参数返回的字节数,要>=返回值。

第四个参数指返回值指向内存空间的地址,若设为NULL则忽略。

第五个参数返回实际查询到的参数的大小。

 下面来说几个重要的可查询的参数:

CL_KERNEL_WORK_GROUP_SIZE:查询在某一指定设备上执行一个kernel可以使用的最大work-group的size。OpenCL实现会使用资源,这就要求kernel确定work-group大小。

This provides a mechanism for the applicationto query the maximum work-group size that can be used to execute a kernel on aspecificdevice given by device. The OpenCL implementationuses the resource requirements of the kernel (register usage etc.) to determinewhat this work -group size should be.

举例:

status = clGetKernelWorkGroupInfo(kernel,
device,
CL_KERNEL_LOCAL_MEM_SIZE,
sizeof(cl_ulong),
&localMemoryUsed,
NULL);
if(checkVal(status, CL_SUCCESS, "clGetKernelWorkGroupInfo failed(CL_KERNEL_LOCAL_MEM_SIZE)"))
return SDK_FAILURE;

两个例子均来自SDKCommon.cpp.

 

 work-group/work-item/size等关系说明

为执行一个数据并行kernel,除work-items的数目外也要指定work-groups的数目。这也就是为什么两个参数都必须传递给clEnqueueNDRangeKernel。例如:

size_t global_item_size = 4;//总的线程数

size_t local_item_size = 1;//每一个group的线程数

/* Execute OpenCL kernel as data parallel*/

ret = clEnqueueNDRangeKernel(command_queue,kernel, 1, NULL,

                   &global_item_size,&local_item_size, 0, NULL, NULL);

这个就表示上面这个数据并行计算的kernel中每一个work-group由1个work-item组成,而共有4个work-items要被处理,即总的work-items要被分成4个work-group。

另外work-item对应硬件上的一个PE(processing element),而一个work-group对应硬件上的一个CU(computing unit)。这种对应可以理解为,一个work-item不能被拆分到多个PE上处理;同样,一个work-group也不能拆分到多个CU上同时处理(忘了哪里看到的信息)。当映射到OpenCL硬件模型上时,每一个work-item运行在一个被称为处理基元(processing element)的抽象硬件单元上,其中每个处理基元可以处理多个work-item(注:摘自《OpenCL异构计算》P87)。(如此而言,是不是说对于二维的globalx必须是localx的整数倍,globaly必须是localy的整数倍?那么如果我数据很大,work-item所能数量很多,如果一个group中中work-item的数量不超过CU中PE的个数,那么group的数量就可能很多;如果我想让group数量小点,那work-item的数目就会很多,还能不能处理了呢?以当前这个示例是能的,但是对于多的work-item,这涉及到如何确定work-item数目的问题。

  结合Cuda的概念进行解释:因为实际上,一个 SM 可以允许的 block 数量,还要另外考虑到他所用到 SM 的资源:shared memory、registers 等。在 G80 中,每个 SM 有 16KB 的 shared memory 和 8192 个 register。而在同一个 SM 里的 block 和 thread,则要共享这些资源;如果资源不够多个 block 使用的话,那 CUDA 就会减少 Block 的量,来让资源够用。在这种情形下,也会因此让 SM 的 thread 数量变少,而不到最多的 768 个。

  比如说如果一个 thread 要用到 16 个 register 的话(在 kernel 中宣告的变量),那一个 SM 的 8192 个 register 实际上只能让 512 个 thread 来使用;而如果一个 thread 要用 32 个 register,那一个 SM 就只能有 256 个 thread 了~而 shared memory 由于是 thread block 共享的,因此变成是要看一个 block 要用多少的 shread memory、一个 SM 的 16KB 能分给多少个 block 了。

  所以虽然说当一个 SM 里的 thread 越多时,越能隐藏 latency,但是也会让每个 thread 能使用的资源更少。因此,这点也就是在优化时要做取舍的了

 

继续向下解释work-group,work-item,size的关系:

每一个work-group中work-item的数目是不能改变的,始终如一。如果work-item的数目不能在work-groups中均分,clEnqueueNDRangeKernel失败,返回错误码CL_INVALID_WORK_GROUP_SIZE。此处要注意,自己在尝试检测GPU处理能力的时候给出的work-item和work-group的数目不能整除时不一定是数量超限,有可能只是不能整除。

 global work-item ID、localwork-item ID,和work-group ID之间的关系如下图所示。

 

图1  work-group ID和work-item ID

表1  获取ID的函数

函数

返回值

get_group_id

Work-group ID

get_global_id

Global work-item ID

get_local_id

Local work-item ID

 

因为要处理2D图像或3D空间,work-items和work-groups可以被指定为2或3维。图2给出一个work-group和work-item被定义为2D的例子。

图2  work-group和work-item定义为2D

因为work-group和work-item可至3维,get_group_id(), get_global_id(), get_local_id()每一个的参数可以是0~2。

注意,空间维度指数和每个work-group中work-item的数目能够依据设备而变化。最大维度指数可以通过clGetDeviceInfo()来获取CL_DEVICE_MAX_WORK_ITEM_DIMENSIONS,每个work-group中work-items的最大值可以通过CL_DEVICE_MAX_WORK_ITEM_SIZES获取。前者是cl_uint型,后者是size_t的数组。

 

在kernel函数中,我们能够通过API调用得到global id以及其他信息:

get_global_id(dim)

get_global_size(dim)

这两个函数能得到每个维度上的global id。

get_group_id(dim)

get_num_groups(dim)

get_local_id(dim)

get_local_size(dim)

这几个函数用来计算group id以及在group内的local id。

get_global_id(0) = column, get_global_id(1) = row

get_num_groups(0) * get_local_size(0) == get_global_size(0)

 

CL_DEVICE_MAX_WORK_ITEM_SIZES,CL_DEVICE_MAX_WORK_GROUP_SIZE(clGetDeivceInfo获取)它跟CL_KERNEL_WORK_GROUP_SIZE(clGetKernelWorkGroupInfo获取)有什么区别?

 

CL_DEVICE_MAX_WORK_ITEM_SIZES : Max work-items sizes in each dimensions, 每一个维度允许的最大的work-item数

CL_DEVICE_MAX_WORK_GROUP_SIZE: Max allowed work-items in a group,一个workgroup所允许的最多work-item数。

CL_KERNEL_WORK_GROUP_SIZE: Group size returned by kernel  实际在kernel中执行的workgroup数目。

 

执行cinfo,可以检测硬件信息

 

 

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
使用 OpenCL API 调用 GPU 进行计算,需要先编写 OpenCL 程序。OpenCL 程序是由 C 语言编写的,可以使用 OpenCL API 调用 OpenCL 驱动程序来执行计算任务。 以下是一个简单的 OpenCL 程序示例,可以将两个向量相加: ``` __kernel void vectorAdd(__global const float* a, __global const float* b, __global float* c) { int i = get_global_id(0); c[i] = a[i] + b[i]; } ``` 使用 OpenCL API 调用 OpenCL 驱动程序执行上述程序,以下是一个简单的代码示例: ```c #include <CL/opencl.h> int main() { cl_platform_id platform_id; cl_device_id device_id; cl_context context; cl_command_queue command_queue; cl_program program; cl_kernel kernel; cl_mem buffer_a, buffer_b, buffer_c; cl_int err; // 获取可用平台 clGetPlatformIDs(1, &platform_id, NULL); // 获取可用设备 clGetDeviceIDs(platform_id, CL_DEVICE_TYPE_GPU, 1, &device_id, NULL); // 创建 OpenCL 上下文 context = clCreateContext(NULL, 1, &device_id, NULL, NULL, &err); // 创建命令队列 command_queue = clCreateCommandQueue(context, device_id, 0, &err); // 创建 OpenCL 内核程序 program = clCreateProgramWithSource(context, 1, &source_str, &source_size, &err); // 编译 OpenCL 程序 clBuildProgram(program, 1, &device_id, NULL, NULL, NULL); // 创建内核对象 kernel = clCreateKernel(program, "vectorAdd", &err); // 创建输入缓冲区 buffer_a = clCreateBuffer(context, CL_MEM_READ_ONLY, sizeof(float) * N, NULL, &err); buffer_b = clCreateBuffer(context, CL_MEM_READ_ONLY, sizeof(float) * N, NULL, &err); // 创建输出缓冲区 buffer_c = clCreateBuffer(context, CL_MEM_WRITE_ONLY, sizeof(float) * N, NULL, &err); // 将数据从主机内存拷贝到设备缓冲区 clEnqueueWriteBuffer(command_queue, buffer_a, CL_TRUE, 0, sizeof(float) * N, a, 0, NULL, NULL); clEnqueueWriteBuffer(command_queue, buffer_b, CL_TRUE, 0, sizeof(float) * N, b, 0, NULL, NULL); // 设置内核参数 clSetKernelArg(kernel, 0, sizeof(cl_mem), (void *)&buffer_a); clSetKernelArg(kernel, 1, sizeof(cl_mem), (void *)&buffer_b); clSetKernelArg(kernel, 2, sizeof(cl_mem), (void *)&buffer_c); // 执行内核 size_t global_item_size = N; size_t local_item_size = 64; // 可以根据设备配置调整 clEnqueueNDRangeKernel(command_queue, kernel, 1, NULL, &global_item_size, &local_item_size, 0, NULL, NULL); // 将数据从设备缓冲区拷贝到主机内存 clEnqueueReadBuffer(command_queue, buffer_c, CL_TRUE, 0, sizeof(float) * N, c, 0, NULL, NULL); // 释放资源 clReleaseMemObject(buffer_a); clReleaseMemObject(buffer_b); clReleaseMemObject(buffer_c); clReleaseKernel(kernel); clReleaseProgram(program); clReleaseCommandQueue(command_queue); clReleaseContext(context); return 0; } ``` 以上代码示例中,我们使用了 OpenCL API 创建了一个命令队列和一个内核对象,并将输入数据拷贝到设备缓冲区,最后执行内核程序并将输出数据拷贝到主机内存。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值