利用预取内在机制改进CUDA SDK matrixMul

利用预取内在机制改进CUDA SDK matrixMul,cuda,gpgpu,prefetch,Cuda,Gpgpu,Prefetch,以下是CUDA SDK(2.3)matrixMultiply内核的一部分: for (int a = aBegin, b = bBegin; a <= aEnd; a += aStep, b += bStep) { __shared__ float As[BLOCK_SIZE][BLOCK_SIZE]; __shared__ float Bs[BLOCK_SIZE][BLOCK_SIZE]; int XI=wA * ty +

以下是CUDA SDK(2.3)matrixMultiply内核的一部分:

for (int a = aBegin, b = bBegin;
         a <= aEnd;
         a += aStep, b += bStep) {

    __shared__ float As[BLOCK_SIZE][BLOCK_SIZE];
    __shared__ float Bs[BLOCK_SIZE][BLOCK_SIZE];

    int XI=wA * ty + tx;
    int XII=wB * ty + tx;
    ////////////////////
    // PREFETCH BLOCK //
    ////////////////////
    AS(ty, tx) = A[a + XI];
    BS(ty, tx) = B[b + XII];

    __syncthreads();

    for (int k = 0; k < BLOCK_SIZE; ++k)
        Csub += AS(ty, k) * BS(k, tx);

    __syncthreads();
}
测试后,两个版本(带或不带预取)的性能非常相似(平均运行3次):

无预取:6434.866211(毫秒)

带预取:6480.041016(毫秒)

问题:

我希望看到预取的速度有所提高,但我对结果感到困惑。任何人都有理由解释为什么这两种实现的性能非常接近?也许我执行了错误的预取

先谢谢你

更多信息:

GPU:Tesla C2050

CUDA版本:4.0

inline __device__ void prefetch_l1 (unsigned int addr)
{

  asm(" prefetch.global.L1 [ %1 ];": "=r"(addr) : "r"(addr));
}
预取(在任何体系结构上)只有在以下情况下才有好处:

  • 您有空闲的内存带宽,并且
  • 您可以在正确的时间启动预取,即在实际需要数据之前足够远的时间,以及在空闲内存带宽可用时启动预取

如果您不能满足这些标准,那么预取将毫无帮助,甚至可能弊大于利。

如果我的代码的预取请求与特斯拉C2050中的加载/存储具有相同的优先级,那么数据应该在下一次迭代请求之前及时到达。我不知道特斯拉是怎么工作的!如果优先级是公平的,根据您的指示,这里的主要标准应该是内存带宽。您是否确认您的算法是内存受限的?我认为预取只对内存受限的算法有用。在计算绑定算法中,GPU通过在其内存请求得到服务时暂停扭曲来隐藏内存延迟。因此,如果总是有翘曲准备运行,预取将不会有帮助。还请注意,与指令吞吐量相比,内存延迟非常长。费米上大约有300个时钟。在预取和使用值来隐藏这两者之间,你必须做大量的工作。@ahmad:是的,这就是我的意思,但现在我认为我的评论中有一些有缺陷的逻辑,我真的想知道预取什么时候可能有用。我说了两件我现在认为有冲突的事情。1) “我认为预取只在内存受限的算法上有用。”和2)“如果总是有扭曲准备运行,预取不会有帮助。”在(2)中,让扭曲总是准备运行意味着算法是内存受限的。因此,它与(1)相冲突。因此,Paul R的回答是,只有当你有空闲的内存带宽时,预取才有好处,这是最有意义的。@ahmad,(继续)毕竟,如果你已经用完了所有可用的内存带宽,那么在你的算法中,开始传输的地方没有多大区别。因此,底线似乎是预取无法帮助您,因为您有一个内存限制算法。一个小问题是,我想不出预取在计算绑定算法中有什么帮助!这是因为,如果所有可用的计算资源总是很忙,那么更快地引入数据是没有帮助的。@ahmad:在CPU上,即使在内存带宽未饱和的情况下,算法也会受到内存限制。这是因为CPU在等待第一个值到达时无法切换到其他工作并可能启动新的内存事务。在我看来,这是唯一一种预取有帮助的情况,而且,就其本质而言,这种情况不会发生在GPU上。
inline __device__ void prefetch_l1 (unsigned int addr)
{

  asm(" prefetch.global.L1 [ %1 ];": "=r"(addr) : "r"(addr));
}