我正在使用一个天真的素数生成函数.这段代码需要大约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
}
}
Run Code Online (Sandbox Code Playgroud)
我刚开始优化代码,我做了以下修改,而不是:
for (c = 2; c <= num - 1; c++)
{
if (num % c == 0) //not prime
break;
}
if (c == num) {...}
Run Code Online (Sandbox Code Playgroud)
我现在有了 :
int prime = 1;
...
for (c = 2; c <= num - 1 && prime; c++)
{
if (num % c == 0) prime = 0; // not prime
}
if (prime) {...} // if prime
Run Code Online (Sandbox Code Playgroud)
现在我可以在0.707s中产生10k.我只是想知道为什么这种简单的修改加速,是打破那么糟糕?
小智 2
正如 Tony 所建议的,不同的代码执行可能会导致 GPU 代码大幅减慢,迫使某些代码串行运行而不是并行运行。在上面代码的慢速版本中,遇到中断的线程与继续运行的代码不同。
cuda c 编程指南是GPU 编程技术的良好资源。 这是关于控制流的内容:
任何流控制指令(if、switch、do、for、while)都会导致同一 warp 的线程发散(即遵循不同的执行路径),从而显着影响有效指令吞吐量。如果发生这种情况,则必须对不同的执行路径进行序列化,从而增加为此扭曲执行的指令总数。当所有不同的执行路径完成后,线程会收敛回相同的执行路径。
较新的 nvidia 硬件和 cuda 版本可以比旧版本更好地处理某些分支,但最好还是尽可能避免分支。