OpenCL中LDS内存的性能问题

时间:2014-01-20 18:05:32

标签: performance opencl gpgpu

使用AMD Radeon HD 6850的LDS内存时出现性能问题。

我有两个内核作为N粒子模拟的一部分。每个工作单元必须基于与其他粒子的相对位置来计算作用在相应粒子上的力。有问题的内核是:

#define UNROLL_FACTOR 8
//Vernet velocity part kernel
__kernel void kernel_velocity(const float deltaTime,
                            __global const float4 *pos,
                           __global float4 *vel,
                           __global float4 *accel,
                           __local float4 *pblock,
                           const float bound)
{
    const int gid = get_global_id(0); //global id of work item
    const int id = get_local_id(0); //local id of work item within work group

    const int s_wg = get_local_size(0); //work group size
    const int n_wg = get_num_groups(0); //number of work groups

    const float4 myPos = pos[gid];
    const float4 myVel = vel[gid];
    const float4 dt = (float4)(deltaTime, deltaTime, 0.0f, 0.0f);
    float4 acc = (float4)0.0f;

    for (int jw = 0; jw < n_wg; ++jw)
    {
        pblock[id] = pos[jw * s_wg + id]; //cache a particle position; position in array: workgroup no. * size of workgroup + local id
        barrier (CLK_LOCAL_MEM_FENCE); //wait for others in the work group

        for (int i = 0; i < s_wg; )
        {
            #pragma unroll UNROLL_FACTOR
            for (int j = 0; j < UNROLL_FACTOR; ++j, ++i)
            {
                float4 r = myPos - pblock[i];

                float rSizeSquareInv = native_recip (r.x*r.x + r.y*r.y + 0.0001f);
                float rSizeSquareInvDouble = rSizeSquareInv * rSizeSquareInv;
                float rSizeSquareInvQuadr = rSizeSquareInvDouble * rSizeSquareInvDouble;
                float rSizeSquareInvHept = rSizeSquareInvQuadr * rSizeSquareInvDouble * rSizeSquareInv;

                acc += r * (2.0f * rSizeSquareInvHept - rSizeSquareInvQuadr);
            }
        }   
        barrier(CLK_LOCAL_MEM_FENCE);
    }
    acc *= 24.0f / myPos.w;

    //update velocity only
    float4 newVel = myVel + 0.5f * dt * (accel[gid] + acc);

    //write to global memory
    vel[gid] = newVel;
    accel[gid] = acc;
}

模拟在结果方面运行良好,但问题在于使用本地内存缓存粒子位置以减轻全局内存中的大量读数时的性能。实际上如果行

float4 r = myPos - pblock[i];

替换为

float4 r = myPos - pos[jw * s_wg + i];

内核运行得更快。我真的不明白,因为从全球阅读应该比从本地阅读慢得多。

而且,当行

float4 r = myPos - pblock[i];

被完全删除,r的所有后续出现都被myPos - pblock[i]替换,速度与之前相同,就像线路根本不存在一样。我在r中访问私有内存应该是最快的,但编译器会以某种方式“优化”这一行。

全局工作量为4608,本地工作量为192.它在Ubuntu 12.04中使用AMD APP SDK v2.9和Catalyst驱动程序13.12运行。

任何人都可以帮我这个吗?这是我的错还是GPU /驱动程序/ ......的问题?或者它是一个功能? : - )

2 个答案:

答案 0 :(得分:0)

我会猜测一下:

当使用float4 r = myPos - pos[jw * s_wg + i];时,编译器足够聪明地注意到在pblock[id]初始化之后不再需要放置屏障并将其删除。很可能所有这些障碍(在for循环中)都会影响你的表现,因此删除它们非常明显。

是的,但是全球访问成本太高了......所以我猜测幕后的缓存存储器得到了很好的利用。还有一个事实是你使用向量,事实上AMD Radeon HD 6850的架构使用VLIW处理器...也许它有助于更​​好地利用缓存......也许。

修改 我刚刚发现了article基准GPU / APU缓存和内存延迟。你的GPU在列表中。你可能会得到更多的答案(抱歉没有真正读过它 - 太累了)。

答案 1 :(得分:0)

经过一番挖掘后发现,该代码导致了一些LDS银行冲突。原因是AMD有32个库,长度为4个字节,但float4覆盖16个字节,因此半波前访问同一个库中的不同地址。解决方案是分别为 x y 坐标设置__local float*并单独读取它们,并将数组索引正确移位为(id + i) % s_wg。尽管如此,整体性能提升很小,很可能是由于@CaptainObvious提供的链接中描述的整体延迟(然后必须增加全局工作大小以隐藏它们)。