OpenCL 教學(一)

文章推薦指數: 80 %
投票人數:10人

OpenCL 是由Khronos Group 針對異質性計算裝置(heterogeneous device)進行平行化運算所設計的標準API 以及程式語言。

所謂的「異質性計算裝置」,是 ... Hotball'sHiveSearchthissite NavigationHomeSitemapOpenCL 連結BlogMail OpenCL‎>‎ OpenCL教學(一) Contents1OpenCL簡介2OpenCL的架構3OpenCL環境設定4開始撰寫OpenCL程式5建立CommandQueue6產生資料7配置記憶體並複製資料8編譯OpenCLkernel程式9執行OpenCLkernelOpenCL簡介OpenCL是由KhronosGroup針對異質性計算裝置(heterogeneousdevice)進行平行化運算所設計的標準API以及程式語言。

所謂的「異質性計算裝置」,是指在同一個電腦系統中,有兩種以上架構差異很大的計算裝置,例如一般的CPU以及顯示晶片,或是像CELL的PPE以及SPE。

目前,最為常見的就是所謂的GPGPU應用,也就是利用一般的顯示晶片(即GPU)進行3D繪圖以外的計算工作。

過去GPGPU的應用,有各種不同的使用方式。

最早的GPGPU,多半是直接透過3D繪圖的API進行,例如OpenGL或D3D的HLSL(HighLevelShadingLanguage)。

但是,這樣做有很多缺點,主要是即使想要進行的運算和3D繪圖無關,仍需要處理很多3D繪圖方面的動作(例如建立texture,安排render-to-texture動作等等)。

這讓GPGPU變得十分複雜。

後來開始有些嘗試把這些3D繪圖部份隱藏起來的想法,例如由Stanford大學設計的BrookGPU,可以透過不同的backend將Brook程式轉換成由CPU、Direct3D、或OpenGL來執行。

另外,也有各家顯示卡廠商自行開發的系統,包括ATI針對其產品設計的ClosetoMetal(以及後來的AMDStream),以及NVIDIA的CUDA。

Microsoft也在DirectX11中加入了特別為GPGPU設計的DirectCompute。

由於各家廠商的GPGPU方案都是互不相容的(例如AMDStream的程式無法在NVIDIA的顯示晶片上執行,而CUDA的程式也不能在AMD的顯示晶片上執行),這對GPGPU的發展是不利的,因為程式開發者必須為不同廠商的顯示晶片分別撰寫程式,或是選擇只支援某個顯示晶片廠商。

由於顯示晶片的發展愈來愈彈性化,GPGPU的應用範圍也增加,因此Apple決定提出一個統一的GPGPU方案。

這個方案得到包括AMD、IBM、Intel、NVIDIA等相關廠商的支持,並很快就交由KhronosGroup進行標準化。

整個計畫只花了五個月的時間,並在2008年十二月時正式公開。

第一個正式支援OpenCL的作業系統是Apple的MacOSX10.6"SnowLeopard"。

AMD和NVIDIA也隨後推出了在Windows及Linux上的OpenCL實作。

IBM也推出了支援CELL的OpenCL實作。

OpenCL的主要設計目的,是要提供一個容易使用、且適用於各種不同裝置的平行化計算平台。

因此,它提供了兩種平行化的模式,包括taskparallel以及dataparallel。

目前GPGPU的應用,主要是以dataparallel為主,這裡也是以這個部份為主要重點。

所謂的dataparallel,指的是有大量的資料,都進行同樣的處理。

這種形式的平行化,在很多工作上都可以見到。

例如,影像處理的程式,經常要對一個影像的每個pixel進行同樣的動作(例如Gaussianblur)。

因此,這類工作很適合dataparallel的模式。

OpenCL的架構OpenCL包括一組API和一個程式語言。

基本上,程式透過OpenCLAPI取得OpenCL裝置(例如顯示晶片)的相關資料,並將要在裝置上執行的程式(使用OpenCL程式語言撰寫)編繹成適當的格式,在裝置上執行。

OpenCLAPI也提供許多裝置控制方面的動作,例如在OpenCL裝置上取得一塊記憶體、把資料從主記憶體複製到OpenCL裝置上(或從OpenCL裝置上複製到主記憶體中)、取得裝置動作的資訊(例如上一個程式執行所花費的時間)等等。

例如,我們先考慮一個簡單的工作:把一群數字相加。

在一般的C程式中,可能是如下:floata[DATA_SIZE];floatb[DATA_SIZE];floatresult[DATA_SIZE];//...for(inti=0;i#else#include#endif這樣就可以在MacOSX10.6下,以及其它的作業系統下,都可以include正確的OpenCLheader檔。

接著,要先取得系統上所有的OpenCLplatform。

