理论教育 编写OpenCL向量相加程序的步骤

编写OpenCL向量相加程序的步骤

时间:2023-06-23 理论教育 版权反馈
【摘要】:所以我们需要查询系统中可用的OpenCL框架,即platform。本例使用的是GPU作为OpenCL计算设备。详细使用情况请参阅OpenCL Spec-ifications。代码清单4-12 向量相加的kernal code2.编写OpenCL kernel首先准备kernal code,如果有过shader编程经验的人可能会比较熟悉,这里面需要把在每个compute

编写OpenCL向量相加程序的步骤

1.OpenCL初始化

与CUDA不同,OpenCL为了兼顾不同的设备,其需要用一系列的API来初始化OpenCL的运行环境,其主要通过以下四项工作来初始化device的运行环境。

(1)平台查询与选择

首先我们要取得系统中所有支持OpenCL的平台(platform)。所谓的platform指的就是硬件厂商提供的OpenCL框架,不同的CPU/GPU开发商(比如Intel、AMD、NVIDIA)可以在一个系统上分别定义自己的OpenCL框架。所以我们需要查询系统中可用的OpenCL框架,即platform。使用API函数clGetPlatformIDs获取可用platform的数量。然后根据数量来分配内存,并得到所有可用的platform,所使用的API还是clGetPlatformIDs。在OpenCL中,类似这样的函数调用很常见:第一次调用以取得数目,便于分配足够的内存;然后调用第二次以获取真正的信息。现在,所有的platform都存在了变量platforms中,接下来需要做的就是取得我们所需的platform。通过使用clGetPlatformInfo来获得platform的信息。通过这个API以知晓platform的厂商信息,以便我们选出需要的platform。其具体代码如代码清单4-8所示。

代码清单4-8 查询并选择一个platform

(续)

(续)

(2)创建上下文(context)

第一步是通过platform得到相应的contextpro perties,第二步是通过clCreate-ContextFromType函数创建context。其具体代码如代码清单4-9所示。

其中clCreateContextFromType函数的第二个参数可以设定context关联的设备类型。本例使用的是GPU作为OpenCL计算设备。目前可以使用的类别包括:

-CL_DEVICE_TYPE_CPU

-CL_DEVICE_TYPE_GPU

-CL_DEVICE_TYPE_ACCELERATOR

-CL_DEVICE_TYPE_DEFAULT

-CL_DEVICE_TYPE_ALL

代码清单4-9platform上建立context

(2)创建上下文(context)

第一步是通过platform得到相应的contextpro perties,第二步是通过clCreate-ContextFromType函数创建context。其具体代码如代码清单4-9所示。

其中clCreateContextFromType函数的第二个参数可以设定context关联的设备类型。本例使用的是GPU作为OpenCL计算设备。目前可以使用的类别包括:

-CL_DEVICE_TYPE_CPU

-CL_DEVICE_TYPE_GPU

-CL_DEVICE_TYPE_ACCELERATOR

-CL_DEVICE_TYPE_DEFAULT

-CL_DEVICE_TYPE_ALL

代码清单4-9platform上建立context

(3)查询与选择设备(device)

context创建好之后,要做的就是查询可用的device。与获取platform类似,我们调用两次clGetContextInfo来完成查询。第一次调用获取关联context的device个数,并根据个数申请内存;第二次调用获取所有device实例。如果你想了解每个device的具体信息,可以调用clGetDeviceInfo函数来获取,返回的信息有设备类型、生产商以及设备对某些扩展功能的支持与否等。详细使用情况请参阅OpenCL Spec-ifications。其具体代码如代码清单4-10所示。

代码清单4-10context上查询device

(3)查询与选择设备(device)

context创建好之后,要做的就是查询可用的device。与获取platform类似,我们调用两次clGetContextInfo来完成查询。第一次调用获取关联context的device个数,并根据个数申请内存;第二次调用获取所有device实例。如果你想了解每个device的具体信息,可以调用clGetDeviceInfo函数来获取,返回的信息有设备类型、生产商以及设备对某些扩展功能的支持与否等。详细使用情况请参阅OpenCL Spec-ifications。其具体代码如代码清单4-10所示。

代码清单4-10context上查询device

(4)创建命令队列(command queue)

