話說軟件開發從來沒有速成一說,一門語言你學的越快,說明你在別的語言上下個功夫越多,所以這次加了引號,只不過幾周之后可能會有一個公司內部OpenCL的考核,雖然本人不需要考核,不過也正好借機整理下之前OpenCL的經驗,一方面幫著下別的同事,一方面也給自己留點干活。這個教程針對有一點C/C++開發經驗的童鞋,如果沒有太多經驗,我建議還是先去學學C語言。
這個是wiki上OpenCL的定義,我感覺想學OpenCL的人應該都已經知道了。
OpenCL?(Open?Computing?Language,開放計算語言) 是一個為異構平臺編寫程序的框架,此異構平臺可由CPU,GPU或其他類型的處理器組成。OpenCL由一門用于編寫kernels (在OpenCL設備上運行的函數)的語言(基于C99)和一組用于定義并控制平臺的API組成。OpenCL提供了基于任務分割和數據分割的并行計算機制。
第一天,環境,項目,HelloWorld
硬件環境
你需要一臺支持OpenCL的設備,AMD,Nvidia的顯卡,Intel的CPU(最好帶核顯),型號不能太老了,3年內的基本沒問題。
本次教程將以Intel的CPU配合AMD的顯卡為例。
軟件環境
操作系統是windows的,下載安裝AMDAPPSDK for windows,VS2012(老版本也行,我推薦用新的)。
操作系統是linux的,下載安裝AMDAPPSDK for linux,Code::blocks(喜歡VIM的也行,或者說更好)。
下載自己常用的版本控制軟件,和C/C++輔助開發軟件。
PS:OpenCL也就是庫和頭文件,各個廠商的SDK也就是OpenCL+Tools+Samples,別的廠商也有相應的SDK,百度“廠商 OpenCL SDK"的關鍵字組合都能找到。
檢測開發環境
安裝好AMDAPPSDK之后,在命令行下運行clinfo命令,將顯示出你支持OpenCL的硬件信息。
PS:不是AMD的設備,可以用CPUz,GPUz等軟件查詢是否支持OpenCL。
配置項目
個人認為,既然你已經知道了OpenCL就是庫那么應該知道怎么配置項目,否則可能就是不滿足有一點C/C++開發經驗這個前提了。不過為了方便,還是貼出來。
自己去安裝軟件的目錄下找到OpenCL庫文件的位置(一般是C:\Program Files (x86)\AMD APP),linux直接find -name ”opencl“ 命令就行(一般在/opt/AMDAPP下)。
Visual Studio系列(別用VC6,不解釋)
項目屬性->C++->常規->附加包含目錄,把SDK下邊的include目錄加進去。
項目屬性->鏈接器->常規->附加庫目錄,把SDK下邊的lib/x86_64(x64)目錄加進去。
項目屬性->鏈接器->輸入->附加依賴項,輸入opencl.lib
PS: 安裝完SDK,一般會在系統中加入環境變量的,用環境變量配置上邊的路徑更方便,比如$(AMDAPPSDKROOT)include就解決了,這樣換了電腦的話,只要是相同廠商就能通用。
Code::blocks
右鍵項目->Properties...->Project's build options,輸入下邊內容
-I/opt/AMDAPP/include ?-L/opt/AMDAPP/lib/x86_64 ?-lOpenCL?
PS: 其實OpenCL就是配置好頭文件和庫文件,直接粘貼到項目中也行,但是不推薦,換個電腦可能就不好使了。
HelloWorld
本人偷懶就用fixstar的代碼了,建立項目,新建c或者cpp文件,粘貼下列代碼進去:
- #include?<stdio.h>??
- #include?<stdlib.h>??
- #include?<CL/cl.h>??
- ??
- #define?MEM_SIZE?(128)??
- #define?MAX_SOURCE_SIZE?(0x100000)??
- ??
- int?main()??
- {??
- ????cl_device_id?device_id?=?NULL;??
- ????cl_context?context?=?NULL;??
- ????cl_command_queue?command_queue?=?NULL;??
- ????cl_mem?memobj?=?NULL;??
- ????cl_program?program?=?NULL;??
- ????cl_kernel?kernel?=?NULL;??
- ????cl_platform_id?platform_id?=?NULL;??
- ????cl_uint?ret_num_devices;??
- ????cl_uint?ret_num_platforms;??
- ????cl_int?ret;??
- ??
- ????char?string[MEM_SIZE];??
- ??
- ????FILE?*fp;??
- ????char?fileName[]?=?"./main.cl";??
- ????char?*source_str;??
- ????size_t?source_size;??
- ??
- ????/*?Load?the?source?code?containing?the?kernel*/??
- ????fp?=?fopen(fileName,?"r");??
- ????if?(!fp)?{??
- ????????fprintf(stderr,?"Failed?to?load?kernel.\n");??
- ????????exit(1);??
- ????}??
- ????source_str?=?(char*)malloc(MAX_SOURCE_SIZE);??
- ????source_size?=?fread(source_str,?1,?MAX_SOURCE_SIZE,?fp);??
- ????fclose(fp);??
- ??
- ????/*?Get?Platform?and?Device?Info?*/??
- ????ret?=?clGetPlatformIDs(1,?&platform_id,?&ret_num_platforms);??
- ????ret?=?clGetDeviceIDs(platform_id,?CL_DEVICE_TYPE_DEFAULT,?1,?&device_id,?&ret_num_devices);??
- ??
- ????/*?Create?OpenCL?context?*/??
- ????context?=?clCreateContext(NULL,?1,?&device_id,?NULL,?NULL,?&ret);??
- ??
- ????/*?Create?Command?Queue?*/??
- ????command_queue?=?clCreateCommandQueue(context,?device_id,?0,?&ret);??
- ??
- ????/*?Create?Memory?Buffer?*/??
- ????memobj?=?clCreateBuffer(context,?CL_MEM_READ_WRITE,MEM_SIZE?*?sizeof(char),?NULL,?&ret);??
- ????/*?Create?Kernel?Program?from?the?source?*/??
- ????program?=?clCreateProgramWithSource(context,?1,?(const?char?**)&source_str,(const?size_t?*)&source_size,?&ret);??
- ??
- ????/*?Build?Kernel?Program?*/??
- ????ret?=?clBuildProgram(program,?1,?&device_id,?NULL,?NULL,?NULL);??
- ??
- ????/*?Create?OpenCL?Kernel?*/??
- ????kernel?=?clCreateKernel(program,?"hello",?&ret);??
- ??
- ????/*?Set?OpenCL?Kernel?Parameters?*/??
- ????ret?=?clSetKernelArg(kernel,?0,?sizeof(cl_mem),?(void?*)&memobj);??
- ??
- ????/*?Execute?OpenCL?Kernel?*/??
- ????ret?=?clEnqueueTask(command_queue,?kernel,?0,?NULL,NULL);??
- ??
- ????/*?Copy?results?from?the?memory?buffer?*/??
- ????ret?=?clEnqueueReadBuffer(command_queue,?memobj,?CL_TRUE,?0,??
- ????????MEM_SIZE?*?sizeof(char),string,?0,?NULL,?NULL);??
- ??
- ????/*?Display?Result?*/??
- ????puts(string);??
- ??
- ????/*?Finalization?*/??
- ????ret?=?clFlush(command_queue);??
- ????ret?=?clFinish(command_queue);??
- ????ret?=?clReleaseKernel(kernel);??
- ????ret?=?clReleaseProgram(program);??
- ????ret?=?clReleaseMemObject(memobj);??
- ????ret?=?clReleaseCommandQueue(command_queue);??
- ????ret?=?clReleaseContext(context);??
- ??
- ????free(source_str);??
- ??
- ????getchar();??
- ????return?0;??
- ??
- }??
新建文件,后綴改成.cl,粘貼下列代碼進去:
- __kernel?void?hello(__global?char*?string)??
- {??
- ????string[0]?=?'H';??
- ????string[1]?=?'e';??
- ????string[2]?=?'l';??
- ????string[3]?=?'l';??
- ????string[4]?=?'o';??
- ????string[5]?=?',';??
- ????string[6]?=?'?';??
- ????string[7]?=?'W';??
- ????string[8]?=?'o';??
- ????string[9]?=?'r';??
- ????string[10]?=?'l';??
- ????string[11]?=?'d';??
- ????string[12]?=?'!';??
- ????string[13]?=?'\0';??
- }??
編譯執行文件。
第一天的內容就是這些,明天將講解上邊的代碼和OpenCL的組織結構。
作業:閱讀上邊的代碼,解釋各個API的含義,寫上中文注釋(最好別直接翻譯),將代碼上傳到自己的版本管理工具上。