在MacOSX10.6下,目前只有一個由Apple提供的OpenCLplatform,但是在其它系統上,可能會有不同廠商提供的多個不同的OpenCLplatform,因此需要先取得platform的數目:cl_interr;cl_uintnum;err=clGetPlatformIDs(0,0,&num);if(err!=CL_SUCCESS){std::cerr<platforms(num);err=clGetPlatformIDs(num,&platforms[0],&num);if(err!=CL_SUCCESS){std::cerr<(platforms[0]),0};cl_contextcontext=clCreateContextFromType(prop,CL_DEVICE_TYPE_DEFAULT,NULL,NULL,NULL);if(context==0){std::cerr<devices(cb/sizeof(cl_device_id));clGetContextInfo(context,CL_CONTEXT_DEVICES,cb,&devices[0],0);CL_CONTEXT_DEVICES表示要取得裝置的列表。

和前面取得platformID的情形相同,clGetContextInfo被呼叫了兩次:第一次是要取得需要存放裝置列表所需的記憶體空間大小(也就是傳入&cb),然後第二次呼叫才真正取得所有裝置的列表。

接下來,可能會想要確定倒底找到的OpenCL裝置是什麼。

所以,可以透過OpenCLAPI取得裝置的名稱,並將它印出來。

取得和裝置相關的資料,是使用clGetDeviceInfo函式,和前面的clGetContextInfo函式相當類似。

以下是取得裝置名稱的方式:clGetDeviceInfo(devices[0],CL_DEVICE_NAME,0,NULL,&cb);std::stringdevname;devname.resize(cb);clGetDeviceInfo(devices[0],CL_DEVICE_NAME,cb,&devname[0],0);std::cout<#include#include#ifdef__APPLE__#include#else#include#endifintmain(){cl_interr;cl_uintnum;err=clGetPlatformIDs(0,0,&num);if(err!=CL_SUCCESS){std::cerr<platforms(num);err=clGetPlatformIDs(num,&platforms[0],&num);if(err!=CL_SUCCESS){std::cerr<(platforms[0]),0};cl_contextcontext=clCreateContextFromType(prop,CL_DEVICE_TYPE_DEFAULT,NULL,NULL,NULL);if(context==0){std::cerr<devices(cb/sizeof(cl_device_id));clGetContextInfo(context,CL_CONTEXT_DEVICES,cb,&devices[0],0);clGetDeviceInfo(devices[0],CL_DEVICE_NAME,0,NULL,&cb);std::stringdevname;devname.resize(cb);clGetDeviceInfo(devices[0],CL_DEVICE_NAME,cb,&devname[0],0);std::cout<a(DATA_SIZE),b(DATA_SIZE),res(DATA_SIZE);for(inti=0;idata(length+1);in.read(&data[0],length);data[length]=0;//createandbuildprogram constchar*source=&data[0];cl_programprogram=clCreateProgramWithSource(context,1,&source,0,0);if(program==0){return0;}if(clBuildProgram(program,0,0,0,0,0)!=CL_SUCCESS){return0;}returnprogram;}上面的程式,就是直接將檔案讀到記憶體中,再呼叫clCreateProgramWithSource建立一個programobject。

建立成功後,再呼叫clBuildProgram函式編譯程式。

clBuildProgram函式可以指定很多參數,不過在這裡暫時沒有使用到。

有了這個函式,在main函式中,直接呼叫:cl_programprogram=load_program(context,"shader.cl");if(program==0){std::cerr<#include#include#include#include#ifdef__APPLE__#include#else#include#endifcl_programload_program(cl_contextcontext,constchar*filename){std::ifstreamin(filename,std::ios_base::binary);if(!in.good()){return0;}//getfilelengthin.seekg(0,std::ios_base::end);size_tlength=in.tellg();in.seekg(0,std::ios_base::beg);//readprogramsourcestd::vectordata(length+1);in.read(&data[0],length);data[length]=0;//createandbuildprogram constchar*source=&data[0];cl_programprogram=clCreateProgramWithSource(context,1,&source,0,0);if(program==0){return0;}if(clBuildProgram(program,0,0,0,0,0)!=CL_SUCCESS){return0;}returnprogram;}intmain(){cl_interr;cl_uintnum;err=clGetPlatformIDs(0,0,&num);if(err!=CL_SUCCESS){std::cerr<platforms(num);err=clGetPlatformIDs(num,&platforms[0],&num);if(err!=CL_SUCCESS){std::cerr<(platforms[0]),0};cl_contextcontext=clCreateContextFromType(prop,CL_DEVICE_TYPE_DEFAULT,NULL,NULL,NULL);if(context==0){std::cerr<devices(cb/sizeof(cl_device_id));clGetContextInfo(context,CL_CONTEXT_DEVICES,cb,&devices[0],0);clGetDeviceInfo(devices[0],CL_DEVICE_NAME,0,NULL,&cb);std::stringdevname;devname.resize(cb);clGetDeviceInfo(devices[0],CL_DEVICE_NAME,cb,&devname[0],0);std::cout<a(DATA_SIZE),b(DATA_SIZE),res(DATA_SIZE);for(inti=0;i



請為這篇文章評分?