我想问两个关于表现的问题。我无法创建简单的代码来说明。
问题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(×tamp, 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微秒)为什么会发生这种情况?
此致 丹尼尔
答案 0 :(得分:1)
您确定您的内核是计算绑定还是内存绑定?如果您的内核是计算绑定的,那么您的第一个问题将是最相关的,而如果您的内核受内存限制,则第二个问题最相关。你可能会得到令人困惑或难以复制的结果,如果你假设一个,而它是另一个。
(1)我不认为分支机构的成本已经公布。您可能需要通过实验确定您的架构。 CUDA编程指南确实说没有“分支预测,也没有推测执行。”
(2)您是正确的,当您从warp中的所有线程访问共享内存中的单个32位值时,该值将被广播。但我的猜测是,只要不发生任何银行冲突,从所有线程访问单个值与访问任何值组合的成本相同。因此,您最终会得到共享内存中单次提取的延迟。我认为延迟的周期数已经公布。它足够短,通常很容易隐藏。
答案 1 :(得分:0)
您需要记住编译器是高度优化的。因此,如果你注释掉分支,你也可以消除对条件的评估,不管你是否将它留在源代码中。因此,对于您的示例,四条指令的差异似乎非常合理:
-1
,v
与其进行比较(并将结果存储在b
),b
,虽然我没有编译你的例子并查看代码(这是你应该做的 - 在你的二进制文件上运行cuobjdump -sass
并查看机器代码中的实际差异。
仅使用.x
的{{1}}组件更改共享内存中的布局,以便您从银行冲突免费访问到双向银行冲突,从而导致轻微你的例子进一步放缓。 IIRC共享内存访问的延迟大约为30个周期,通常很容易被其他线程隐藏(正如Roger已经提到的那样)。