OpenCL学习笔记(11)--OpenCL设备模型

1.Opencl设备模型:__global被称为地址空间修饰符,这是因为它所修饰的任何指针都会被保存在全局地址空间之中,而这个地址空间也被称为全局内存。每个引用内存的内核参数都必须有一个地址空间修饰符。

2.OpenCL设备模型提供了四种地址空间:
(1)全局内存–保存整个设备的数据,既可读,也可写;
(2)常数内存–和全局内存相似,但是只可读;
(3)局部内存–保存工作组中工作项的数据;
(4)私有内存–保存各个工作项的数据。
有些设备为常数提供了专门的内存,但多数情况下,常数内存是和全局内存一样的内存区域,因此,这两种内存也是经常被放在一起讨论。
工作项和工作组的数量是没有限制的,但如果一个设备只含有M个计算单元,N个工作项/工作组,那么任何时候,最多只能有MN个工作项来执行内核。

3.通过设置参数CL_DEVICE_GLOBAL_MEM_SIZE,CL_DEVICE_GLOBAL_MEM_CACHE_SIZE,CL_DEVICE_MAX_CONSTANT_BUFFER_SIZE,或CL_DEVICE_LOCAL_MEM_SIZE,调用函数clGetDeviceInfo的方式来确定设备的地址空间。
(1)__global–参数的数据被保存在全局内存中;
(2)__constant–参数的数据被保存在全局、只读内存(如果有的话)中;
(3)local–参数的数据被保存在局部内存中;
(4)__private__参数的数据被保存在私有内存(默认的)中。
例如,如果两个指针指向的不同地址空间中的数据,就不可以在他们之间进行内存转换。

4.(__global)__global char16或者__global uchar16.__global限定符可以用于所有的内核参数,并不仅仅是指针参数。除了参数之外,__global还可以限定内核之中所声明的指针变量,在一个内核中,__global限定符只能用于修饰指针变量。

(__constant)内核参数以及内核中声明的变量都可以被冠以__constant限定符。字符串常量以常数的形式保存,而任何修改常数的行为都会报错。__constant限定符让数据对每个执行内核的工作项都可用。常数数据对整个程序,(而不仅仅只是为单个内核)是全局可见的。所以在使用前必须要先初始化。

(__local)如果数据只需要在同一个工作组的不同工作项之间进行共享,就应该使用__local限定符来声明。这种类型的数据只会针对处理内核的各个工作组分配一次,然后在工作组处理结束之后释放内存。__local限定符也可以用内核参数以及内核中声明的变量,但是不管是主机还是设备,都不能直接初始化内核中的局部变量。例如,下面一行内核函数中的代码就会编译报错:
__local float x = 4.0;
解决方法如下:
__local float x ;
x = 4.0 ;

(__private)如果内核参数或声明变量没有任何地址空间限定符来修饰,它将会被保存在私有内存中。这其中包括所有非内核函数的参数和变量。私有数据是面向执行内核的每个工作项进行分配的。如果指针变量没有限定符,他就会被设置指向私有内存,但image2d_t和image3d_t型指针会一直指向全局内存。

5.内存对齐;如果float3型量包含12个字节,这个向量会按16字节来对齐保存,因为16是大于或等于12 的2的最小幂数,你也可以通过aligned属性来控制数据对齐,但只能是在数据声明时使用。aligned关键字之前要冠以__attribute__,例子:
short x attribute((align(4)));
这行代码表示的是一般 按2字节对齐的 x,会改为按4字节对齐。对齐因子必须是2的幂数。

6.局部和私有内核参数:一般内核参数是用__global限定符,然后以内存对象的形式从主机上输出。但是并不一定需要用内存对象来构建内核函数。通过正确配置clSetKernelArg函数,来配置设备的局部和私有空间中的参数,
clSetKernelArg(cl_kernel kernel, cl_uint index, size_t size, const void*value)
最后一个参数指向的是数据,这些数据会被以内核参数的形式传输给设备。一般代码都是指针指向内存对象,而与此同时的内核参数也被声明为__global或__constant.全局和常数空间中的数据都很容易使用,但是和局部或私有内存的数据相比,内存宽带会更小一些。

7.局部参数:为了编程配置整个过程,需要将clSetKernelArg函数的最后一个参数设为NULL,
最好是先将数据从全局内存读到局部内存中,然后在局部内存中进行处理。在工作项处理完数据之后,就可以将结果写到全局内存中,然后再将结果传输回主机。可以有多个工作项处理相同的数据,以此来提升运算性能。

8.私有参数:私有参数只能被单个工作项访问,它的内存访问比局部内存访问还要快,但是私有内存的空间也比局部和全局内存空间要小。和局部数据不同,内核的私有数据可以由主机应用程序来初始化。为了编程配置私有数据,主机需要将clSetKernelArg函数的最后一个参数设为基本数据类型,例如int*,float*,char*.

9.OpenCL的各种数据类型总结:
(1)假设你需要每个工作项都能够访问int型变量num_iterationg所在的地址空间,并且希望其初始值为4,对应的主机应用程序中的操作如下:
int num_iterationg = 4;
clSetKernelArg(kernel, 0, sizeof(num_interation), &num_iterations);
内核函数:
__kernel void proc_data(int num_iters, …){
}
注意。其一,它并没有像__global或__local那样的地址空间限定符,换言之,Num_iters将会被保存在设备的私有地址空间中,每个工作项都会有各自不同的变量副本。其二,和其他我们所见到过的内核参数不同,num_iters并不是一个指针。私有内核参数并不是引用—他们必须是像int和float那样的基本数据类型。
(2)私有数据是值传递的方式。
私有内核参数必须是基本数据类型,但不一定需要是标量类型。
float nums[4] = {0.0f, 1.0f, 2.0f, 3.0f };
clSetKernelArg{kernel, 0, sizeof(nums), nums};
内核不能以4元素数组的形式访问私有数据,因为私有参数不能是指针,但是数据可以以float4型向量的形式来访问:
__kernel void proc_data(float4 values, …){
}

(3)OpenCL,每个内核都必须被冠以__kernel标识符,函数必须返回void.其次,OpenCL并不支持所有的C数据类型,而且还提供了一些新的数据类型。最后OpenCL的设备模型让你可以控制内核数据的地址分配。

(4)OpenCL的标量数据类型并没有多少不同,你还是可以用char,short,int, float和long等数据类型来编程。

(5)OpenCL的向量类型是最容易使用的。存有四个float型量的数据类型就被称为float4.初始化向量就跟初始化数组一样,而且访问向量的各个分量也很简单–你可以使用数字(.so, .s1, .s2… .sF),字符(x,y,z,和w)以及使用返回一半向量分量的后缀名(.hi, .lo , .even, 和 .odd)等不同方式来访问。

(6)全局地址空间保存的是整个设备的数据,常数地址空间保存的是只读数据,局部地址空间保存的是特定工作组的数据,私有地址空间保存的值特定工作项的数据,OpenCL提供了不同的限定符,来修饰变量和函数参数,指明其所在的内存位置。

(7)如果在调用clSetKernelArg函数时,指针指向内存对象,那么,对应的内核函数参数必须是声明为__global或__constant类型的指针。如果调用clSetKernelArg函数时,指针被设为NULL,对应的内核参数就必须声明为__local类型的指针。如果调用clSetKernelArg函数时,指针指向的是基本类型的数据,内核参数就不会是一个指针,也不需要有任何地址修饰符。

发布了19 篇原创文章 · 获赞 20 · 访问量 566

猜你喜欢

转载自blog.csdn.net/INFINALGEORGE/article/details/105145808