2010-06-19 2 views
1

OpenCL에 문제가있어 다른 사람이 그 원인에 대해 힌트를 얻길 바랍니다. 다음은 문제가되는 프로그램의 버전입니다. 나는 크기가 4000 인 입력 int 배열을 가지고있다. 나의 커널에서는 스캔을하고있다. 분명히, 이것을 병렬로 처리하는 좋은 방법이 있지만, 문제를 재현하기 위해 하나의 스레드 만 전체 계산을 수행하고 있습니다. 주사하기 전에, 입력 (result_mask)는 단지 갖고 예상 결과는 초기 결과 마스크 5 개의 그러나 0이 아닌 다른 아무것도 값을 한 소자의 수있을 것이다 0 또는 1OpenCL의 문제점

__kernel void 
sel_a(__global db_tuple * input, 
     __global int * result_mask, 
     __global int * result_count, 
     const unsigned int max_id) 
{ 
// update mask based on input in parallel 

mem_fence(CLK_GLOBAL_MEM_FENCE); 

if(gid == 0) 
{ 
    int i, c = 0; 
    for(i = 0; i < max_id; i++) 
    { 
     if(result_mask[i]!=0) 
     { 
      c++; 
      result_mask[i] = 5; 
     } 
     else 
     { 
      result_mask[i] = 5; 
     } 
    } 
    *result_count = c; 
} 
} 

값. 그러나 그렇지 않습니다. 출력은 다음과 같습니다.

... 
5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 
5 5 5 5 5 5 5 5 5 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 1 0 0 0 1 0 0 0 1 0 0 0 0 0 0 0 0 0 0 
0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 5 5 5 5 5 5 5 5 5 5 5 
5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 
... 

약 80 개 요소의 블록을 대략 다음에 표시합니다. 3200 개의 요소. 항상 동일한 위치는 아니지만, 항상 같은 양의 요소 - 80입니다. 첫 번째 행을 (gid == 2000) 으로 변경하면 문제가 해결됩니다. 그러나 스레드 ID로 놀고 난 후에는 문제가 해결되지 않았다는 결론을 내 렸습니다. 방금 옮겼습니다. 1425 스레드를 사용하면 문제가 절반으로 줄어들고 버그가있는 블록은 배열의 끝에 있습니다. 따라서 0과 1을 갖지 않으면 블록이 "뒤로"이동했다고 가정합니다. 좀 더 흥분을 위해 - 입력 크기를 5000으로 늘리면 출력은 모두 0으로 구성됩니다. 또한, 다음 코드는 작동하지 않습니다

if(gid == 0) 
{ 
    int i, c = 0; 
    for(i = 0; i < max_id; i++) 
    { 
     if(result_mask[i]!=0) 
     { 
      c++; 
      result_mask[i] = 5; 
     } 
     else 
     { 
      result_mask[i] = 5; 
     } 
    } 
    *result_count = c; 
} 
if(gid == 3999) 
{ 
    int i, c = 0; 
    for(i = 0; i < max_id; i++) 
    { 
     if(result_mask[i]!=0) 
     { 
      c++; 
      result_mask[i] = 5; 
     } 
     else 
     { 
      result_mask[i] = 5; 
     } 
    } 
    *result_count = c; 
} 

if(gid == 3999) 
{ 
    int i, c = 0; 
    for(i = 0; i < max_id; i++) 
    { 
     if(result_mask[i]!=0) 
     { 
      c++; 
      result_mask[i] = 5; 
     } 
     else 
     { 
      result_mask[i] = 5; 
     } 
    } 
    *result_count = c; 
} 

이 (다시, 더 큰 입력이 가능한, 그것은 작동하지 않을 수 있습니다) 작동합니다 반면. 다음은 기기에 대한 몇 가지 세부 정보입니다.

Device name: GeForce 9600M GT 
Device vendor: NVIDIA 
    Clock frequency:  1250 MHz 
    Max compute units:  4 
    Global memory size:  256 MB 
    Local memory size:.  16 KB 
    Max memory allocation size: 128 MB 
    Max work group size:  512 

