2012-01-14 2 views
2

나는 몇 가지 예제를 통해 요소 배열을 하나의 요소로 축소하여 성공하지 못했습니다. 누군가가 이것을 NVIDIA 포럼에 올렸습니다. 부동 소수점 변수에서 정수로 변경했습니다.OpenCL : 축소 예제 및 메모리 개체 유지/cuda 코드를 openCL로 변환

__kernel void sum(__global const short *A,__global unsigned long *C,uint size, __local unsigned long *L) { 
      unsigned long sum=0; 
      for(int i=get_local_id(0);i<size;i+=get_local_size(0)) 
        sum+=A[i]; 
      L[get_local_id(0)]=sum; 

      for(uint c=get_local_size(0)/2;c>0;c/=2) 
      { 
        barrier(CLK_LOCAL_MEM_FENCE); 
        if(c>get_local_id(0)) 
          L[get_local_id(0)]+=L[get_local_id(0)+c]; 

      } 
      if(get_local_id(0)==0) 
        C[0]=L[0]; 
      barrier(CLK_LOCAL_MEM_FENCE); 
} 

이 모양이 맞습니까? 세 번째 인자 인 "크기"는 지역 노동 크기, 즉 글로벌 근로 규모라고 가정 한 것인가?

나는

clSetKernelArg(ocReduce, 0, sizeof(cl_mem), (void*) &DevA); 
clSetKernelArg(ocReduce, 1, sizeof(cl_mem), (void*) &DevC); 
clSetKernelArg(ocReduce, 2, sizeof(uint), (void*) &size); 
clSetKernelArg(ocReduce, 3, LocalWorkSize * sizeof(unsigned long), NULL); 

입력하다 첫 번째 인수

, 나는 그것을 전에 시작 커널의 출력에서 ​​유지하기 위해 노력하고,이 같은 내 인수를 설정합니다.

clRetainMemObject(DevA); 
clEnqueueNDRangeKernel(hCmdQueue[Plat-1][Dev-1], ocKernel, 1, NULL, &GlobalWorkSize, &LocalWorkSize, 0, NULL, NULL); 
//the device memory object DevA now has the data to be reduced 

clEnqueueNDRangeKernel(hCmdQueue[Plat-1][Dev-1], ocReduce, 1, NULL, &GlobalWorkSize, &LocalWorkSize, 0, NULL, NULL); 
clEnqueueReadBuffer(hCmdQueue[Plat-1][Dev-1],DevRE, CL_TRUE, 0, sizeof(unsigned long)*512,(void*) RE , 0, NULL, NULL); 

오늘 다음 cuda reduction 예제를 openCL로 변환하려고합니다.

__global__ voidreduce1(int*g_idata, int*g_odata){ 
extern __shared__ intsdata[]; 

unsigned int tid = threadIdx.x; 
unsigned int i = blockIdx.x*(blockDim.x*2) + threadIdx.x; 
sdata[tid] = g_idata[i] + g_idata[i+blockDim.x]; 
__syncthreads(); 


for(unsigned int s=blockDim.x/2; s>0; s>>=1) { 
if (tid < s) { 
sdata[tid] += sdata[tid + s]; 
} 
__syncthreads(); 
} 

// write result for this block to global mem 
if(tid == 0) g_odata[blockIdx.x] = sdata[0]; 
} 

더욱 최적화 된 (스레드 당 여러 요소가 완전히 풀리고).

http://developer.download.nvidia.com/compute/cuda/1_1/Website/projects/reduction/doc/reduction.pdf

는 OpenCL을 사용하여이 가능합니까?

그리 즐 전날 저에게이 조언을 준,

"

... n 개의 요소를 운영하고 N/16 (또는 다른 번호) 같은로 감소 감소 커널을 사용합니다. 그런 다음 당신에게 하나의 요소가 될 때까지 반복적으로 커널을 호출하십시오. 결과는 "

나는 이것을 시도하고 싶지만 정확히 어디에서 시작해야할지 모르겠다. 일하다.

답변

6

줄이기 위해 작업하는 작업 그룹이 하나 뿐인 경우 사용자가 지정한 첫 번째 축소 코드가 작동해야합니다 (예 : get_global_size(0) == get_local_size(0)). 이 경우 커널의 size 인수는 A의 요소 수 (전역 또는 로컬 작업량과 실제 상관 관계가 없음)입니다. 이것이 실행 가능한 해결책 인 반면, 감소를 수행하는 동안 대부분의 사람들이 유휴 상태로있게하는 것은 낭비 적이라고 생각합니다. 정확하게 반복 커널을 호출하는 이유입니다. 이 코드에만 약간의 수정을 가능하게 할 것이다 : 이것을 호출

