OpenCL并行计算4-内核编程之数据类型和设备内存
1.内核编程简介
内核函数与普通C/C++函数的区别:
1)每个内核函数的声明都是以_kernel开头的
2)每个内核函数的返回值必须是void
3)有些平台拒绝编译那些不带参数的内核函数
向量和数组的区别:
1)某种特定类型的向量只能装特定个数的元素。
2)对向量中的元素操作时是对它们同时进行的,对他们操作时是一个原子操作。
3)向量的初始化其元素必须放在圆括号内,而不是花括号内。而且须要进行类型转换。
4)访问向量元素的形式主要有3种:数字(v.0-F),字母(v.x,y,z,w)和后缀形式(v.hi,lo,odd和even)
2.OpenCL的内存模型
说明:
1)Work-item是最小的计算单元,每一个Work-Item有两个ID,一个ID用来标识与Work-group中的其它Work-item的区别(班号),另一个ID用来标识与Device中其它Work-item的区别(学号)
2)Compute Unit是固定不动的,相当于盛装Work-group的容器,一个Work-group计算完,Compute unit还可以用来计算其它的group。当我们在为一个内核分配工作项和工作组数量的时候是没有限制的。但如果一个设备只含有M个计算单元,N个工作项/工作组,那么任何时候,最多只能有MN个工作项来执行内核。
3)Global memory和Constant memory是整个设备数据的Global是可读/写,Constant仅可读不可写,这个内存区是Device和Host数据交换的桥梁,内存对象从Host会传递到此内存区,Host从Device中读取内存对象也是在这个区域。这个内存区域一般在Device上是最大的,但Work-item对它的访问速度是最慢的。类似与C++运行时的动态内存区域。
4)Local memory是存储一个Work-group中所有Work-item数据的内存区。Work-item访问此内存区域的速度是访问Global区域的100倍以上。不同Work-group的Work-item是不能相互访问Local memory的。
5)Private memory是存储单个Work-item数据的内存区,存储数据的速度最快,但一般是最小的。
6)_global限定符在修饰kernel函数的形参时既可以修饰指针,也可以修饰其他的变量,但是如果在kernel函数内部用作修饰符的话,只能修饰指针变量。
7)_constant限定符修饰的变量主要是在kernel函数的变量或参数,任何修改_constant变量的操作都将会引发错误。_constant修饰的变量是被整个program共享的,而不当当是被某个kernel所特有的。其修饰的变量使用前必须初始化。
8)_local修饰符所修饰的变量主要是被Work-group中的所有Work-Item共享访问,其所修饰的变量不可以直接被初始化。要么被host或者device初始化。这种类型的数据类型只会针对处理内核的各个工作项分配一次,然后在工作组处理结束后释放其内存(黑板会在下一个班级到来之前被搽干净)。
9)_private修饰符是在默认情况下对变量的修饰,包括所有的非内核参数和变量。
3.Local和Private内核参数
Local参数:Host如果想传递一个内存对象给Device,内核函数的接收不一定要采用cl_mem对象,我们可以通过设定clSetKernelArg(最后一个参数为null)函数来设定参数,也就是说host是不可以初始化局部内核参数的。我们可以通过参数的设定来告诉Device分配一个特定大小的Local空间给kernel参数。
Private:默认变量所加的修饰符。private变量可以通过host来设定初始值。private修饰的变量有两点要注意:
1)private变量是放在device的私有地址空间中的,而且传递给每个work-item的是一份拷贝数据,因为是值传递的方式。
2)private变量不可以是引用类型,它所修饰的变量只能是基本数据类型。如果host传递给内核的数据是内存对象,那么Host是可以读取这个修改数据的;如果传递给内核的数据是基本数据类型,那么Host是不可以读取这个修改数据的。
Global/Constant和内存对象数据的工作方式是比较容易操作的,但是如果你想让应用程序获得更高的运行性能,我们通常需要花点心思将我们需要传递的数据设置为Local和private。
一般意义的总结:如果你调用的clSetKernelArg函数设置一个指向内存对象的地址传递给device那么内核参数必须设置为_global或_constant类型的指针;如果设置clSetKernelArg的数据为NULL,那么内核参数就必须设置为_local类型的指针;如果设置clSetKernelArg的数据是基本的数据类型那么就不需要使用任何类型限定符。
小结:
如果要进行浮点型的数据运算,应当先确认你的设备支持哪些浮点类型和不支持那些,特别是double和half。对向量的操作类型也是类似的。必须了解OpenCL内核函数与普通C语言函数的区别,其次OpenCL并不支持所有的C数据类型,但是它提供了向量数据类型,其实向量数据类型就是数组类型。