분명히 여기서 큰 것을 놓치고 있습니다. 나의 첫 번째 생각은 80 가지 요소의 블록이 다른 'thread'에 의해 무시되는 메모리 충돌이다. 그러나 내가 그것에 대해 생각할수록 그것이 의미를 갖지 않습니다.

나는 어떤 힌트에도 매우 감사 할 것입니다! 감사합니다. .

편집 : 늦게 응답 해 주셔서 감사합니다. 그래서 코드를 수정하여 문제를 재현하기 위해 코드를 최소화했습니다.

#include <stdio.h> 
#include <stdlib.h> 

#include <OpenCL/openCL.h> 

#define INPUTSIZE (200) 

typedef struct tag_openCL 
{ 
    cl_device_id  device; 

    cl_context   ctx; 
    cl_command_queue queue; 
    cl_program   program; 
} openCL; 

int main(void) 
{ 
    int err; 
    openCL* cl_ctx = malloc(sizeof(openCL)); 

    if(!cl_ctx) 
     exit(1); 

    err = clGetDeviceIDs(NULL, CL_DEVICE_TYPE_GPU, 1, &cl_ctx->device, NULL); 

    cl_ctx->ctx = clCreateContext(0, 1, &cl_ctx->device, clLogMessagesToStdoutAPPLE, NULL, &err); 

    cl_ctx->queue = clCreateCommandQueue(cl_ctx->ctx, cl_ctx->device, CL_QUEUE_PROFILING_ENABLE, &err); 

    printf("Successfully created context and queue for openCL device. \n"); 

    /* Build program */ 

    char * kernel_source = "__kernel void \ 
sel(__global int * input, \ 
    __global int * result_mask, \ 
    const unsigned int max_id) \ 
{ \ 
    int gid = get_global_id(0); \ 
    \ 
    result_mask[gid] = input[gid] % 2 == 0; \ 
    result_mask[gid] &= (input[gid] + 1) % 3 == 0; \ 
    \ 
    if(gid == 0) { \ 
     int i; \ 
     for(i = 0; i < max_id; i++) { \ 
      if(result_mask[i]) { \ 
       result_mask[i] = 5; \ 
      } \ 
      else { \ 
       result_mask[i] = 5; \ 
      } \ 
     } \ 
    } \ 
}"; 

    cl_program prog = clCreateProgramWithSource(cl_ctx->ctx, 1, (const char**)&kernel_source, NULL, &err); 
    cl_ctx->program = prog; 

    err = clBuildProgram(cl_ctx->program, 0, NULL, NULL, NULL, NULL); 

    cl_kernel kernel = clCreateKernel(cl_ctx->program, "sel", &err); 

    /* create dummy input data */ 
    int * input = calloc(sizeof(int), INPUTSIZE); 
    int k; 
    for(k = 0; k < INPUTSIZE; k++) 
    { 
     input[k] = abs((k % 5) - (k % 3))+ k % 2; 
    } 

    cl_mem source, intermediate; 

    unsigned int problem_size = INPUTSIZE; 

    source = clCreateBuffer(cl_ctx->ctx, CL_MEM_READ_WRITE, problem_size * sizeof(int), NULL, NULL); 
    clEnqueueWriteBuffer(cl_ctx->queue, source, CL_TRUE, 0, problem_size * sizeof(int), (void*) input, 0, NULL, NULL); 

    intermediate = clCreateBuffer(cl_ctx->ctx, CL_MEM_READ_WRITE, problem_size * sizeof(int), NULL, NULL); 

    int arg = 0; 
    clSetKernelArg(kernel, arg++, sizeof(cl_mem), &source); 
    clSetKernelArg(kernel, arg++, sizeof(cl_mem), &intermediate); 
    clSetKernelArg(kernel, arg++, sizeof(unsigned int), &problem_size); 

    size_t global_work_size = problem_size; 
    size_t local_work_size = 1; 
    clEnqueueNDRangeKernel(cl_ctx->queue, kernel, 1, NULL, &global_work_size, &local_work_size, 0, NULL, NULL); 

    clFinish(cl_ctx->queue); 

    // read results 
    int * result = calloc(sizeof(int), problem_size); 
    clEnqueueReadBuffer(cl_ctx->queue, intermediate, CL_TRUE, 0, problem_size * sizeof(int), result, 0, NULL, NULL); 
    clFinish(cl_ctx->queue); 


    int j; 
    for(j=1; j<=problem_size; j++) 
    { 
     printf("%i \t", result[j-1]); 
     if(j%10 ==0 && j>0) 
      printf("\n"); 
    } 

    return EXIT_SUCCESS; 
} 

