2013-09-26 1 views
3

나는 루프의 여러 블록 스캔을 계산하는 간단한 스캔 커널을 가지고있다. get_local_id()가 루프 내부에서 호출하는 대신 로컬 변수에 저장 될 때 성능이 다소 상승하는 것으로 나타났습니다. 그래서, 코드와이를 요약 :OpenCL의 비용 get_local_id()

__kernel void LocalScan_v0(__global const int *p_array, int n_array_size, __global int *p_scan) 
{ 
    const int n_group_offset = get_group_id(0) * SCAN_BLOCK_SIZE; 
    p_array += n_group_offset; 
    p_scan += n_group_offset; 
    // calculate group offset 

    const int li = get_local_id(0); // *** local id cached *** 
    const int gn = get_num_groups(0); 
    __local int p_workspace[SCAN_BLOCK_SIZE]; 
    for(int i = n_group_offset; i < n_array_size; i += SCAN_BLOCK_SIZE * gn) { 
     LocalScan_SingleBlock(p_array, p_scan, p_workspace, li); 

     p_array += SCAN_BLOCK_SIZE * gn; 
     p_scan += SCAN_BLOCK_SIZE * gn; 
    } 
    // process all the blocks in the array (each block size SCAN_BLOCK_SIZE) 
} 

는 GTX-780 74 GB/s의 처리량을 가지고,이 때 :

__kernel void LocalScan_v0(__global const int *p_array, int n_array_size, __global int *p_scan) 
{ 
    const int n_group_offset = get_group_id(0) * SCAN_BLOCK_SIZE; 
    p_array += n_group_offset; 
    p_scan += n_group_offset; 
    // calculate group offset 

    const int gn = get_num_groups(0); 
    __local int p_workspace[SCAN_BLOCK_SIZE]; 
    for(int i = n_group_offset; i < n_array_size; i += SCAN_BLOCK_SIZE * gn) { 
     LocalScan_SingleBlock(p_array, p_scan, p_workspace, get_local_id(0)); 
     // *** local id polled inside the loop *** 

     p_array += SCAN_BLOCK_SIZE * gn; 
     p_scan += SCAN_BLOCK_SIZE * gn; 
    } 
    // process all the blocks in the array (each block size SCAN_BLOCK_SIZE) 
} 

가 동일한 하드웨어에서 70 GB/s의를 가지고 있습니다. 유일한 차이점은 get_local_id()에 대한 호출이 루프의 내부인지 외부인지 여부입니다. LocalScan_SingleBlock()의 ​​코드는 this GPU Gems article에 설명되어 있습니다.

이제 몇 가지 질문이 제기됩니다. 나는 항상 스레드 ID가 일부 레지스터 내에 저장되어 있으며 스레드에 대한 액세스가 모든 스레드 로컬 변수만큼 빠르다는 것을 상상했습니다. 이것은 사실이 아닌 것 같습니다. 나는 항상 루프에서 함수를 호출하지 않을 오래된 "C"프로그래머가 꺼려하는 변수에 로컬 ID를 캐싱하는 습관을 가지고 있었지만 매번 동일한 값을 반환 할 것을 기대했지만, 심각하게 그것이 어떤 차이를 만들 것이라고 생각하지 않습니다.

이유에 대한 의견이 있으십니까? 나는 컴파일 된 바이너리 코드를 검사하지 않았다. 누구도 같은 경험을합니까? CUDA에서 threadIdx.x과 동일합니까? ATI 플랫폼은 어떻습니까? 이 동작이 어딘가에 설명되어 있습니까? 신속하게 CUDA Best Practices를 스캔했지만 아무것도 찾지 못했습니다.

+0

CUDA 태그를 제거하지 마십시오. 코드 자체가 CUDA에는 없지만이 문제는 NVIDIA 하드웨어에 나타나며 CUDA의 threadIdx가 구현되는 방식 및 프로그램의 runitme에 미치는 영향과 밀접하게 관련됩니다. –

답변

5

이것은 단지 추측이지만 크로노스 페이지 당

http://www.khronos.org/registry/cl/sdk/1.0/docs/man/xhtml/get_local_id.html

get_local_id()은 상수 값 (단순히이 size_t)을 반환하도록 정의되지 않는다. 즉, 컴파일러가 알고있는 한 컴파일러의 관점에서 볼 때 함수 값의 반환이 변경 될 수 있기 때문에 상수 local_id와 비교하여 특정 최적화를 수행하지 못하게 될 수 있습니다.)

+0

NVIDA에서 그런 식으로 남겨 두는 것은 정말 어리석은 일입니다. 특히 CUDA threadIdx는 변수가 아니라 함수이기 때문에 그대로 두는 것이 좋습니다. get_local_id()를 매크로로 선언하면 쉽게 해결할 수 있습니다. 또한, 어딘가에서 그것에 관해 읽을 것을 기대할 것입니다. 그럼에도 불구하고 좋은 추측. –

+0

글쎄, 그것의 최대 nvidia 최대 opencl 사양을 말하고, 문제가 컴파일러 최적화 기능을하지 않는 non-const 함수를 멀리 다음 스레드 독립적 인 가능성이 어떻게 독립적 인 하드웨어에서 표현됩니다. 또한 매크로가 일정하지 않고 상수가 아닌가? 링크의 스펙에 대한 실제 참조에 따라 구체적으로 "빌트인 함수"섹션 및 "작업 아이템 관련 함수"섹션에서 매크로로 구현하는 것이 부적절 할 수 있습니다. 그냥 짐작하다 –

+0

NVIDIA는 컴파일러를 작성하는 사람입니다. 공급 업체의 구현에 관해서는 사양이 법이 아님을 알게 될 것입니다. 내가 말한 의미는 OpenCL 컴파일러는 #define get_local_id (coord) (threadIdx.x * (~ (coord | coord >> 1) + 1) + threadIdx.y * ...) 함수처럼 보이고 평가합니다. 컴파일 타임 상수로. 그들이 그렇게 할 필요는 없지만, 아마 이미지를 그리는 것이 더 쉽습니다. –