9

私は素朴な素数生成関数を使用していました。このコードは、10,000 個の素数を生成するのに約 5.25 秒かかります (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.707 秒で 10k を生成できます。なぜこの単純な変更でこのような速度が上がるのか疑問に思っていましたが、ブレークはそれほど悪いのでしょうか?

4

1 に答える 1

2

Tonyが示唆したように、発散したコード実行はgpuコードの大幅な速度低下を引き起こし、一部のコードを並列ではなく直列で実行することを余儀なくされる可能性があります。上記のコードの遅いバージョンでは、ブレークにヒットしたスレッドは、継続するコードから分岐します。

cuda cプログラミングガイドは、 GPUプログラミングテクニックの優れたリソースです。 これが制御フローについての説明です:

フロー制御命令(if、switch、do、for、while)は、同じワープのスレッドを発散させる(つまり、異なる実行パスをたどる)ことにより、有効な命令スループットに大きな影響を与える可能性があります。これが発生した場合は、さまざまな実行パスをシリアル化する必要があり、このワープに対して実行される命令の総数が増加します。すべての異なる実行パスが完了すると、スレッドは同じ実行パスに収束します。

新しいnvidiaハードウェアとcudaバージョンは、古いバージョンよりも少しうまく分岐を処理できますが、可能な限り分岐を避けるのが最善です。

于 2012-11-25T23:07:15.083 に答える