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-9 在platform上建立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-9 在platform上建立context
(3)查询与选择设备(device)
context创建好之后,要做的就是查询可用的device。与获取platform类似,我们调用两次clGetContextInfo来完成查询。第一次调用获取关联context的device个数,并根据个数申请内存;第二次调用获取所有device实例。如果你想了解每个device的具体信息,可以调用clGetDeviceInfo函数来获取,返回的信息有设备类型、生产商以及设备对某些扩展功能的支持与否等。详细使用情况请参阅OpenCL Spec-ifications。其具体代码如代码清单4-10所示。
代码清单4-10 在context上查询device
(3)查询与选择设备(device)
context创建好之后,要做的就是查询可用的device。与获取platform类似,我们调用两次clGetContextInfo来完成查询。第一次调用获取关联context的device个数,并根据个数申请内存;第二次调用获取所有device实例。如果你想了解每个device的具体信息,可以调用clGetDeviceInfo函数来获取,返回的信息有设备类型、生产商以及设备对某些扩展功能的支持与否等。详细使用情况请参阅OpenCL Spec-ifications。其具体代码如代码清单4-10所示。
代码清单4-10 在context上查询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-16 为kernel创建内存对象
a和b是我们要运算的两个输入数组(注意它们是位于CPU上的)。OpenCL计算的变量要位于device的存储上(例如GPU的全局内存)。因此需要进行数据传输,将输入数据从主机内存传输到设备内存上,最后还要在设备内存上分配保存计算结果内存空间。其具体代码如代码清单4-16所示。
代码清单4-16 为kernel创建内存对象
(2)为kernel设置参数
使用clSetKernelArg函数为kernel设置参数。传递的参数既可以是常数、变量,也可以是内存对象。本例传递的就是内存对象。其具体代码如代码清单4-17所示。
代码清单4-17 为kernel设置参数
(2)为kernel设置参数
使用clSetKernelArg函数为kernel设置参数。传递的参数既可以是常数、变量,也可以是内存对象。本例传递的就是内存对象。其具体代码如代码清单4-17所示。
代码清单4-17 为kernel设置参数
(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 释放内存空间
免责声明:以上内容源自网络,版权归原作者所有,如有侵犯您的原创版权请告知,我们将尽快删除相关内容。