2011-08-16 11 views
2

私はCPUのバイナリ検索をスピードアップしようとしています。残念ながら、GPUのバージョンはCPUのバージョンよりずっと遅いです。おそらく、この問題はGPUには適していないか、何か間違っているのでしょうか?CUDAバイナリ検索の実装

CPUバージョン(約0.6ms): 長さ2000ソートされた配列を使用し、特定の値をバイナリ検索を行う

... 
Lookup (search[j], search_array, array_length, m); 
... 
int Lookup (int search, int* arr, int length, int& m) 
{  
    int l(0), r(length-1); 
    while (l <= r) 
    { 
     m = (l+r)/2;  
     if (search < arr[m]) 
     r = m-1; 
     else if (search > arr[m]) 
     l = m+1; 
     else 
     {   
     return index[m]; 
     }   
    } 
    if (arr[m] >= search) 
     return m; 
    return (m+1);  
} 

GPUバージョン(約20ミリ秒): 長さ2000ソートされた配列を使用して私は大きな配列を試している場合でも、特定の値

.... 
p_ary_search<<<16, 64>>>(search[j], array_length, dev_arr, dev_ret_val); 
.... 

__global__ void p_ary_search(int search, int array_length, int *arr, int *ret_val) 
{ 
    const int num_threads = blockDim.x * gridDim.x; 
    const int thread = blockIdx.x * blockDim.x + threadIdx.x; 
    int set_size = array_length; 

    ret_val[0] = -1; // return value 
    ret_val[1] = 0; // offset 

    while(set_size != 0) 
    { 
     // Get the offset of the array, initially set to 0 
     int offset = ret_val[1]; 

     // I think this is necessary in case a thread gets ahead, and resets offset before it's read 
     // This isn't necessary for the unit tests to pass, but I still like it here 
     __syncthreads(); 

     // Get the next index to check 
     int index_to_check = get_index_to_check(thread, num_threads, set_size, offset); 

     // If the index is outside the bounds of the array then lets not check it 
     if (index_to_check < array_length) 
     { 
     // If the next index is outside the bounds of the array, then set it to maximum array size 
     int next_index_to_check = get_index_to_check(thread + 1, num_threads, set_size, offset); 
     if (next_index_to_check >= array_length) 
     { 
      next_index_to_check = array_length - 1; 
     } 

     // If we're at the mid section of the array reset the offset to this index 
     if (search > arr[index_to_check] && (search < arr[next_index_to_check])) 
     { 
      ret_val[1] = index_to_check; 
     } 
     else if (search == arr[index_to_check]) 
     { 
      // Set the return var if we hit it 
      ret_val[0] = index_to_check; 
     } 
     } 

     // Since this is a p-ary search divide by our total threads to get the next set size 
     set_size = set_size/num_threads; 

     // Sync up so no threads jump ahead and get a bad offset 
     __syncthreads(); 
    } 
} 

用のバイナリ検索を行う、時間比は任意のより良いではありません。

+2

単純なバイナリ検索は、GPU操作にはまったく適していません。これは、並列化できないシリアル操作です。ただし、配列を小さなチャンクに分割し、それぞれをバイナリ検索することもできます。 X個のチャンクを作成し、変数にX並列スレッドを含むものを特定します。候補以外のすべてを投げ捨て、さらに細分化するなど... –

+2

推力のバイナリ検索はhttp://wiki.thrust.googlecode.com/hg/html/group__binary__search.htmlでチェックしてみるとよいでしょう – jmsu

答えて

1

コード内に分岐が多すぎるので、基本的にGPUのプロセス全体をシリアル化しています。同じワープのすべてのスレッドがブランチ内の同じパスになるように作業を分割したいとします。 CUDA Best Practices Guideの47ページを参照してください。

+0

私は2000の配列を使いました要素。私のPC上では0.000933msしかかかりませんでした。テストのために、カーネル<<<2000,1> >>を作成し、カーネルを絶対に何もしませんでした:__global__ void Search() { int tid = threadIdx.x + blockIdx.x * blockDim.x; if(tid <2000) { } }この呼び出しは0.034704 msです。この結果から、私は本当に物事をより速くするためにCUDAを使用することが理にかなっているのだろうかと思っています。あるいは、私は何か間違っているのです... – Izidor

+0

これは実際にはCUDAのようなものですが、オーバーヘッドは少し時間がかかりますが、例えばCPUで10秒かかるとGPUはそれを行うことができますオーバーヘッドが0.03秒ある場合でも10倍速くなります。 CUDAは間違いなく動作しますが、CPU上であれば既に非常に高速ですが、価値がないかもしれません。ありがとう。 – jmsu

+0

ありがとう。私はGPU上でPCからより多くの仕事を移そうとします。そして、これは何とか既存のオーバーヘッドを救済することを願っています。私は、CPUとGPUの間でメモリをコピーしなければ、オーバーヘッドはすでに最小限に抑えられていると思いますが、明らかにそうではありません。私は "推力"のバイナリ検索もチェックします。 – Izidor

0

あなたのカーネルが何をしているのかよく分かっていないのですが、あなたの検索条件を満たす索引を1つだけ探していると仮定しているのでしょうか?もしそうなら、そのようなクエリを構造化して最適化する方法についてのいくつかの指針については、CUDAに付属の削減サンプルを見てください。しかし

いくつかの簡単なポインタ(何あなたがやっていることは、本質的に、あなたのクエリに最も近い指標を削減しようとしている):

あなたが読み、信じられないほど遅いグローバルメモリへの書き込みの非常に多くを行っています。代わりに共有メモリを使用してみてください。

__syncthreads()は同じブロック内のスレッドのみを同期するため、グローバルメモリへの読み書きは必ずしもすべてのスレッドで同期されるわけではありません(グローバルメモリ書き込みのレイテンシは実際にはそうであれば)

関連する問題