2010-06-19 67 views
1

我面臨着OpenCL的問題,我希望有人會對可能的原因有所暗示。以下是該方案的一個版本,簡化爲該問題。我有一個大小爲4000的輸入int數組。在我的內核中,我正在執行掃描。顯然,有很好的方法可以並行執行,但爲了重現問題,只有一個線程正在執行整個計算。掃描之前,輸入(result_mask)僅具有值0或1。OpenCL的問題

__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; 
} 
} 

預期的結果將是,最初有在結果掩模大於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 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個元素的塊被另一個'線程'覆蓋。但是我越想越覺得它越不理想。

我會非常感謝任何提示! 謝謝。編輯: 對不起,遲到的迴應。所以我修改了代碼,將其降低到最小程度以重現問題。以下是該程序的C代碼:

#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的在輸出隨機位置。對於本地工作組大小爲1的數組,它們位於數組的前半部分,大小爲2 - 在下半部分,大小爲4時,對於200個元素看起來可以,但還有0和1問題大小爲400.此外,對於全球工作組大小爲1,所有工作都很好。也就是說,如果我使用兩個內核 - 一個用[問題大小]的全局工作組大小進行並行計算,另一個用全局工作組大小爲1的第二個內核,則一切正常。再一次,我完全意識到這不是執行它的方式(運行這樣的順序代碼的內核),但是,我想知道,爲什麼它不起作用,因爲它看起來我錯過了某些東西。

感謝, Vassil

+0

正如Josep所說,你能發佈設置代碼以及你從內核中刪除的代碼嗎? – Tom 2010-06-24 09:46:25

回答

1

你的OpenCL代碼很簡單,結果很怪異。我認爲問題可能來自設置部分。創建緩衝區,調用EnqueueNDRange等。您可以發佈設置部分嗎?我想這個問題可以在那裏。

編輯: 在看到您的代碼並進行測試後,我意識到,起初我並沒有完全理解您的問題。當你嘲笑面具更新部分時,我的腦子剛剛擺脫了那條線。我應該能夠第一次正確回答。

問題是您無法同步不同的工作組。 CLK_GLOBAL_MEM_FENCE影響工作組的內存順序訪問(確保在回讀之前寫入全​​局內存)。真正解決問題的方法是在兩個調用中執行代碼,首先並行更新掩碼,然後在另一個內核中執行其餘內容,第一個內核將在第一個內核完成時執行。在繼續之前,您需要完成整個操作,因此您必須在命令隊列級別使用屏障。沒有其他辦法。

從說明書逐字:

有OpenCL中同步的兩個結構域:在一個單一的工作組

  • 工作項

  • 命令排隊到命令 - 隊列在單個環境下

+0

好的,正如我寫的那樣,我設法使它以這種方式工作(將內核分爲兩個不同的內核並逐個執行它們)。我只是在一個內核中也可以這樣做,因爲我不知道提到的「限制」。非常感謝你清理這個! – VHristov 2010-07-06 10:21:00