3.6 OpenCL运行时(例子)

OpenCL的四种模型在之前的章节中已经全部讨论了,OpenCL通过运行时API让应用开发者了解这些模型。平台模型用来使能一个主机,以及一个或多个设备,让其参与到OpenCL应用的执行中。应用开发者使用编程模型来让OpenCL内核实现其核心计算部分。内核执行时如何获取需要的数据,则有内存模型定义。开发者通过执行模型提交相应的命令到设备端(执行内存搬运或执行内核任务)。本节会将这些内容融合到一个完整的OpenCL应用中。

创建并执行一个简单的OpenCL应用大致需要以下几步:

  1. 查询平台和设备信息

  2. 创建一个上下文

  3. 为每个设备创建一个命令队列

  4. 创建一个内存对象(数组)用于存储数据

  5. 拷贝输入数据到设备端

  6. 使用OpenCL C代码创建并编译出一个程序

  7. 从编译好的OpenCL程序中提取内核

  8. 执行内核

  9. 拷贝输出数据到主机端

  10. 释放资源

下面的代码将具体实现以上总结的每一步。OpenCL应用中,大部分的通用代码对OpenCL的执行进行设置,这样就允许跨硬件平台(不同的供应商)架构来执行OpenCL内核。因此,其中的大部分代码可以直接在其他的应用中直接使用,并且可以抽象成用户定义函数。之后的章节,将展示OpenCL C++ API,其冗余要小于C API。

现在我们来讨论逐个步骤。本节之后,将会提供完整的程序代码。

1. 查询平台和设备

OpenCL内核需要执行在设备端,那么就需要至少一个平台和一个设备可以查询。

之后的完整代码中,我们将默认使用首先找到的平台和设备。这样的源码看起来更加简单明了。

2. 创建一个上下文

找到平台和设备之后,就可以在主机端对上下文进行配置。

3. 为每个设备创建一个命令队列

创建完上下文,就要为每个设备创建一个命令队列(每个命令队列只关联其对应的设备)。主机端需要设备端执行的命令将提交到命令队列中,由命令队列管理执行。

4. 创建设备数组用于存储数据

创建一个数组需要提供其长度,以及与该数组相关的上下文;该数组能对该上下文所有设备可见。通常,调用者需要提供一些标识,来表明数据是可只读、只写或读写。如果第四个参数传NULL,OpenCL将不会在这步对数组进行初始化。

5. 拷贝输入数据到设备端

下一步就是将主机端指针指向的数组拷贝到设备端。该API需要一个命令队列对象作为参数,所以数据通常都是直接拷贝到设备端。将第三个参数设置为CL_TRUE,我们将等待该API将数据全部拷贝到设备端之后才返回。

6. 使用OpenCL C代码创建并编译出一个程序

代码列表3.3中的向量相加内核存储在一个字符数组中,programSource,当可以用来创建程序对象(之后需要编译)。当我们编译一个程序时,我们需要提供目标设备的信息。

7. 从编译好的OpenCL程序中提取内核

内核通过提供内核函数名,在程序上进行创建。

8. 执行内核

内核创建完毕,数据都已经传输到设备端,数组需要设置到内核的参数上。之后,一条需要执行内核的命令就进入了命令队列。内核的执行方式需要指定的NDRange进行配置。

9. 拷贝输出数据到主机端

10. 释放资源

内核执行完成后,并且输出已经传出到主机端,OpenCL分配的资源需要进行释放。这点和C/C++程序中的内存操作、文件处理以及其他资源的处理,都需要开发者显式释放。OpenCL为不同的对象提供了不同的释放API。OpenCL上下文需要最后释放,因为数组和命令队列都绑定在上下文上。这点与C++删除对象有些相似,成员数组需要在成员释放前释放。

3.6.1 向量相加的完整代码

下面将完整的展示向量相加这个例子。其具有上一节的所有步骤,不过这个例子中使用了第一个平台对象和设备对象。

代码清单3.4 使用C API实现的OpenCL向量相加

Last updated