原子函数真的使变量在CUDA中变得不稳定吗?

时间:2014-10-21 14:14:25

标签: cuda atomic

我编写了一个非常简单的代码,请求线程0更新全局变量,而其他线程继续读取该变量。但是我发现其他线程并没有真正获得该值。

  

代码在这里,很简单。任何人都可以给我任何建议如何解决它?   非常感谢

__global__ void addKernel(int *c)
{
int i = threadIdx.x;
int j = 0;
if (i == 0)
{
    while(*c < 2000){
        int temp = *c;
        printf("*c = %d\n",*c);
        atomicCAS(c,temp, temp+1);
    }       
}else{
    while(*c < 1000)
    {
        j++;
    }
}

}

1 个答案:

答案 0 :(得分:2)

我想做一个类比:想象一下,原子操作是互斥体:对于一个定义良好的程序,访问共享资源的两个线程必须两个同意使用互斥锁专门访问资源。如果其中一个线程在没有首先持有互斥锁的情况下访问资源,则结果是未定义的。

对于原子论也是如此:如果您决定将内存中的特定位置视为原子变量,那么访问该位置的所有线程都应该同意并将其视为具有意义的程序。您应该通过原子加载和存储来操作它,而不是非原子操作和原子操作的组合。

换句话说,这个:

atomicCAS(c,temp, temp+1);

包含原子加载比较存储。生成的指令将一直向下到全局内存 load c,进行比较,然后一直向下到全局内存到 store 新价值。

但是这个:

while(*c < 2000)

无论如何都不是原子的。编译器(和硬件)不知道c可能已被另一个线程修改过。因此,它不会一直向下到全局内存,而只是从最快的可用缓存中读取。可能编译器甚至会将变量放在寄存器中,因为在当前线程中没有看到其他人修改它

你想要的是(想象的):

while (atomicLoad(c) < 2000)

但据我所知,在撰写本文时,CUDA中没有这样的结构。

在这方面,volatile限定符可能有帮助:它告诉编译器不优化变量,并将其视为“可从外部源修改”。这将触发每次读取变量的负载,但我不确定此负载是否会绕过所有缓存。在实践中,它可能有效,但理论上我认为你不应该依赖它。此外,这还将禁用对该变量的任何优化(例如常量传播或将变量提升到寄存器以获得更好的性能)。

你可能想尝试下面的黑客攻击(我还没试过):

while(atomicAdd(c, 0) < 2000)

这将发出从全局内存加载的原子指令,因此应该看到c的最新值。但是,它还引入了一个(在这种情况下无用)原子库。