CUDA性能:分支和共享内存

时间:2012-11-05 20:15:26

标签: cuda

我想问两个关于表现的问题。我无法创建简单的代码来说明。

问题1:非发散分支有多贵?在我的代码中,它似乎甚至超过相当于4个非fma FLOPS。请注意,我说的是BRA PTX代码,其中谓词已经计算

问题2:我一直在阅读很多关于共享内存性能的文章,而a Dr Dobbs article这样的文章甚至表明它可以和寄存器一样快(就访问而言)。在我的代码中,块内warp中的所有线程都访问相同的共享变量。我相信在这种情况下共享内存是在广播模式下访问的,不是吗?它应该以这种方式达到寄存器的性能吗?是否有任何特殊的事情需要考虑才能使其发挥作用?

编辑:我已经能够构建一些简单的代码,为我的查询提供更多洞察力

这是

#include <math.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <float.h>
#include "cuComplex.h"
#include "time.h"
#include "cuda_runtime.h"
#include <iostream>
using namespace std;

__global__ void test()
{
__shared__ int t[1024];
   int v=t[0];
    bool b=(v==-1);
    bool c=(v==-2);
    int myValue=0;
    for (int i=0;i<800;i++)
    {
#if 1
            v=i;
#else
            v=t[i];
#endif

#if 0
            if (b) {
                    printf("abs");
            }
#endif
            if (c)
            {
                    printf ("IT HAPPENED");
                    v=8;
            }
            myValue+=v;

    }
    if (myValue==1000)
            printf ("IT HAPPENED");



}
int main(int argc, char *argv[])
{
    cudaEvent_t event_start,event_stop;
    float timestamp;
float4  *data;
    // Initialise
    cudaDeviceReset();
    cudaSetDevice(0);
dim3 threadsPerBlock;
dim3 blocks;
 threadsPerBlock.x=32;
 threadsPerBlock.y=32;
 threadsPerBlock.z=1;
 blocks.x=1;
 blocks.y=1000;
 blocks.z=1;
 cudaEventCreate(&event_start);
 cudaEventCreate(&event_stop);
cudaEventRecord(event_start, 0);
test<<<blocks,threadsPerBlock,0>>>();
    cudaEventRecord(event_stop, 0);
    cudaEventSynchronize(event_stop);
    cudaEventElapsedTime(&timestamp, event_start, event_stop);
    printf("Calculated in %f", timestamp);
}

我在GTX680上运行此代码。

现在结果如下..

如果按原样运行需要5.44 ms

如果我将第一个#if条件更改为0(这将启用从共享内存中读取),则需要6.02ms ..不多但对我来说还不够

如果我启用第二个#if条件(插入一个永远不会评估为true的分支),它将以9.647040ms运行。性能降低非常大。原因是什么,可以做些什么?

我还略微更改了代码以进一步检查共享内存

而不是

__shared__ int t[1024]

我做了

__shared__ int2 t[1024] 

无论我访问t [],只需访问t []。x。在性能进一步下降到10ms ..(另外400微秒)为什么会发生这种情况?

此致 丹尼尔

2 个答案:

答案 0 :(得分:1)

您确定您的内核是计算绑定还是内存绑定?如果您的内核是计算绑定的,那么您的第一个问题将是最相关的,而如果您的内核受内存限制,则第二个问题最相关。你可能会得到令人困惑或难以复制的结果,如果你假设一个,而它是另一个。

(1)我不认为分支机构的成本已经公布。您可能需要通过实验确定您的架构。 CUDA编程指南确实说没有“分支预测,也没有推测执行。”

(2)您是正确的,当您从warp中的所有线程访问共享内存中的单个32位值时,该值将被广播。但我的猜测是,只要不发生任何银行冲突,从所有线程访问单个值与访问任何值组合的成本相同。因此,您最终会得到共享内存中单次提取的延迟。我认为延迟的周期数已经公布。它足够短,通常很容易隐藏。

答案 1 :(得分:0)

  • 您需要记住编译器是高度优化的。因此,如果你注释掉分支,你也可以消除对条件的评估,不管你是否将它留在源代码中。因此,对于您的示例,四条指令的差异似乎非常合理:

    1. 加载-1
    2. v与其进行比较(并将结果存储在b),
    3. 测试b
    4. 分支,
    5. 虽然我没有编译你的例子并查看代码(这是你应该做的 - 在你的二进制文件上运行cuobjdump -sass并查看机器代码中的实际差异。

    6. 仅使用.x的{​​{1}}组件更改共享内存中的布局,以便您从银行冲突免费访问到双向银行冲突,从而导致轻微你的例子进一步放缓。 IIRC共享内存访问的延迟大约为30个周期,通常很容易被其他线程隐藏(正如Roger已经提到的那样)。