我是一个简单的CUDA内核,它将int的值递增1并将bool从true更改为false。这是我的代码。
#include <stdio.h>
__global__ void cube(int* d_var, bool* d_bool){
int idx = threadIdx.x;
//do basically nothing
__syncthreads();
*d_var = *d_var + 1;
*d_bool = false;
}
int main(int argc, char** argv){
int h_var = 1;
int* d_var;
bool h_bool = true;
bool* d_bool;
cudaMalloc((void**)&d_var, sizeof(int));
cudaMalloc((void**)&d_bool, sizeof(bool));
while(h_var < 10){
h_bool = true;
//printf("%d\n", h_bool);
//printf("%d\n", h_var);
cudaMemcpy(d_var, &h_var, sizeof(int), cudaMemcpyHostToDevice);
cudaMemcpy(d_bool, &h_bool, sizeof(bool), cudaMemcpyHostToDevice);
cube<<<10, 512>>>(d_var, d_bool);
cudaThreadSynchronize();
cudaMemcpy(&h_var, d_var, sizeof(int), cudaMemcpyDeviceToHost);
cudaMemcpy(&h_bool, d_bool, sizeof(bool), cudaMemcpyDeviceToHost);
printf("%d\n", h_var);
printf("%d\n", h_bool);
}
cudaFree(d_var);
cudaFree(d_bool);
//cudaFree(d_out);
return 0;
}
问题是代替1,输出在每一步中显示增量为2。因此输出
1
3
5
7
11
有人可以帮我理解这里发生的事情。
答案 0 :(得分:1)
你有竞争条件。与内核启动相关联的网格中的所有线程都在尝试更新相同的位置(d_var
)。您必须正确管理此访问权限,否则您将获得不可预测的结果。
为了更好地了解竞争条件,你需要意识到这样的操作:
*d_var = *d_var + 1;
由机器在multiple steps执行。当多个线程在同一位置上异步执行这些多个步骤时,结果是不可预测的。一个线程可以覆盖另一个线程刚写入的内容,结果将不一致。
__syncthreads()
doesn't do anything to manage multiple threads accessing the same location,此外__syncthreads
仅对块内的线程进行操作,而不是对网格中的所有线程进行操作。
管理同时访问的一种可能方法是使用 atomics 。 Atomics将强制同时尝试这样做的多个线程有序地访问内存位置。
您可以像这样修改内核:
__global__ void cube(int* d_var, bool* d_bool){
int idx = threadIdx.x;
//do basically nothing
__syncthreads();
atomicAdd(d_var, 1); // modify this line
*d_bool = false;
}
现在,对于每次内核启动,这将导致d_var
每个线程更新一次。因此,如果您启动单个线程(<<<1,1>>>
),则每个内核启动时您的变量应增加一个。如果你启动了5120个线程(<<<10,512>>>
),那么每个内核启动时你的变量应该增加5120个。
请注意,在这种情况下我们不需要担心d_bool
,因为唯一可能的结果是它被设置为false,并且在这种情况下即使多个线程正在执行它也能得到保证。
如果你只希望每个内核启动时将变量增加1,无论网格中有多少个线程,那么你可以修改你的内核代码以仅在一个线程上调整更新:
__global__ void cube(int* d_var, bool* d_bool){
int idx = threadIdx.x + blockDim.x*blockIdx.x; // create globally unique thread ID
//do basically nothing
__syncthreads();
if (idx == 0) // only thread 0 does the update
*d_var = *d_var + 1;
*d_bool = false; // all threads will do this
}
通过这种修改,我得到了我认为的预期结果:
$ cat t997.cu
#include <stdio.h>
__global__ void cube(int* d_var, bool* d_bool){
int idx = threadIdx.x + blockDim.x*blockIdx.x;
//do basically nothing
__syncthreads();
if (idx == 0)
*d_var = *d_var + 1;
*d_bool = false;
}
int main(int argc, char** argv){
int h_var = 1;
int* d_var;
bool h_bool = true;
bool* d_bool;
cudaMalloc((void**)&d_var, sizeof(int));
cudaMalloc((void**)&d_bool, sizeof(bool));
while(h_var < 10){
h_bool = true;
//printf("%d\n", h_bool);
//printf("%d\n", h_var);
cudaMemcpy(d_var, &h_var, sizeof(int), cudaMemcpyHostToDevice);
cudaMemcpy(d_bool, &h_bool, sizeof(bool), cudaMemcpyHostToDevice);
cube<<<10, 512>>>(d_var, d_bool);
cudaThreadSynchronize();
cudaMemcpy(&h_var, d_var, sizeof(int), cudaMemcpyDeviceToHost);
cudaMemcpy(&h_bool, d_bool, sizeof(bool), cudaMemcpyDeviceToHost);
printf("%d\n", h_var);
printf("%d\n", h_bool);
}
cudaFree(d_var);
cudaFree(d_bool);
//cudaFree(d_out);
return 0;
}
$ nvcc -o t997 t997.cu
$ ./t997
2
0
3
0
4
0
5
0
6
0
7
0
8
0
9
0
10
0
$
由于您的代码正在打印出h_bool
变量,并且打印输出从2开始,而不是1,因为您的第一个打印输出是在内核执行后,因此存在零。