1. 简介
OpenCL(Open Computing Language),即开放运算语言,是一个统一的开放式的开发平台。OpenCL是首个提出的并行开发的开放式的、兼容的、免费的标准,它的目的是为异构系统通用提供统一开发平台。OpenCL最初是由苹果公司设想和开发,并在与AMD,IBM,英特尔和NVIDIA技术团队的合作之下初步完善。随后,苹果将这一草案提交至Khronos Group。
2.框架组成
OpenCL的框架组成可以划分为三个部分,分别为OpenCL平台API、OpenCL运行时API,以及OpenCL内核编程语言。
2.1 平台API
平台(Platform)这个词在OpenCL中拥有非常特定的含义,它表示的是宿主机、OpenCL设备和OpenCL框架的组合。多个OpenCL平台可以共存于一台异构计算机。举个例子,CPU开发人员和GPU开发人员可以在同一个系统上分别定义自己的OpenCL框架。这时就需要一种方法来查询系统中可用的OpenCL 框架,哪些OpenCL设备是可用的,以及这些OpenCL设备的特性。相当于CUDA的主机和设备之间的关系。
此外,为了形成一个给定的OpenCL应用平台,还需要对这些框架和设备所属的子集进行控制。这些功能都是由OpenCL平台API中的函数来解决的。此外,平台API还提供了为OpenCL创建上下文的函数。OpenCL的上下文规定了OpenCL应用程序的打开方式(相当是CUDA中核函数的调用),这可以在宿主机程序代码中得到验证。
2.2 运行时API
平台API提供函数创建好上下文之后,运行时API主要提供使用上下文提供的功能满足各种应用需求的函数。这是一个规模庞大且内容十分复杂的函数集。运行时API的第一个任务是创建一个命令队列。命令队列与设备相关联,而且一个上下文中可以同时存在多个活动的命令队列。有了命令队列,就可以通过调用运行时API提供的函数来进行内存对象的定义以及管理内存中的对象所依赖的所有其他对象。以上是内存对象的持有操作,另外还有释放操作,也是由运行时API提供的。
此外,运行时API还提供了创建动态库所需要的程序对象的函数,正是这些动态库实现了Kernel的定义。最后,运行时层的函数会发出与命令队列交互的命令。此外,管理数据共享和对内核的执行加以限制同步点也是由运行时API处理的。
2.3 内核编程语言
内核编程语言是用于编写OpenCL内核代码的。除了宿主机程序之外,内核程序也十分重要,它负责完成OpenCL中的实际工作。在部分OpenCL实现中用户可以跟其他语言编写的原生内核实现交互,但多数情况下内核是需要用户使用内核编程语言编写实现的。OpenCL C编程语言就是OpenCL中的内核编程语言,该编程语言是"ISO C99 标准"的一个扩展子集,也就是说它是由 ISO C99语言派生而来的。现在的OpenCL2.1还支持C++,是基于eISO/IEC JTC1 SC22 WG21 N 3690(C++14)。
2.4 适合平台
1)AMD
根据AMD官网所提供的内容,OpenCL在AMD显卡中只能适用X86核心的CPU架构,而对其他PowerPC和ARM架构则不适用;并且也不是所有的AMD显卡都能运行OpenCL,按其官网介绍只能是AMD Radeon、AMD FirePro和AMD Firestream三种类型的显卡;但对于操作系统则可以是Linux或Windows的系统。
2)NVIDIA
NVIDIA OpenCL是一种运行于具有CUDA能力GPU上的一种底层API,即OpenCL是运行于CUDA之上的一种API,从而若适用CUDA的平台,也同样适用OpenCL。根据NVIDIA官网最新版本的CUDA 7.5适合的平台。
3.计算架构
OpenCL 的设计目标是为开发人员提供一套移植性强且高效运行的解决方案。为了更好的描述OpenCL设计的核心理念,Khronos Group官方将OpenCL的计算架构分解成四个模型,分别平台模型(Platform Model)、内存模型(Memory Model)、执行模型(Execution Model)以及编程模型(Programming Model)。
3.1 平台模型(Platform Model)
从整体上来看,主机(host)端是负责掌管整个运算的所有计算资源,因此OpenCL 应用程序首先是由主机端开始,然后由程序将各个计算命令从主机端发送给每个 GPU 设备处理单元,运行完毕之后最后由主机端结束。
从图中可以直观的看到,最基本处理单位是Processing Element,简称PE(处理单元),而一个或多个PE组成了Compute Unit,简称CU(计算单元),进而一个或多个CU就组成了Compute Device,即OpenCL设备。最后,一个或多个OpenCL设备连接到主机,并等待着处理主机发送的计算指令,由于PE是最基本处理单位,因此每条计算指令最终都归PE进行处理,而PE是在CU中的。
3.2 内存模型(Memory Model)
OpenCL将内核程序中用到的内存分为图示的四种不同的类型。
其中它们的读写特性分别为:
- Global memory:工作区内的所有工作节点都可以自由的读写其中的任何数据。OpenCL C语言提供了全局缓存(Global buffer)的内建函数。
- Constant memory: 工作区内的所有工作节点可以读取其中的任何数据但不可以对数据内容进行更改,在内核程序的执行过程中保持不变。主机端负责分配和初始化常量缓存(Constant buffer)。
- Local memory: 只有同一工作组中的工作节点才可以对该类内存进行读写操作。它既可以为 OpenCL 的执行分配一块私有内存空间,也可以直接将其映射到一块全局缓存(Global buffer)上。特点是运行速度快。
- Private memory: 只有当前的工作节点能对该内存进行访问和读写操作。一个工作节点内部的私有缓存(Private buffer)对其他节点来说是不可见的。
3.3 执行模型(Execution Model)
OpenCL的执行模型是应用程序通过主机端对OpenCL设备端上的内核程序进行管理,该模型分为两个模块:一个是在主机端执行的管理程序,也称为Hostprogram,另一个是主机端的Hostprogram所管理的在OpenCL上执行的程序,也被称作Kernels。在执行Kernels前,先要建立一个索引空间,来对设备里的每个节点进行标识,每个节点都将执行相同的kernel程序。在每个工作组中,都有一个局部ID,每个节点在全局里还有个全局 ID,OpenCL使用NDRange来定义这个索引空间。
如图示的OpenCL执行模型,其过程可以细分为如下的步骤完成:
- 查询连接主机上的OpenCL设备;
- 创建一个关联到OpenCL设备的context;
- 在关联的设备上创建可执行程序;
- 从程序池中选择kernel程序;
- 从主机或设备上创建存储单元;
- 如果需要将主机的数据复制到OpenCL设备上的存储单元上;
- 执行kernel程序执行;
- 从OpenCL设备上复制结果到主机上
3.4 编程模型(Programming Model)
OpenCL支持两种编程模型,分别为数据并行编程模型和任务并行编程模型,并支持上面由这两种编程模型混合的混合编程模型。
数据并行编程模型
OpenCL提供一个分层的数据并行编程模型,即典型的SIMD计算模型,其特点是每个数据经由同样的指令序列处理,而处理数据的次序是不确定的,并且每个数据的处理是不相干的,即任一线程的计算不得依赖于其它线程的结果(包括中间结果)。任务并行编程模型
任务并行模型中的每个内核是在一个独立的索引空间中执行的,也就是说,执行内核的计算机单元内只有一个工作组,其中只有一个工作项。在这样的模型中,每个线程都可以执行不同的带啊,着相当于MIMD的计算模型,适合多核心CPU。