command queue是将要执行的各种命令。可以通过clCreateCommandQueue函数创建。其中的device必须为context的关联设备,所有该command queue中的命令都会在这个指定的device上运行。其具体代码如代码清单4-11所示。

代码清单4-11 在指定的device上创建command queue

(4)创建命令队列(command queue)

command queue是将要执行的各种命令。可以通过clCreateCommandQueue函数创建。其中的device必须为context的关联设备,所有该command queue中的命令都会在这个指定的device上运行。其具体代码如代码清单4-11所示。

代码清单4-11 在指定的device上创建command queue

2.编写OpenCL kernel

首先准备kernal code,如果有过shader编程经验的人可能会比较熟悉,这里面需要把在每个compute item上run的那个函数写成一段二进制字符串,通常实现方法是写成单独的一个文件(扩展名随意),然后在程序中使用的时候二进制读入这个文件。本例的向量相加的kernal code如代码清单4-12所示。

代码清单4-12 向量相加的kernal code

2.编写OpenCL kernel

首先准备kernal code,如果有过shader编程经验的人可能会比较熟悉,这里面需要把在每个compute item上run的那个函数写成一段二进制字符串,通常实现方法是写成单独的一个文件(扩展名随意),然后在程序中使用的时候二进制读入这个文件。本例的向量相加的kernal code如代码清单4-12所示。

代码清单4-12 向量相加的kernal code

3.创建OpenCL kernel对象

(1)加载OpenCL内核程序并创建一个program对象

本例中的kernel程序是作为静态字符串读入的(单独的文本文件也一样),所以使用的是clCreate ProgramWith Source。其具体代码如代码清单4-13所示。如果不想让kernel程序被其他人看见,可以先生成二进制文件,再通过clCreateProgram-WithBinary函数动态读入二进制文件,进行一定的保密处理。详细请参阅OpenCL Specifications。

代码清单4-13 加载OpenCL内核程序并创建一个program对象

3.创建OpenCL kernel对象(www.daowen.com)

(1)加载OpenCL内核程序并创建一个program对象

本例中的kernel程序是作为静态字符串读入的(单独的文本文件也一样),所以使用的是clCreate ProgramWith Source。其具体代码如代码清单4-13所示。如果不想让kernel程序被其他人看见,可以先生成二进制文件,再通过clCreateProgram-WithBinary函数动态读入二进制文件,进行一定的保密处理。详细请参阅OpenCL Specifications。

代码清单4-13 加载OpenCL内核程序并创建一个program对象

(2)编译program中的kernel及创建指定名字的kernel对象

kernel程序读入完毕,要做的自然是使用clBuildProgram编译kernel,最终,kernel将被相应device上的OpenCL编译器编译成可执行的机器码。成功编译后,可以通过clCreateKernel来创建一个kernel对象。引号中的adder就是kernel对象所关联的kernel函数的函数名。要注意的是,每个kernel对象必须关联且只能关联一个包含于相应program对象内的kernel程序。实际上,用户可以在cl源代码中写任意多个kernel程序,但在执行某个kernel程序之前必须先建立单独的kernel对象,即多次调用clCreateKernel函数。其具体代码如代码清单4-14所示。

代码清单4-14 编译program中的kernel及创建指定名字的kernel对象

(2)编译program中的kernel及创建指定名字的kernel对象

kernel程序读入完毕,要做的自然是使用clBuildProgram编译kernel,最终,kernel将被相应device上的OpenCL编译器编译成可执行的机器码。成功编译后,可以通过clCreateKernel来创建一个kernel对象。引号中的adder就是kernel对象所关联的kernel函数的函数名。要注意的是,每个kernel对象必须关联且只能关联一个包含于相应program对象内的kernel程序。实际上,用户可以在cl源代码中写任意多个kernel程序,但在执行某个kernel程序之前必须先建立单独的kernel对象,即多次调用clCreateKernel函数。其具体代码如代码清单4-14所示。

代码清单4-14 编译program中的kernel及创建指定名字的kernel对象

4.设置Kernel参数,执行Kernel

(1)为kernel创建内存对象

OpenCL内存对象是指在host中创建,用于kernel程序的内存类型。按维度可以分为两类,一类是buffer,一类是image。buffer是一维的,image可以是二维、三维的texture、frame-buffer或image。本例仅仅使用buffer,可以通过clCreate-Buffer函数来创建。

