我的内核中有以下(伪)代码片段:
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;
}
答案 0 :(得分:2)
好吧,如果a
和b
在循环期间是静态的,那么执行:
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要快得多。