kernel void
parallel_add(global float *a, global float *b, global float *result)
{
...
// The following example is an example of an illegal kernel
// declaration and will result in a compile-time error.
// The kernel function has a return type of int instead of void.
kernel int
parallel_add(global float *a, global float *b, global float *result)
{
...
}
kernel void
my_func_a(global float *src, global float *dst)
{
local float l_var[32];
...
}
kernel voidmy_func_b(global float * src, global float *dst){
my_func_a(src, dst); // implementation-defined behavior
}
为了适用于所有实现,代码进行了修改,将local变量作为参数传给kernel函数:
kernel void
my_func_a(global float *src, global float *dst, local float *l_var)
{
...
}
kernel voidmy_func_b(global float * src, global float *dst, local float *l_var){
my_func_a(src, dst, l_var);
}
// declares a pointer p in the private address space that points to
// a float object in address space global
global float *p;
// declares an array of integers in the private address space
int f[4];
// for my_func_a function we have the following arguments:
//
// src - declares a pointer in the private address space that
// points to a float object in address space constant
//
// v - allocate in the private address space
//
int
my_func_a(constant float *src, int4 v)
{
float temp; // temp is allocated in the private address space.
}
如果kernel函数的参数是一个指针,那么这个指针必须指向以下的地址空间:global,local,或者constant。如果它没有指明,会发生编译错误。这个限制不适用于非kernel函数。
kernel void my_func(int *p) // illegal because generic address space
// name for p is private.
kernel void
my_func(private int *p) // illegal because memory pointed to by
// p is allocated in private.
void
my_func(int *p) // generic address space name for p is private.
// legal as my_func is not a kernel function
void
my_func(private int *p) // legal as my_func is not a kernel function
global float4 *color; // an array of float4 elements
typedef struct {
float3 a;
int2 b[2];
} foo_t;
global foo_t *my_info; // an array of foo_t elements
void
my_func(global float4 *vA, global float4 *vB)
{
global float4 *p; // legal
global float4 a; // illegal
}
这个地址空间名用来描述在全局存储中的、可以在kernel中访问的、只读变量。这个存储区在所有执行的kernel中的所有work-group中的所有work-item的只读访问。
图像类型不能分配在常量地址空间中。下面的例子将图像定义在常量地址空间中,这是非法的,将会导致编译错误。
kernel void
my_func(constant image2d_t imgA)
{
...
}
指向常量地址空间的指针可以作为函数的参数,也可以在函数内部定义变量。
在kernel函数内部(函数作用域的最外层)的变量可以分配到常量地址空间。在program范围的变量只能被分配到常量地址空间。所有这些变量都需要被初始化,所有用于初始化的值都必须是编译是确定的常量。对这些变量进行写操作将会产生编译错误。
在program中申明的字符串也会存储在常量地址空间。
下面是一些例子。
// legal - program scope variables can be allocated only
// in the constant address space
constant float wtsA[] = { 0, 1, 2, . . . }; // program scope
// illegal - program scope variables can be allocated only
// in the constant address space
global float wtsB[] = { 0, 1, 2, . . . };
kernel void
my_func(constant float4 *vA, constant float4 *vB)
{
constant float4 *p = vA; // legal
constant float a; // illegal – not initialized
constant float b = 2.0f; // legal – initialized with a compile-time constant
p[0] = (float4)(1.0f);
// illegal – p cannot be modified
// the string "opencl version" is allocated in the
// constant address space
char *c = "opencl version";
}
这个地址空间的名字描述的是分配在local memory中的变量,它再同一个work-group中的所有work-item共享,但不能一个kernel中的多个work-group之间共享。一个work-group的所有work-item可以对其进行读写操作。
local memory非常类似于用户管理缓冲区。如果一个work-item或者一个work-group中的多个work-item从global memory中的同一个地方读取数据,使用local memory会极大地改善性能。比如对一个图像进行高斯滤波,多个work-item读取图像的重叠区域。重叠区域的大小是有滤波宽度决定的。比起多次从global memory读取多次的方法,一次从global memory中读入到local memory,然后再由多个work-item从local memory分别读取,这样会极大的改善性能。
指向局部地址空间的指针可以作为函数的参数,可以作为函数内部的变量。
在一个kernel中的申明的变量能够分配在局部地址空间中,但是有一些限制:
需要注意的是:在局部地址空间中的变量作为指针变量传给kernel函数时,或者是在kernel函数内部时,这些变量的生命周期和work-group一样。
一些例子
kernel void
my_func(global float4 *vA, local float4 *l)
{
local float4 *p;// legal
local float4 a; // legal
a = 1;
local float4 b = (float4)(0); // illegal – b cannot be initialized
if (...)
{
local float c; // illegal – must be allocated at kernel function scope
...
}
}
这个地址空间中的变量只在单个的work-item中使用,不能再work-item之间共享。在一个kernel函数中、没有被地址空间修饰词说明的变量,所有在非kernel函数中申明的变量,和所有函数参数均是在私有的地址空间中。
一个地址空间的指针只能被同一个地址空间的指针赋值。将一个地址空间中的指针转换为另一个地址空间的指针是不合法的。比如:
kernel void
my_func(global float4 *particles)
{
// legal – particle_ptr & particles are in the
// same address space
global float *particle_ptr = (global float *)particles;
// illegal – private_ptr and particle_ptr are in different
// address spaces
float *private_ptr = (float *)particle_ptr;
}
访问修饰符可以用来指明图像参数,是只读的(read_only)或者是只写(write_only)的。这是因为当前GPUs的一个限制:不允许一个kernel中对同一图像的读和写。因为读取的图像是被缓冲到一个纹理缓冲区中,而对图像的写操作并不更新这个纹理缓冲区。
下面的例子中imageA是只读的2D图像对象,而imageB是只写的图像对象。
kernel void
my_func(read_only image2d_t imageA, write_only image2d_t imageB)
{
...
}
用read_only修饰符修饰的图像可以被内建的读取图像的函数使用,但是不能被内建的写图像的函数使用。类似的,用write_only修饰的图像只能写而不能读。可以参见下面的例子:
kernel void
my_func(read_only image2d_t imageA, write_only image2d_t imageB, sampler_t sampler)
{
float4 clr;
float2 coords;
clr = read_imagef(imageA, sampler, coords); // legal
clr = read_imagef(imageB, sampler, coords); // illegal
write_imagef(imageA, coords, &clr); // illegal
write_imagef(imageB, coords, &clr); // legal
}
imageA是只读的独享,它不能传给write_imagef。类似的,imageB被申明为只写的图像,不能作为参数穿给read_imagef。
读写修饰符read_write是被保留的。使用这个操作符会导致编译时错误。