非确定性CUDA C内核

时间:2017-07-12 21:29:56

标签: c cuda

我还是CUDA的初学者,我一直在尝试编写一个简单的内核来在GPU上执行并行素数筛选。最初我用C编写了我的代码,但是我想调查GPU上的速度,所以我重写了它:

41.cu

#include <stdio.h>
#include <stdlib.h>
#include <cuda.h>
#include <cuda_runtime.h>

#define B 1024
#define T 256
#define N (B*T)

#define checkCudaErrors(error) {\
    if (error != cudaSuccess) {\
        printf("CUDA Error - %s:%d: '%s'\n",__FILE__,__LINE__,cudaGetErrorString(error));\
        exit(1);\
        }\
}\

__global__ void prime_sieve(int *primes) {
    unsigned int i = threadIdx.x + blockIdx.x * blockDim.x;
    primes[i] = i;
    primes[0] = primes[1] = 0;
    if (i > 1 && i<N) { 
        for (int j=2; j<N/2; j++) {
            if (i*j < N) {
                primes[i*j] = 0;
            }
        }
    }
}

int main() {
    int *h_primes=(int*)malloc(N * sizeof(int));
    int *d_primes;
    checkCudaErrors(cudaMalloc( (void**)&d_primes, N*sizeof(int)));
    checkCudaErrors(cudaMemcpy(d_primes,h_primes,N*sizeof(int),cudaMemcpyHostToDevice));    
    prime_sieve<<<B,T>>>(d_primes);
    checkCudaErrors(cudaMemcpy(h_primes,d_primes,N*sizeof(int),cudaMemcpyDeviceToHost));
    checkCudaErrors(cudaFree(d_primes));

    int size = 0;
    int total = 0;
    for (int i=2; i<N; i++) {
        if (h_primes[i]) {
            size++;
        }
        total++;
    }
    printf("\n");
    printf("Length = %d\tPrimes = %d\n",total,size);
    free(h_primes);
    return 0;
}

我在Ubuntu 16.04(4.4.0-83-generic)上运行该程序,并使用版本8.0.61下的nvcc 41.cu -o 41.o -arch=sm_30进行编译。该程序在GeForce GTX 780 Ti上运行,但每次运行时,它总会产生不确定的结果:

Length = 262142 Primes = 49477
Length = 262142 Primes = 49486
Length = 262142 Primes = 49596
Length = 262142 Primes = 49589

没有报告任何错误。起初我认为这是竞争条件,但cuda-memcheck没有报告racecheckinitchecksynccheck的任何危险,我无法思考我的假设有任何问题。我在想这可能是一个同步问题?

这种非确定性行为仅在我增加代码中看到的块大小和线程大小时才会发生。当我尝试块大小和16的线程大小时,没有问题(据我所知)。似乎并非所有线程都有机会执行?我计划在非常大的数组大小(<10亿个整数)上运行它,但我现在卡住了。

我在这里做错了什么?

1 个答案:

答案 0 :(得分:3)

存在巨大的竞争条件

所以prime[i] > 0表示 prime ,而prime[i]=0表示复合

primes[i] = i;作为每个线程在primes上的第一次更新执行。记住这一点。

现在让我们看看线程16执行时会发生什么。它标记primes[16]=1616的所有倍数。类似于以下内容

primes[16] = primes[32] = primes[48]=....=primes[k*16]=0

想象一下线程48在线程16完成其工作之后(或者在线程16循环中j> 3时)调度线程{。}}。

线程48设置primes[48] = 48。您丢失了线程16所做的更新。

这是竞争条件。

在CUDA中编码时,应确保代码的正确性不依赖于特定的warp调度。

你应该认为执行的顺序是不确定的。