我启动了一个非常简单的内核<<<<< 1,512>>>在CUDA Fermi GPU上。
__global__ void kernel(){
int x1,x2;
x1=5;
x2=1;
for (int k=0;k<=1000000;k++)
{
x1+=x2;
}
}
内核非常简单,它可以添加10 ^ 6个并且不会将任何内容传回全局内存。结果是正确的,即在循环x1(在其所有512个线程实例中)包含10 ^ 6 + 5
之后我正在尝试测量内核的执行时间。同时使用visual studio parallel nsight和nvvp。 Nsight测量2.5微秒,nvvp测量4微秒。
问题如下:我可能会大大增加循环的大小,例如10 ^ 8,时间保持不变。如果我减少循环大小相同。为什么会这样?
请注意,如果我在循环中使用共享内存或全局内存,则测量结果会反映正在执行的工作(即存在相称性)。
答案 0 :(得分:3)
如上所述,CUDA编译器优化在删除死代码方面非常积极。因为x2
不参与写入内存的值,所以可以删除它和循环。编译器还会预先计算在编译时可以推导出的任何结果,因此如果编译器知道循环中的所有常量,它可以计算最终结果并用常量替换它。
要解决这两个问题,请重写您的代码:
__global__
void kernel(int *out, int x0, bool flag)
{
int x1 = x0, x2 = 1;
for (int k=0; k<=1000000; k++) {
x1+=x2;
}
if (flag) out[threadIdx.x + blockIdx.x*blockDim.x] = x1;
}
然后像这样运行:
kernel<<<1,512>>>((int *)0, 5, false);
通过将x1
的初始值作为参数传递给内核,可以确保循环结果不可用于编译器。该标志使内存存储有条件,然后内存存储使整个计算不安全删除。只要在运行时将标志设置为false,就不会执行存储,因此不会影响循环的时间。
答案 1 :(得分:2)
因为编译器消除了死路径。你的代码实际上并没有做任何事情。看看装配。
如果你实际上看到了这个值,那么编译器可能刚刚优化了循环,因为它可以在编译期间知道该值。
当您将寄存器内容写出到共享内存时,编译器无法保证不会使用该结果,因此实际将计算该值。换句话说,您计算的值必须最终在某处使用或写入内存,否则其计算将被丢弃。