我正在使用一个天真的素数生成函数。这段代码需要大约5.25秒来生成10k素数(device_primes [0]保存已经找到的数字素数,其余位置是素数找到的。)
_global__ void getPrimes(int *device_primes,int n)
{
int c = 0;
int thread_id = blockIdx.x * blockDim.x + threadIdx.x;
int num = thread_id+2;
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
}
}
我刚刚开始优化代码,我做了以下修改,而不是:
for (c = 2; c <= num - 1; c++)
{
if (num % c == 0) //not prime
break;
}
if (c == num) {...}
我现在有:
int prime = 1;
...
for (c = 2; c <= num - 1 && prime; c++)
{
if (num % c == 0) prime = 0; // not prime
}
if (prime) {...} // if prime
现在我可以在0.707s中产生10k。我只是想知道为什么这么简单的修改加速,是打破那么糟糕?
答案 0 :(得分:2)
正如Tony所说,不同的代码执行会导致gpu代码大幅减速,迫使某些代码以串行而非并行的方式运行。在上面代码的慢速版本中,中断的线程与继续的代码不同。
cuda c programming guide是gpu编程技术的优秀资源。 Here is what it says about control flow:
任何流控制指令(if,switch,do,for,while)都可以通过使相同warp的线程发散(即遵循不同的执行路径)来显着影响有效指令吞吐量。如果发生这种情况,则必须序列化不同的执行路径,从而增加为此warp执行的指令总数。当所有不同的执行路径都完成后,线程会聚回到相同的执行路径。
较新的nvidia硬件和cuda版本可以比旧版本更好地处理一些分支,但是最好尽可能避免分支。