kernel函数需要三个参数,分别是输入的两个数组和一个输出的数组,这些参数都要一一创建准备好。首先是输入的两个输入数组,其具体代码如代码清单4-15所示。

代码清单4-15 输入数组初始化

4.设置Kernel参数,执行Kernel

(1)为kernel创建内存对象

OpenCL内存对象是指在host中创建,用于kernel程序的内存类型。按维度可以分为两类,一类是buffer,一类是image。buffer是一维的,image可以是二维、三维的texture、frame-buffer或image。本例仅仅使用buffer,可以通过clCreate-Buffer函数来创建。

kernel函数需要三个参数,分别是输入的两个数组和一个输出的数组,这些参数都要一一创建准备好。首先是输入的两个输入数组,其具体代码如代码清单4-15所示。

代码清单4-15 输入数组初始化

a和b是我们要运算的两个输入数组(注意它们是位于CPU上的)。OpenCL计算的变量要位于device的存储上(例如GPU的全局内存)。因此需要进行数据传输,将输入数据从主机内存传输到设备内存上,最后还要在设备内存上分配保存计算结果内存空间。其具体代码如代码清单4-16所示。

代码清单4-16kernel创建内存对象

a和b是我们要运算的两个输入数组(注意它们是位于CPU上的)。OpenCL计算的变量要位于device的存储上(例如GPU的全局内存)。因此需要进行数据传输,将输入数据从主机内存传输到设备内存上,最后还要在设备内存上分配保存计算结果内存空间。其具体代码如代码清单4-16所示。

代码清单4-16kernel创建内存对象

(2)为kernel设置参数

使用clSetKernelArg函数为kernel设置参数。传递的参数既可以是常数、变量,也可以是内存对象。本例传递的就是内存对象。其具体代码如代码清单4-17所示。

代码清单4-17kernel设置参数

(2)为kernel设置参数

使用clSetKernelArg函数为kernel设置参数。传递的参数既可以是常数、变量,也可以是内存对象。本例传递的就是内存对象。其具体代码如代码清单4-17所示。

代码清单4-17kernel设置参数

(3)将要执行的kernel放入command queue

OpenCL提供了三种方案来创建kernel执行命令。最常用的即为本例所示的运行在指定工作空间上的kernel程序,使用了clEnqueueNDRangeKernel函数。最后可以用clFinish函数来确认一个command queue中所有的命令都执行完毕。函数会在commandqueue中所有kernel执行完毕后返回。其具体代码如代码清单4-18所示。

代码清单4-18 将要执行的kernel放入command queue

(3)将要执行的kernel放入command queue

OpenCL提供了三种方案来创建kernel执行命令。最常用的即为本例所示的运行在指定工作空间上的kernel程序,使用了clEnqueueNDRangeKernel函数。最后可以用clFinish函数来确认一个command queue中所有的命令都执行完毕。函数会在commandqueue中所有kernel执行完毕后返回。其具体代码如代码清单4-18所示。

代码清单4-18 将要执行的kernel放入command queue

5.将计算结果从GPU全局内存传输到CPU内存

计算完毕,将结果读回host端。使用clEnqueueReadBuffer函数将OpenCLbuffer对象中的内容读取到host可以访问的内存空间。其具体代码如代码清单4-19所示。

代码清单4-19 执行结果传输回host

5.将计算结果从GPU全局内存传输到CPU内存

计算完毕,将结果读回host端。使用clEnqueueReadBuffer函数将OpenCLbuffer对象中的内容读取到host可以访问的内存空间。其具体代码如代码清单4-19所示。

代码清单4-19 执行结果传输回host

6.释放内存

程序的最后是对所有创建的对象进行释放回收,与C/C++的内存回收同理。其具体代码如代码清单4-20所示。

代码清单4-20 释放内存空间

6.释放内存

程序的最后是对所有创建的对象进行释放回收,与C/C++的内存回收同理。其具体代码如代码清单4-20所示。

代码清单4-20 释放内存空间

免责声明:以上内容源自网络,版权归原作者所有,如有侵犯您的原创版权请告知,我们将尽快删除相关内容。

我要反馈