[英]OpenCL Kernel Executes Slower Than Single Thread
總之,我寫了一個非常簡單的OpenCL內核,它使用簡單的平均將RGB圖像轉換為灰度。
一些背景:
clEnqueueMapBuffer
映射)並且為8 bpp 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.