__kernel void sum(__global const short *A, __global unsigned long *C, uint size, __local unsigned long *L) { 
     unsigned long sum=0; 
     for(int i=get_global_id(0); i < size; i += get_global_size(0)) 
       sum += A[i]; 
     L[get_local_id(0)]=sum; 

     for(uint c=get_local_size(0)/2;c>0;c/=2) 
     { 
       barrier(CLK_LOCAL_MEM_FENCE); 
       if(c>get_local_id(0)) 
         L[get_local_id(0)]+=L[get_local_id(0)+c]; 

     } 
     if(get_local_id(0)==0) 
       C[get_group_id(0)]=L[0]; 
     barrier(CLK_LOCAL_MEM_FENCE); 
} 

GlobalWorkSize 작은 size 다음 (예 : 4)에 의해 (반복 될 수 4*LocalWorkSize의 요인에 의해 A의 입력을 줄일 수 출력 버퍼를 다른 출력 버퍼를 사용하는 sum에 대한 다음 호출의 입력으로 사용합니다.두 번째 (이후의 모든) 반복에는 A이 필요하기 때문에 실제로는 사실이 아닙니다. 따라서 실제로 커널을 사용해야 할 것이므로 아이디어를 얻으실 수 있습니다.

cuda 감소 샘플에 대해 : 왜 당신이 그것을 변환하는 것을 귀찮게 할 것인가? 반복적으로 하드 코딩 된 크기 (size/GlobalWorkSize*LocalWorkSize)로만 줄이는 것을 제외하고는 위의 opencl 버전과 기본적으로 동일합니다.

__kernel void reduction_step(__global const unsigned long* A, __global unsigned long * C, uint size) { 
     unsigned long sum=0; 
     for(int i=start; i < size; i += stride) 
       sum += A[i]; 
     C[get_global_id(0)]= sum; 
} 

마지막 단계 전체를 들면 : 나는 두 부분으로 커널을 분할하고 마지막 반복에 대해 로컬 메모리를 사용하여 경로를 사용하고 있지만

개인적으로 나는 감소 실질적으로 동일한 방법을 사용 작업 그룹 내에서 감축하는 버전이 사용되었습니다. 물론 reduction step의 두 번째 버전이 global const short* 일 필요하며이 코드는 테스트되지 않은 코드 적응입니다 (후회할 수는 없습니다). 이 방법의 장점은 대부분의 작업을 수행하는 커널의 복잡성이 훨씬 적고 분기 분기가 많아서 wasted work의 양이 적다는 점입니다. 그래서 다른 변종보다 조금 더 빨랐어 요. 그러나 최신 컴파일러 버전이나 최신 하드웨어에 대한 결과가 없으므로 더 이상 정확하지 않을 수도 있습니다 (분기 지점이 줄어들 기 때문에 의심 스럽지만).

링크 된 문서 : opencl에서 지원하지 않는 템플릿 사용을 제외하고는 opencl에서 제안 된 최적화를 사용할 수 있습니다. 따라서 블록 크기를 하드 코드해야합니다. 물론 opencl 버전은 이미 커널마다 여러 번 추가됩니다. 위에서 언급 한 방법을 따르는 경우 로컬 메모리를 통한 축소를 실제로 해제하지 않아도됩니다. 마지막 단계에서만 수행되기 때문에 실제로는 사용하지 않아야합니다. 충분히 큰 입력에 대한 전체 계산 시간의 중요한 부분. 또한 unrolled 구현에서 약간의 번거 로움을 피할 수 있습니다. 해당 부분에 들어가는 모든 스레드가 동일한 워프에 속하기 때문에 작동합니다. 그러나 현재의 nvidia 카드 이외의 하드웨어 (미래의 nvidia 카드, amd 카드 및 cpus (비록 그것이 현재의 amd 카드와 현재의 cpu 구현을 위해 작동해야한다고 생각하기는하지만)를 실행할 때 이것은 필요하지 않습니다. 그것)), 그래서 내가 줄이기위한 속도의 절대 마지막 비트를 필요로하지 않는 한 멀리 떨어져있을 것입니다 (그리고 나서 일반 버전을 제공하고 하드웨어 나 그와 같은 것을 인식하지 못하면 스위치를 사용합니다).

+0

많은 좋은 정보입니다. 그런 멋진 답변에 다시 한 번 감사드립니다. – MVTC

+0

커널을 시작하고 리소스를 제거하는 중 오류가 발생합니다. – MVTC

+0

내가 가지고있는 크기에 가까운 곳에서 지역 주장을 설정할 수는 없다. – MVTC

1

저감 커널이 내 눈에 딱 들어 맞습니다. 축소에서 size는 입력 배열 A의 숫자 요소 여야합니다. 이 코드는 스레드 당 부분 합을 sum에 누적 한 다음 로컬 메모리 (공유 메모리) 감소를 수행하고 그 결과를 C에 저장합니다. 로컬 작업 그룹 당 하나의 부분 합계가 C이됩니다. 하나의 작업 그룹으로 커널을 두 번 호출하여 최종 답변을 얻거나 부분 결과를 호스트에 누적하십시오.