簡體   English   中英

OpenCL內核執行速度比單線程慢

[英]OpenCL Kernel Executes Slower Than Single Thread

總之,我寫了一個非常簡單的OpenCL內核,它使用簡單的平均將RGB圖像轉換為灰度。

一些背景:

  1. 圖像存儲在映射的存儲器中,作為24位非填充存儲器塊
  2. 輸出數組存儲在固定內存中(使用clEnqueueMapBuffer映射)並且為8 bpp
  3. 在設備上分配了兩個緩沖區( clCreateBuffer ),一個是專門讀取的(我們在內核啟動之前clWriteBuffer ),另一個是專門寫入的(在內核完成后我們clReadBuffer

我在1280x960圖像上運行它。 該算法的串行版本平均為60ms,OpenCL內核平均為200ms! 我做錯了什么,但我不知道如何繼續,優化什么。 (在沒有內核調用的情況下調整我的讀/寫,算法在15ms內運行)

我附加了內核設置(大小和參數)以及內核


編輯 :所以我寫了一個偶數dumber內核,它內部沒有全局內存訪問,它只有150ms ...這仍然是非常慢的。 我想也許我搞亂全局內存讀取,它們必須是4字節對齊或什么? 不...

編輯2:從我的內核中刪除所有參數給了我顯着的加速...我很困惑我認為因為我是clEnqueueWriteBuffer內核應該沒有從主機 - >設備和設備 - >主機進行內存傳輸。 ..

編輯3:想出來,但我仍然不明白為什么。 如果有人能解釋,我很樂意給他們正確答案。 問題是按值傳遞自定義結構。 看起來我需要為它們分配一個全局內存位置並傳遞它們的cl_mem


內核調用:

//Copy input to device
result = clEnqueueWriteBuffer(handles->queue, d_input_data, CL_TRUE, 0, h_input.widthStep*h_input.height, (void *)input->imageData, 0, 0, 0);
if(check_result(result, "opencl_rgb_to_gray", "Failed to write to input buffer on device!")) return 0;

//Set kernel arguments
result = clSetKernelArg(handles->current_kernel, 0, sizeof(OpenCLImage), (void *)&h_input);
if(check_result(result, "opencl_rgb_to_gray", "Failed to set input struct.")) return 0;
result = clSetKernelArg(handles->current_kernel, 1, sizeof(cl_mem), (void *)&d_input_data);
if(check_result(result, "opencl_rgb_to_gray", "Failed to set input data.")) return 0;
result = clSetKernelArg(handles->current_kernel, 2, sizeof(OpenCLImage), (void *)&h_output);
if(check_result(result, "opencl_rgb_to_gray", "Failed to set output struct.")) return 0;
result = clSetKernelArg(handles->current_kernel, 3, sizeof(cl_mem), (void *)&d_output_data);
if(check_result(result, "opencl_rgb_to_gray", "Failed to set output data.")) return 0;

//Determine run parameters
global_work_size[0] = input->width;//(unsigned int)((input->width / (float)local_work_size[0]) + 0.5);
global_work_size[1] = input->height;//(unsigned int)((input->height/ (float)local_work_size[1]) + 0.5);

printf("Global Work Group Size: %d %d\n", global_work_size[0], global_work_size[1]);

//Call kernel
result = clEnqueueNDRangeKernel(handles->queue, handles->current_kernel, 2, 0, global_work_size, local_work_size, 0, 0, 0);
if(check_result(result, "opencl_rgb_to_gray", "Failed to run kernel!")) return 0;

result = clFinish(handles->queue);
if(check_result(result, "opencl_rgb_to_gray", "Failed to finish!")) return 0;

//Copy output
result = clEnqueueReadBuffer(handles->queue, d_output_data, CL_TRUE, 0, h_output.widthStep*h_output.height, (void *)output->imageData, 0, 0, 0);
if(check_result(result, "opencl_rgb_to_gray", "Failed to write to output buffer on device!")) return 0;

核心:

typedef struct OpenCLImage_t
{
    int width;
    int widthStep;
    int height;
    int channels;
} OpenCLImage;

__kernel void opencl_rgb_kernel(OpenCLImage input, __global unsigned char*  input_data, OpenCLImage output, __global unsigned char * output_data)
{
    int pixel_x = get_global_id(0);
    int pixel_y = get_global_id(1);
    unsigned char * cur_in_pixel, *cur_out_pixel;
    float avg = 0;

    cur_in_pixel = (unsigned char *)(input_data + pixel_y*input.widthStep + pixel_x * input.channels);
    cur_out_pixel = (unsigned char *)(output_data + pixel_y*output.widthStep + pixel_x * output.channels);

    avg += cur_in_pixel[0];
    avg += cur_in_pixel[1];
    avg+= cur_in_pixel[2];
    avg /=3.0f;

    if(avg > 255.0)
        avg = 255.0;
    else if(avg < 0)
        avg = 0;

    *cur_out_pixel = avg;
}

將值復制到將要創建的所有線程的開銷可能是時間的可能原因; 對於全局存儲器,在另一種情況下引用就足夠了。 唯一的SDK實現者將能夠准確回答.. :)

你可能想嘗試像[64,1,1]這樣的local_work_size來合並你的內存調用。 (注意64是1280的加法器)。

如前所述,您必須使用分析器才能獲得更多信息。 你在使用nvidia卡嗎? 然后下載CUDA 4(不是5),因為它包含一個openCL分析器。

你的表現必須遠離最佳狀態。 更改本地工作大小,全局工作大小,嘗試每個胎面處理兩個或四個像素。 你能改變像素的儲存方式嗎? 然后打破樹形數組的結構,以便更有效地合並memomry訪問。

使用GPU可以隱藏你的內存傳輸:使用你附近的探查器會更容易。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM