所有的面向异构平台应用都必须完成以下步骤: 1.发现构成异构系统的组件 2.探查这些组件的特征,使软件能够适应不同硬件单元的特定特性。 3.创建将在平台上运行的指令块(内核) 4.建立并管理计算中涉及的内存对象。 5.在系统中正确的组件上按正确的顺序执行内核。 6.收集最终结果。
上述步骤OpenCL可分解为以下模型:
OpenCL官方文档
OpenCL程序员编写好程序并不知道用户在哪里运行(GPU, CPU, FPGA),OpenCL程序员只知道目标平台符合OpenCL规范。 因此只有在运行时从源代码构建程序对象。宿主机程序定义上下文中的设备。只有那时才由可能知道如何编译程序源代码。
内核由宿主机定义。宿主机发出指令提交内核在一个OpenCL设备上执行。此时 OpenCL 运行时系统会创建一个整数索引空间。对应这个索引空间中的各个点将分别执行内核的一个实例。我们将这些实例插座工作项(work item),工作项由它在索引空间中的坐标来标识。这些坐标就是工作项的全局ID。
来自OpenCL官方文档
给定工作组中的工作项会在一个计算单元的处理单元上并发执行。这是理解OpenCL并发性的关键。具体实现可能串行化内核的执行,甚至可能在一个内核调用中串行化工作组的执行。OpenCL只能确保一个工作组中的工作项并发执行(和共享设备上的处理器资源)。因此不要认为工作组或内核调用会并发执行。尽管实际上他们通常确实会并发执行,但是算法设计人员不能依赖这一点。
一个队列中的命令执行时可以有以下两种模式:
in-order : 命令按顺序发出,前一个执行完才会执行下一个。
out-order : 命令按顺序发出,但是下一个命令执行之前不会等待前一个命令完成。程序员要通过显式的同步机制来强制顺序约束。(参考自动负载均衡)
将多个内核提交到队列时,它们可能需要交互。例如,一组内核可能生成内存对象,而另一组内核则使用这些对象来完成处理。在这种情况下,可以使用同步命令来强制第一组内核先完成,在此之前另一组内核不能开始。
buffer object 和 image object。 它们的区别参考官方文档:
• 缓衝对象中的元素是顺序存储的,内核在设备上运行时可以用指位器访问这些元素。而图 像对象中元素的存储格式对用户是透明的,不能通过指位器直接访问。可以使用 OpenCL C 编程语言提供的内建函式来读写图像对象。 • 对于缓衝对象,内核按其存储格式访问其中的数据。而对于图像对象,其元素的存储格式 可能与内核中使用的数据格式不一样。内核中的图像元素始终是四元矢量(每一元都可以 是浮点型别或者带符号/无符号整形)。内建函式在读写图像元素时会进行相应的格式转 换。 内存对象是用 cl_mem 来表示的。内核的输入和输出都是内存对象。
来自OpenCL编程指南
来自OpenCL 規範
OpenCL 内存一致性模式基于ISO C11 编程语言。
宽松一致性 (relaxed consistency)是OpenCL 采用的一种内存一致性 memory consistency模型。对于同一块内存而言,不同的工作项或者命令所看到的内容可能不同,当然,栅栏( barrier 分为工作组栅栏和命令队列栅栏)和其他的显示同步点(explicit synchronization points)除外。
来自OpenCL编程指南
算法设计者要保证问题中的数据结构与NDRange 索引空间一致。 如果工作项之间有依赖性,工作项的执行需要同步。一个工作组中的工作项必须执行同一个工作组栅栏。
执行同一内核时不同的工作组的工作项之前如何同步,OpenCL 1.1 并未提供任何机制。
OpenCL提供了层次结构的数据并行性: 1.工作组中的工作项数据并行 2.工作组层次的数据并行 OpenCL规范讨论了两个变种: 1.显示模式(explicit model)程序员负责显示的定义工作组大小 2.隐式模式(implicit model) 程序员只需定义 NDRange 空间,有系统选择工作组。
OpenCL数据并行模型有两个重要子集: 1.SIMD(Single Instruction Multiple Data) 内核不包含任何分支语句,也就是每个工作项执行相同的操作。 2.SPMD(Single Program Multiple Data) 内核的分支语句可能让各个工作项执行完全不同的操作。也就是说各个工作项执行同一个“程序”(也就是内核),但是它的具体工作可能完全不同。
在指令内存带宽有限的平台上,或者如果处理单元映射到一个矢量单元,SIMD模型更为高效。
来自OpenCL编程指南 参考OpenCL文档原文:
在 OpenCL 的任務並行編程模型中,內核的實體在執行時獨立於任何索引空間。這在邏輯上 等同於:在計算器件上執行內核時,相應作業組中只有一個作業項。這種模型下,用戶以如下方式 表示並行: • 使用设备所實現的矢量數據型別; • 將多個任務入隊,和/或 • 將多個原生內核入隊(他們是使用一個與 OpenCL 正交的編程模型開發的)。
参考官方文档原文:
在 OpenCL 中,有兩方面的同步: • 隸屬同一作業組的作業項之間; • 隸屬同一上下文的命令隊列中的命令之間。 前者是通過作業组屏障實現的。對於同一作業組中的所有作業項來說,任意一個要想越過屏障 繼續執行,所有作業項都必須先執行這個屏障。注意,在同一作業組中,所有正在執行內核的作業 項必須都能執行到這個作業组屏障,或者都不會去執行。作業組之間沒有同步機制。 命令隊列中命令間的同步點是: • 命令隊列屏障。他保證:所有之前排隊的命令都執行完畢,並且他們對內存對象的所有更 新,在後續命令開始執行前都是可見的。他只能在隸屬同一命令隊列的命令間進行同步。 • 等在一個事件上。所有會將命令入隊的 OpenCL API 函式都會返回一個事件(用來標識這 個命令以及他所更新的內存對象)。如果某個後續命令正在等待那個事件,可以保證在其 開始執行前,可以見到對那些內存對象的所有更新。