我还是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
没有报告racecheck
,initcheck
或synccheck
的任何危险,我无法思考我的假设有任何问题。我在想这可能是一个同步问题?
这种非确定性行为仅在我增加代码中看到的块大小和线程大小时才会发生。当我尝试块大小和16的线程大小时,没有问题(据我所知)。似乎并非所有线程都有机会执行?我计划在非常大的数组大小(<10亿个整数)上运行它,但我现在卡住了。
我在这里做错了什么?
答案 0 :(得分:3)
所以prime[i] > 0
表示 prime ,而prime[i]=0
表示复合。
primes[i] = i;
作为每个线程在primes
上的第一次更新执行。记住这一点。
现在让我们看看线程16
执行时会发生什么。它标记primes[16]=16
和16
的所有倍数。类似于以下内容
primes[16] = primes[32] = primes[48]=....=primes[k*16]=0
想象一下线程48
在线程16
完成其工作之后(或者在线程16
循环中j> 3时)调度线程{。}}。
线程48
设置primes[48] = 48
。您丢失了线程16
所做的更新。
这是竞争条件。
在CUDA中编码时,应确保代码的正确性不依赖于特定的warp调度。
你应该认为执行的顺序是不确定的。