我是CUDA的新手。我试图并行化以下代码。现在它坐在内核上但根本没有使用线程,因此很慢。我试图使用这个answer,但到目前为止无济于事。
内核应该生成前n个素数,将它们放入device_primes数组中,稍后从主机访问该数组。代码是正确的,在串行版本中工作正常,但我需要加快速度,也许使用共享内存。
//CUDA kernel code
__global__ void generatePrimes(int* device_primes, int n)
{
//int i = blockIdx.x * blockDim.x + threadIdx.x;
//int j = blockIdx.y * blockDim.y + threadIdx.y;
int counter = 0;
int c = 0;
for (int num = 2; counter < n; num++)
{
for (c = 2; c <= num - 1; c++)
{
if (num % c == 0) //not prime
{
break;
}
}
if (c == num) //prime
{
device_primes[counter] = num;
counter++;
}
}
}
我目前的,初步的,并且肯定是错误的尝试并行化,如下所示:
//CUDA kernel code
__global__ void generatePrimes(int* device_primes, int n)
{
int i = blockIdx.x * blockDim.x + threadIdx.x;
int j = blockIdx.y * blockDim.y + threadIdx.y;
int num = i + 2;
int c = j + 2;
int counter = 0;
if ((counter >= n) || (c > num - 1))
{
return;
}
if (num % c == 0) //not prime
{
}
if (c == num) //prime
{
device_primes[counter] = num;
counter++;
}
num++;
c++;
}
但是这段代码使用没有意义的数据填充数组。此外,许多值都是零。在此先感谢您的帮助,我们表示赞赏。
答案 0 :(得分:4)
您的代码中存在一些问题,例如:
int num = i + 2;
为线程0提供交互2,为线程1提供迭代3,依此类推。问题是线程将计算的下一次迭代是基于num ++;。所以这意味着线程0将在线程1已经完成的迭代3之后执行。因此,您将进行冗余计算。此外,我认为对于这个问题,使用2(x,y)只使用一个维度会更容易。因此,基于这个假设,您必须更改num ++:
num += blockDim.x * gridDim.x;
另一个问题是你没有考虑到必须在线程之间共享变量计数器。否则,每个线程将尝试找到'n'素数,并且所有线程将填充整个数组。所以你必须改变int counter = 0;对于共享或全局变量,我们将使用全局变量,因此它将在所有块的所有线程中可见。我们可以使用数组device_primes的位置零来保存计数器。
另外,您必须初始化此值,您将只将此作业提供给一个线程。让我们把这个工作交给id = 0的线程,所以:
if (thread_id == 0) device_primes[0] = 1;
但是由于这个变量是全局的并且将由所有线程写入,所以你必须保证所有线程在写入之前都会看到计数器是1(device_primes的第一个位置是素数,零是计数器)所以你最后还要添加一个障碍,所以:
if (thread_id == 0) device_primes[0] = 1;
__syncthreads()
这是一个可行的解决方案(效率低下):
__global__ void getPrimes(int *device_primes,int n)
{
int c = 0;
int thread_id = blockIdx.x * blockDim.x + threadIdx.x;
int num = thread_id;
if (thread_id == 0) device_primes[0] = 1;
__syncthreads();
while(device_primes[0] < n)
{
for (c = 2; c <= num - 1; c++)
{
if (num % c == 0) //not prime
{
break;
}
}
if (c == num) //prime
{
int pos = atomicAdd(&device_primes[0],1);
device_primes[pos] = num;
}
num += blockDim.x * gridDim.x; // Next number for this thread
}
}
以下行atomicAdd(&amp; device_primes [0],1);基本上会做device_primes [0] ++;但由于反击是全球性的,你必须保证相互排斥。这就是我使用这种原子操作的原因。请注意,您可能必须使用标志-arch sm_20进行编译。
<强>优化强>: 就代码而言,优选的是具有较少/不同步的方法。您还可以通过考虑http://en.wikipedia.org/wiki/Sieve_of_Eratosthenes中可以看到的素数的某些特征来减少计算次数。