CUDA并行化嵌套for循环

Nik*_*a K 6 c++ for-loop cuda gpu nested

我是CUDA的新手.我试图并行化以下代码.现在它坐在内核上但根本没有使用线程,因此很慢.我尝试使用这个答案但到目前为止无济于事.

内核应该生成前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++;
    }
}
}
Run Code Online (Sandbox Code Playgroud)

我目前的,初步的,并且肯定是错误的尝试并行化这个看起来如下:

//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++;
}
Run Code Online (Sandbox Code Playgroud)

但是这段代码使用没有意义的数据填充数组.此外,许多值都是零.在此先感谢您的任何帮助,我们对此表示赞赏.

dre*_*ash 4

您的代码中存在一些问题,例如:

int num = i + 2;
Run Code Online (Sandbox Code Playgroud)

该表达式分配给thread 0交互 2、thread 1迭代 3,依此类推。问题在于线程将计算的下一次迭代基于表达式num++;。因此,thread 0接下来将计算迭代 3,该迭代已经由 计算thread 1。因此,导致冗余计算。此外,我认为对于这个问题,仅使用一维而不是二维会更容易(x,y)。因此,考虑到这一点,您必须更改num++为:

num += blockDim.x * gridDim.x;
Run Code Online (Sandbox Code Playgroud)

另一个问题是您没有考虑到变量counter必须在线程之间共享。否则,每个线程将尝试查找“n”个素数,并且所有这些素数都将填充整个数组。所以你必须更改int counter = 0;为共享或全局变量。让我们使用一个全局变量,以便它可以在所有块的所有线程中可见。我们可以使用数组的零位置device_primes来保存变量counter

您还必须初始化该值。让我们将这项工作仅分配给一个线程,即 id = 0 的线程,因此:

if (thread_id == 0) device_primes[0] = 1;
Run Code Online (Sandbox Code Playgroud)

然而,这个变量是全局的,它会被所有线程写入。因此,我们必须保证所有线程在写入该全局变量之前都会看到该变量counter为 1(device_primes带质数的第一个位置,零代表counter),因此您还必须在最后添加一个屏障,因此:

if (thread_id == 0) 
    device_primes[0] = 1;
__syncthreads()
Run Code Online (Sandbox Code Playgroud)

因此,一种可能的解决方案(尽管效率低下):

__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       
    }
}
Run Code Online (Sandbox Code Playgroud)

下面这行atomicAdd(&device_primes[0], 1);基本上会执行device_primes[0]++;。我们使用原子操作是因为变量counter是全局的并且我们需要保证互斥。请注意,您可能必须使用flag -arch sm_20.

优化:就代码而言,最好使用较少/不同步的方法。此外,通过考虑素数的一些属性也可以减少计算量,如http://en.wikipedia.org/wiki/Sieve_of_Eratosthenes中所示。