这是cuda线程的问题,记忆魔法,它返回单线程结果" 100"但是会期望9个线程结果" 900"。
#indudel <stdio.h>
#include <assert.h>
#include <cuda_runtime.h>
#include <helper_functions.h>
#include <helper_cuda.h>
__global__
void test(int in1,int*ptr){
int e = 0;
for (int i = 0; i < 100; i++){
e++;
}
*ptr +=e;
}
int main(int argc, char **argv)
{
int devID = 0;
cudaError_t error;
error = cudaGetDevice(&devID);
if (error == cudaSuccess)
{
printf("GPU Device fine\n");
}
else{
printf("GPU Device problem, aborting");
abort();
}
int* d_A;
cudaMalloc(&d_A, sizeof(int));
int res=0;
//cudaMemcpy(d_A, &res, sizeof(int), cudaMemcpyHostToDevice);
test <<<3, 3 >>>(0,d_A);
cudaDeviceSynchronize();
cudaMemcpy(&res, d_A, sizeof(int),cudaMemcpyDeviceToHost);
printf("res is : %i",res);
Sleep(10000);
return 0;
}
它返回: GPU设备很好\ n res是:100
预计它会返回更高的数字,因为3x3(块,线程),只有一个线程的结果? 做错了什么,丢失的数字在哪里?
答案 0 :(得分:1)
您不能以这种方式将总和写入全局记忆。 您必须使用atomic function来确保存储是原子的。
通常,当多个设备线程在全局内存中写入相同的值时,您必须使用atomic functions:
float atomicAdd(float * address,float val); double atomicAdd(double * 地址,双重val);
读取位于地址的32位或64位字 全局或共享内存,计算(旧+ val),并存储结果 回到同一地址的内存。这三个操作是 在一个原子事务中执行。该函数返回旧的。
__syncthreads()的吞吐量是每个时钟周期16次操作 计算能力2.x的设备,每个时钟周期128次操作 计算能力3.x的设备,每个时钟周期32次操作 计算能力的设备每个时钟周期6.0和64次操作 对于计算能力为5.x,6.1和6.2的设备。
请注意__syncthreads()可以通过强制执行来影响性能 多处理器空闲,如设备内存访问中所述。
答案 1 :(得分:1)
(改编我的另一个answer:)
您正在体验增量运算符不是原子的效果。 (C++-oriented description of what that means)。按时间顺序排列的事件是以下事件序列(不一定按照线程的顺序排列):
...(其他工作)......
块0线程0向寄存器r发出带有地址ptr的LOAD指令 块0线程1向寄存器r发出带有地址ptr的LOAD指令 ...
块2线程0向寄存器r发出带有地址ptr的LOAD指令
块0线程0完成LOAD,现在寄存器r中为0 ...
块2线程2完成LOAD,现在寄存器r中有0
块0线程0将r加100 ...
块2线程2将100添加到r
块0线程0从寄存器r发出STORE指令到地址ptr
...
块2线程2从寄存器r发出STORE指令到地址ptr
因此每个线程都看到*ptr
的初始值,即0;增加100;和商店0 + 100 = 100回来。只要所有线程都试图存储相同的假值,商店的顺序就不重要了。
您需要做的是:
__syncthreads()
或其他机制。因此,它可能首先让每个线程添加自己的两个元素;然后同步块线程;然后有更少的线程加上成对的对,等等。这是nVIDIA blog post关于在更现代的GPU架构上实现快速缩减的问题。阻止本地或warp-local和/或特定于工作组的部分结果,这需要更少/更便宜的同步,并在完成大量工作后最终将它们组合在一起。< / p>