下面是我编写的一个小程序,用于了解CUDA中的竞争条件如何,但我对输出感到惊讶。
#include<cutil.h>
#include<iostream>
__global__ void testLocal(int *something, int val[]){
*something = *something/2;
val[threadIdx.x] = *something;
}
void main(){
int *a, *c;
int r =16;
cudaMalloc((void**)&a, 4*sizeof(int));
cudaMalloc((void**)&c, sizeof(int));
cudaMemcpy(c, &r, sizeof(int) , cudaMemcpyHostToDevice);
testLocal<<<1,4>>>(c,a);
int *b = (int *)malloc(4 * sizeof(int));
cudaMemcpy(b,a, 4 * sizeof(int), cudaMemcpyDeviceToHost);
for( int j =0 ; j< 4; j++){
printf("%d\n",b[j]);
}
getchar();
}
当我发布4个线程时,我希望每个线程将*分为2次。我明白他们划分*的顺序并不固定。因此,当我试图打印这些值时,我预计其中一个打印值为8,一个为4,一个为2,一个为1.但是,所有打印值均为8.为什么这样?不应该所有的线程分开*一次。
答案 0 :(得分:1)
您正在关注的是未定义的行为。因为您正在启动具有4个线程的单个块,所以所有线程都在相同的warp中执行。这意味着
*something = *something/2;
正在由您启动的所有线程同时执行。 CUDA编程模型仅保证当来自同一warp的多个线程尝试写入相同的内存位置时,其中一个写操作将成功。它没有说明哪个线程会成功,以及warp中其他没有“获胜”的线程会发生什么。要获得您期望的行为,需要序列化内存访问 - 这只能通过在支持它们的那些体系结构上使用原子内存访问原语来实现。
答案 1 :(得分:1)
应该是一个强有力的词。你所做的是未指定的,所以它应该不做任何特定的事情。
现在,可能所做的是在相同的计算单元上运行4个线程,在同一个 warp 中。 (“SIMT”模型使每个线程作为warp的一部分运行)。由于您在something
上的操作不是原子操作,因此warp中的所有线程都以锁定步骤读写内存。所以4个线程一起读取*something
,然后将结果全部除以2,并且都尝试将8写入内存。
您期望的是,*something
可以通过原子操作来读取和写入原子,尽管CUDA中没有原子划分或多重可用。因此,如果你真的想要这个,你需要编写自己的(在atomicCAS的帮助下)。并且您将开始看到您的性能急剧下降,因为您现在正在强制并行运行的线程以串行方式运行。