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 버전을 바이너리 검색을 수행 (약 20ms의.) : 의 길이는 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에서 추력 2 진수 검색을 확인하십시오. – jmsu

답변

1

코드에서 분기가 너무 많아서 GPU에서 전체 프로세스를 직렬화하고 있습니다. 동일한 워프의 모든 스레드가 분기에서 동일한 경로를 사용하도록 작업을 분할하려고합니다. CUDA Best Practices Guide의 47 페이지를 참조하십시오.

+0

배열 2000 개를 사용했습니다. 집단. 그리고 395 번 CPU 바이너리 검색 버전을 사용했습니다. 내 PC에서는 0.000933ms 밖에 걸리지 않았습니다. 테스트를 위해 나는 커널 <<<2000,1> >>을 생성하고 절대 아무것도하지 않고 커널을 떠나게했다 : __global__ void Search() { int tid = threadIdx.x + blockIdx.x * blockDim.x; if (tid <2000) { } } 이걸 호출하면 0.034704ms 걸립니다. 이 결과에서 나는 물건을 더 빠르게 만들기 위해 CUDA를 사용하는 것이 합리적인지 궁금합니다. 아니면 내가 뭔가 잘못하고 있습니다 ... – Izidor

+0

약간의 시간이 걸리는 오버 헤드와 같은 CUDA이지만, 예를 들어 CPU에서 10 초 걸리면 GPU가 만들 수 있습니다. 오버 헤드가 0.03s라도 10 배는 더 빠릅니다. CUDA는 확실히 작동하지만 CPU에서 이미 매우 빠르면 가치가 없을 수도 있습니다. 덕분에 – jmsu

+0

. 나는 GPU에서 PC로 더 많은 작업을 전송하려고 시도 할 것이고, 이것이 기존 오버 헤드를 어떻게 든 해결할 수 있기를 희망한다. CPU와 GPU간에 메모리를 복사하지 않으면 오버 헤드가 이미 최소가 될 것이지만 분명히 그렇지 않다고 생각했습니다. 나는 또한 "추력"이진 탐색을 점검 할 것이다. – Izidor

0

커널이하는 일이 무엇인지 완전히 모르겠다. 그렇지만 검색 기준을 만족하는 색인을 하나만 찾고 있다고 가정 할 때 나는 맞는가? 그렇다면 그러한 쿼리를 구조화하고 최적화하는 방법에 대한 몇 가지 지침을 위해 CUDA와 함께 제공되는 축소 샘플을 살펴보십시오.

빠른 포인터하지만 (무엇 당신이하고있는 것은 기본적으로 쿼리에 가장 가까운 인덱스를 줄이기 위해 노력하고있다) : 읽고 매우 느린 글로벌 메모리에 기록의 당신은 엄청 많이 수행하는

. 대신 공유 메모리를 사용해보십시오.

두 번째로 __syncthreads()는 동일한 블록의 스레드 만 동기화하므로 전역 메모리에 대한 읽기/쓰기가 모든 스레드에서 동기화되지는 않습니다 (전역 메모리 쓰기로 인한 대기 시간은 실제로는 그럴 경우)

관련 문제