CUDA并行化嵌套for循环

时间:2012-11-04 02:53:06

标签: c++ for-loop cuda nested gpu

我是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++;
}

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

1 个答案:

答案 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中可以看到的素数的某些特征来减少计算次数。