이 결과는 여전히 비 결정적이다, 내가 출력에 임의의 위치에서 0과 1을 얻을 : 프로그램의 c 코드는 다음과 같습니다. 로컬 작업 그룹 크기가 1 인 경우 배열의 첫 번째 절반에 2의 크기가 있으며 두 번째 절반에 크기가 4 인 경우 200 개의 요소가 괜찮은 것처럼 보입니다. 그러나 다시 0과 1이 있습니다. 문제 크기는 400입니다. 또한 글로벌 작업 그룹 크기가 1 인 경우 모두 정상적으로 작동합니다. 즉, 글로벌 작업 그룹 크기가 [문제 크기]이고 두 번째 커널 작업 그룹 크기가 1 인 두 커널을 사용하면 모든 것이 훌륭하게 작동합니다. 다시 말하지만,이 방법 (커널과 같은 순차적 코드 실행)이 아니라는 것을 알고 있습니다. 왜 작동하지 않는지 알고 싶습니다. 뭔가 빠져있는 것처럼 보입니다.

감사합니다, 바실

+0

Josep에 따르면 설치 코드와 커널에서 제거한 코드를 게시 할 수 있습니까? – Tom

답변

1

귀하의 OpenCL 코드는 매우 간단하고 결과 매우 이상한. 나는 문제가 셋업 부분에서 올 수 있다고 생각한다. 버퍼 생성, EnqueueNDRange 호출 등 설정 부분을 게시 할 수 있습니까? 나는 그 문제가있을 수 있다고 생각한다.

편집 : 코드를보고 테스트 한 후에 처음에는 문제를 완전히 이해하지 못했습니다. 당신이 마스크 업데이트 부분을 코멘트하면서 내 마음이 그 라인을 없앴습니다. 나는 처음 정확하게 대답 할 수 있었어야 했음에 틀림 없다.

문제는 다른 작업 그룹을 동기화 할 수 없다는 것입니다. CLK_GLOBAL_MEM_FENCE는 작업 그룹의 메모리 주문 액세스에 영향을줍니다 (전역 메모리에 대한 쓰기가 읽기 전에 완료되었는지 확인). 문제에 대한 실제 해결책은 코드를 두 번 호출하여 실행하는 것입니다. 먼저 마스크를 병렬로 업데이트 한 다음 다른 커널에서 나머지 작업을 수행합니다. 첫 번째 완료시 실행됩니다. 계속하기 전에 전체 작업을 완료해야하므로 명령 대기열 수준에서 장벽을 사용해야합니다. 다른 방법은 없습니다.

  • 명령은 Command 키의 대기열에 하나의 작업 그룹에

    • 일 - 항목 :

      가 오픈 CL에서 동기화의 두 도메인이 있습니다 사양에서 축 어적으로

      단일 컨텍스트의 큐 (들)

  • +0

    좋아, 내가 쓴 것처럼, 나는 그 방식으로 작동하도록 할 수 있었다. (커널을 두 개의 다른 커널로 나눠서 하나씩 실행한다.) 나는 언급 된 '한계'를 알지 못했기 때문에 하나의 커널에서이 작업을 수행 할 수도 있습니다. 이 문제를 해결해 주셔서 대단히 감사합니다! – VHristov