减少OCL代码中的间接性

时间:2016-06-22 09:58:22

标签: c++ opencl

我的内核中有以下(伪)代码片段:

kernel void krnl(global X* restrict x){ 
    for(int i = 0; i < 100; i++){
        x[a].y[b].z[i] * x[a].y[i].n;
    }
}

我正在使用Xilinx FPGA器件,所以有些东西可能会有所不同。该代码适用于GPU / CPU,但不适用于FPGA。只是一些细节,如果它可以帮助任何人。

x,y,z和n如下:

typedef struct Y
{
    float n;
    float z[MAXSIZE]
} Y;

typedef struct X
{
    int i;
    Y y[MAXSIZE];
} X;

'a'和'b'只是int的向量。

我需要减少间接的数量,理想情况下只需更改内核(或添加内联函数 - 我已经尝试了这一点,但似乎数据没有传递给内核)。

具体来说,我需要代码是这样的:

for(int i = 0; i < 100; i++){
    V.z[i] * K[i].n;
}

2 个答案:

答案 0 :(得分:2)

好吧,如果ab在循环期间是静态的,那么执行:

kernel void krnl(global X* restrict x){ 
    const float* fast_l = x[a].y[b].z;
    const Y*     fast_r = x[a].y;
    for(int i = 0; i < 100; i++){
        fast_l[i] * fast_r[i].n;
    }
}

编译器通常不缓存全局读取和写入,因为全局数据被认为是高度不稳定的(所有工作项都接受)。手动缓存通常有助于解决这些问题。

然而,一个聪明的编译器应该能够猜测所有间接只是一个简单的指针偏移。在这种情况下,我不认为你会有任何好处。例如:

kernel void krnl(global X* restrict x){ 
    const float* off_l = ((float *)x)+sizeof(X)*a+sizeof(Y)*b+sizeof(int);
    const float* off_r = ((float *)x)+sizeof(X)*a;
    for(int i = 0; i < 100; i++){
        *(off_l+i)  *  *(off_r+sizeof(Y)*i);
    }
}

答案 1 :(得分:1)

你需要更少的间接来减少内存操作?

对于gpu来说这应该更好:

typedef struct X
{
    Y y[MAXSIZE];
    int i;
} X;

而不是

typedef struct X
{
    int i;
    Y y[MAXSIZE];
} X;

因为每个项目可能需要较少的内存读取操作,因为原始struct中的第一个读取操作效率较低,而后一个struct可以在全效率操作下执行。

如果有效,请:

typedef struct Y
{
    float z[MAXSIZE];
    float n;
} Y;

应该更快,特别是当MAXSIZE为偶数时。

将Y从i中分离为两个不同的数组而不是Y + i的对象数组,对于gpu来说会更快。 Y和z中的n相同。纯粹的原生元素数组更快,特别是当需要其中一个字段而其他字段不需要时。

在每个结构的末尾添加虚拟浮点数/整数也可能会改变性能。

最佳性能需要线程级并行性和来自内存的连续读取,而面向对象方法提供可读性,可维护性和可升级性但不具备可移植性,因为某些硬件存在对齐问题。为什么只需要z [i]从存储器中加载整个浮点数z [MAXSIZE]?因为它在一个物体中。如果它是纯数组,则只需要1个索引操作来获得z [i]。加载一个对象字段需要在内存中跳过MAXSIZE步,即使它只加载一个浮点数,但你选择的纯数组版本会使它以1的大小步进并达到最佳速度。

z的示例数组:

z[0]:  1st thread's z[0]
z[1]:  2nd thread's z[0]
z[2]:  3rd thread's z[0]
....
z[n]:  1st thread's z[1]
z[n+1]:  2nd thread's z[1]
z[n+3]:  3rd thread's z[1]
....

所以

的每一步
for(int i = 0; i < 100; i++)
对于所有z,gpu以非黑洞的方式访问内存,这比面向对象的版本imho要快得多。