CUDA点积给出错误的结果

CUDA点积给出错误的结果,cuda,gpu,Cuda,Gpu,我用CUDA写了一个点积代码来计算两个双向量的点积。内核由N个线程调用(N让我们看看三行代码中的两行: sum[0] = sum[0]+a[tid] * b[tid]; //every thread write to the sum[0] __syncthreads(); 第一行包含内存争用。块中的每个线程都将同时尝试写入求和[0]。cuda执行模型中没有任何东西可以阻止这种情况发生。没有自动序列化或内存保护可以阻止这种行为 第二行是指令屏障。这意味着每个线程扭曲都将被

我用CUDA写了一个点积代码来计算两个双向量的点积。内核由N个线程调用(N让我们看看三行代码中的两行:

    sum[0] = sum[0]+a[tid] * b[tid];  //every thread write to the sum[0] 
    __syncthreads();  
第一行包含内存争用。块中的每个线程都将同时尝试写入求和[0]。cuda执行模型中没有任何东西可以阻止这种情况发生。没有自动序列化或内存保护可以阻止这种行为

第二行是指令屏障。这意味着每个线程扭曲都将被阻止,直到每个线程扭曲都达到屏障为止。它对以前的指令没有任何影响,对内存一致性或代码发出的任何内存事务的行为没有任何影响

您编写的代码已不可逆转地被破坏。执行此类操作的规范方法是通过并行简化。可以通过多种不同的方法来实现。这可能是GPU中描述和记录最多的并行算法。如果您安装了CUDA工具包,您已经拥有了完整的king示例和一篇全面的论文描述了使用共享内存实现的算法。我建议您研究一下


你可以看到一个(几乎)我建议您也学习使用共享内存的点积的工作实现。您还可以在库中找到并行块缩减的最佳实现,例如

我编写了两个版本的点积过程。第一个版本使用atomiAdd函数,第二个版本为每个块分配一个共享变量K 计算时间分别为3.33 ms和0.19 ms,而还原点积和单螺纹点积的计算时间分别为0.17 ms和411.43 ms

GPU Device 0: "GeForce GTX 1070 Ti" with compute capability 6.1
2000000flow array allocated 2147483647
 naive elapsedTimeIn Ms 411.437042 Ms
sum is 6.2831853071e+06

thread atomic add elapsedTimeIn Ms 3.3371520042 Ms
sum is 6.2831853071e+06

 cache reduction elapsedTimeIn Ms 0.1764480025 Ms
sum is 6.2831853072e+06

 cache atomicadd elapsedTimeIn Ms 0.1914239973 Ms
sum is 6.2831853072e+06

__global__ void dotatomicAdd(double* a, double *b,double *sum, int N)
{
    int tid = blockDim.x * blockIdx.x + threadIdx.x;
    while (tid < N){
        double t=a[tid] * b[tid];
        atomicAdd(sum,t);
        tid+=blockDim.x * gridDim.x;
    }
}


__global__ void dotcache(double* a, double *b,double *c, int N)
{

    __shared__ double cache;
    int tid = threadIdx.x + blockIdx.x * blockDim.x;
    int cacheIndex = threadIdx.x;
    double  temp = 0.0;
    cache=0.0;
    __syncthreads();

    while (tid < N) {
        temp += a[tid] * b[tid];
        tid += blockDim.x * gridDim.x;
    }
    atomicAdd(&cache,temp);
    __syncthreads();

    if (cacheIndex == 0) c[blockIdx.x] = cache;
}
GPU设备0:“GeForce GTX 1070 Ti”,具有计算能力6.1
2000000分配的流量阵列2147483647
原始elapsedTimeIn Ms 411.437042毫秒
总和为6.2831853071e+06
线程原子添加elapsedTimeIn毫秒3.3371520042毫秒
总和为6.2831853071e+06
缓存缩减时间以毫秒为单位0.1764480025毫秒
总和为6.2831853072e+06
缓存原子添加elapsedTimeIn毫秒0.1914239973毫秒
总和为6.2831853072e+06
__全局\uuuvoid dotatomicAdd(双*a,双*b,双*sum,int N)
{
int tid=blockDim.x*blockIdx.x+threadIdx.x;
而(tid
“每个线程都写入求和[0]”是问题所在。我使用命令“\uu syncthreads()”来避免这些线程之间的冲突。syncthreads不是这样做的。无法避免代码中的内存竞争。要么使用原子内存事务,要么使用共享内存缩减。我真的很困惑。\uu syncthreads()用于块中的所有线程。为什么不能在这种情况下使用?标记的副本有一个点积内核的工作版本。仔细研究它。当您了解它的工作原理时,您将了解您的版本不工作的原因。您应该知道,双精度原子操作仅在计算能力上受支持6.x和更新的硬件,因此此代码在野外的大多数CUDA硬件上都不起作用。同样对于Bendchmark,向量的大小和数量是多少?对于您在原始问题中显示的1024个元素的情况,我使用的参考块点需要4微秒亲爱的先生:我在1070Ti平台上使用CUDA-9.2。它支持atomiAdd(双精度*,双精度)。GPU设备0:“GeForce GTX 1070 Ti”,计算能力6.1 2000000分配的流量阵列
GPU Device 0: "GeForce GTX 1070 Ti" with compute capability 6.1
2000000flow array allocated 2147483647
 naive elapsedTimeIn Ms 411.437042 Ms
sum is 6.2831853071e+06

thread atomic add elapsedTimeIn Ms 3.3371520042 Ms
sum is 6.2831853071e+06

 cache reduction elapsedTimeIn Ms 0.1764480025 Ms
sum is 6.2831853072e+06

 cache atomicadd elapsedTimeIn Ms 0.1914239973 Ms
sum is 6.2831853072e+06

__global__ void dotatomicAdd(double* a, double *b,double *sum, int N)
{
    int tid = blockDim.x * blockIdx.x + threadIdx.x;
    while (tid < N){
        double t=a[tid] * b[tid];
        atomicAdd(sum,t);
        tid+=blockDim.x * gridDim.x;
    }
}


__global__ void dotcache(double* a, double *b,double *c, int N)
{

    __shared__ double cache;
    int tid = threadIdx.x + blockIdx.x * blockDim.x;
    int cacheIndex = threadIdx.x;
    double  temp = 0.0;
    cache=0.0;
    __syncthreads();

    while (tid < N) {
        temp += a[tid] * b[tid];
        tid += blockDim.x * gridDim.x;
    }
    atomicAdd(&cache,temp);
    __syncthreads();

    if (cacheIndex == 0) c[blockIdx.x] = cache;
}