OpenCL学习入门
1.OpenCL概念
OpenCL是一個為異構平臺編寫程序的框架,此異構平臺可由CPU、GPU或其他類型的處理器組成。OpenCL由一門用于編寫kernels (在OpenCL設備上運行的函數)的語言(基于C99)和一組用于定義并控制平臺的API組成。
OpenCL提供了兩種層面的并行機制:任務并行與數據并行。
名詞的概念:
Platform (平臺):主機加上OpenCL框架管理下的若干設備構成了這個平臺,通過這個平臺,應用程序可以與設備共享資源并在設備上執行kernel。實際使用中基本上一個廠商對應一個Platform,比如Intel, AMD都是這樣。
Device(設備):官方的解釋是計算單元(Compute Units)的集合。舉例來說,GPU是典型的device。Intel和AMD的多核CPU也提供OpenCL接口,所以也可以作為Device。
Context(上下文):OpenCL的Platform上共享和使用資源的環境,包括kernel、device、memory objects、command queue等。使用中一般一個Platform對應一個Context。
Program:OpenCL程序,由kernel函數、其他函數和聲明等組成。
Kernel(核函數):可以從主機端調用,運行在設備端的函數。
Memory Object(內存對象):在主機和設備之間傳遞數據的對象,一般映射到OpenCL程序中的global memory。有兩種具體的類型:Buffer Object(緩存對象)和Image Object(圖像對象)。
Command Queue(指令隊列):在指定設備上管理多個指令(Command)。隊列里指令執行可以順序也可以亂序。一個設備可以對應多個指令隊列。
NDRange:主機端運行設備端kernel函數的主要接口。實際上還有其他的,NDRange是非常常見的,用于分組運算,以后具體用到的時候就知道區別了。
2.OpenCL與CUDA的區別
不同點:OpenCL是通用的異構平臺編程語言,為了兼顧不同設備,使用繁瑣。
CUDA是nvidia公司發明的專門在其GPGPU上的編程的框架,使用簡單,好入門。
相同點:都是基于任務并行與數據并行。
3.OpenCL的編程步驟
(1)Discover and initialize the platforms
調用兩次clGetPlatformIDs函數,第一次獲取可用的平臺數量,第二次獲取一個可用的平臺。
(2)Discover and initialize the devices
調用兩次clGetDeviceIDs函數,第一次獲取可用的設備數量,第二次獲取一個可用的設備。
(3)Create a context(調用clCreateContext函數)
上下文context可能會管理多個設備device。
(4)Create a command queue(調用clCreateCommandQueue函數)
一個設備device對應一個command queue。上下文conetxt將命令發送到設備對應的command queue,設備就可以執行命令隊列里的命令。
(5)Create device buffers(調用clCreateBuffer函數)
Buffer中保存的是數據對象,就是設備執行程序需要的數據保存在其中。
Buffer由上下文conetxt創建,這樣上下文管理的多個設備就會共享Buffer中的數據。
(6)Write host data to device buffers(調用clEnqueueWriteBuffer函數)
(7)Create and compile the program
創建程序對象,程序對象就代表你的程序源文件或者二進制代碼數據。
(8)Create the kernel(調用clCreateKernel函數)
根據你的程序對象,生成kernel對象,表示設備程序的入口。
(9)Set the kernel arguments(調用clSetKernelArg函數)
(10)Configure the work-item structure(設置worksize)
配置work-item的組織形式(維數,group組成等)
(11)Enqueue the kernel for execution(調用clEnqueueNDRangeKernel函數)
將kernel對象,以及 work-item參數放入命令隊列中進行執行。
(12)Read the output buffer back to the host(調用clEnqueueReadBuffer函數)
(13)Release OpenCL resources(至此結束整個運行過程)
OpenCL的主要執行過程:
4.說明
OpenCL中的核函數必須單列一個文件。
OpenCL的編程一般步驟就是上面的13步,太長了,以至于要想做個向量加法都是那么困難。
不過上面的步驟前3步一般是固定的,可以單獨寫在一個.h/.cpp文件中,其他的一般也不會有什么大的變化。
5.程序實例,向量運算
5.1通用前3個步驟,生成一個文件
tool.h
tool.c
#include <CL/cl.h> #include <string.h> #include <stdio.h> #include <stdlib.h> #include <iostream> #include <string> #include <fstream> #include "tool.h" using namespace std;/** convert the kernel file into a string */ int convertToString(const char *filename, std::string& s) {size_t size;char* str;std::fstream f(filename, (std::fstream::in | std::fstream::binary));if(f.is_open()){size_t fileSize;f.seekg(0, std::fstream::end);size = fileSize = (size_t)f.tellg();f.seekg(0, std::fstream::beg);str = new char[size+1];if(!str){f.close();return 0;}f.read(str, fileSize);f.close();str[size] = '\0';s = str;delete[] str;return 0;}cout<<"Error: failed to open file\n:"<<filename<<endl;return -1; }/**Getting platforms and choose an available one.*/ int getPlatform(cl_platform_id &platform) {platform = NULL;//the chosen platformcl_uint numPlatforms;//the NO. of platformscl_int status = clGetPlatformIDs(0, NULL, &numPlatforms);if (status != CL_SUCCESS){cout<<"Error: Getting platforms!"<<endl;return -1;}/**For clarity, choose the first available platform. */if(numPlatforms > 0){cl_platform_id* platforms =(cl_platform_id* )malloc(numPlatforms* sizeof(cl_platform_id));status = clGetPlatformIDs(numPlatforms, platforms, NULL);platform = platforms[0];free(platforms);}elsereturn -1; }/**Step 2:Query the platform and choose the first GPU device if has one.*/ cl_device_id *getCl_device_id(cl_platform_id &platform) {cl_uint numDevices = 0;cl_device_id *devices=NULL;cl_int status = clGetDeviceIDs(platform, CL_DEVICE_TYPE_GPU, 0, NULL, &numDevices);if (numDevices > 0) //GPU available.{devices = (cl_device_id*)malloc(numDevices * sizeof(cl_device_id));status = clGetDeviceIDs(platform, CL_DEVICE_TYPE_GPU, numDevices, devices, NULL);}return devices; }5.2核函數文件
HelloWorld_Kernel.cl
__kernel void helloworld(__global double* in, __global double* out){int num = get_global_id(0);out[num] = in[num] / 2.4 *(in[num]/6) ;}5.3主函數文件
HelloWorld.cpp
//For clarity,error checking has been omitted. #include <CL/cl.h> #include "tool.h" #include <string.h> #include <stdio.h> #include <stdlib.h> #include <iostream> #include <string> #include <fstream> using namespace std;int main(int argc, char* argv[]) {cl_int status;/**Step 1: Getting platforms and choose an available one(first).*/cl_platform_id platform;getPlatform(platform);/**Step 2:Query the platform and choose the first GPU device if has one.*/cl_device_id *devices=getCl_device_id(platform);/**Step 3: Create context.*/cl_context context = clCreateContext(NULL,1, devices,NULL,NULL,NULL);/**Step 4: Creating command queue associate with the context.*/cl_command_queue commandQueue = clCreateCommandQueue(context, devices[0], 0, NULL);/**Step 5: Create program object */const char *filename = "HelloWorld_Kernel.cl";string sourceStr;status = convertToString(filename, sourceStr);const char *source = sourceStr.c_str();size_t sourceSize[] = {strlen(source)};cl_program program = clCreateProgramWithSource(context, 1, &source, sourceSize, NULL);/**Step 6: Build program. */status=clBuildProgram(program, 1,devices,NULL,NULL,NULL);/**Step 7: Initial input,output for the host and create memory objects for the kernel*/const int NUM=512000;double* input = new double[NUM];for(int i=0;i<NUM;i++)input[i]=i;double* output = new double[NUM];cl_mem inputBuffer = clCreateBuffer(context, CL_MEM_READ_ONLY|CL_MEM_COPY_HOST_PTR, (NUM) * sizeof(double),(void *) input, NULL);cl_mem outputBuffer = clCreateBuffer(context, CL_MEM_WRITE_ONLY , NUM * sizeof(double), NULL, NULL);/**Step 8: Create kernel object */cl_kernel kernel = clCreateKernel(program,"helloworld", NULL);/**Step 9: Sets Kernel arguments.*/status = clSetKernelArg(kernel, 0, sizeof(cl_mem), (void *)&inputBuffer);status = clSetKernelArg(kernel, 1, sizeof(cl_mem), (void *)&outputBuffer);/**Step 10: Running the kernel.*/size_t global_work_size[1] = {NUM};cl_event enentPoint;status = clEnqueueNDRangeKernel(commandQueue, kernel, 1, NULL, global_work_size, NULL, 0, NULL, &enentPoint);clWaitForEvents(1,&enentPoint); ///waitclReleaseEvent(enentPoint);/**Step 11: Read the cout put back to host memory.*/status = clEnqueueReadBuffer(commandQueue, outputBuffer, CL_TRUE, 0, NUM * sizeof(double), output, 0, NULL, NULL);cout<<output[NUM-1]<<endl;/**Step 12: Clean the resources.*/status = clReleaseKernel(kernel);//*Release kernel.status = clReleaseProgram(program); //Release the program object.status = clReleaseMemObject(inputBuffer);//Release mem object.status = clReleaseMemObject(outputBuffer);status = clReleaseCommandQueue(commandQueue);//Release Command queue.status = clReleaseContext(context);//Release context.if (output != NULL){free(output);output = NULL;}if (devices != NULL){free(devices);devices = NULL;}return 0; }編譯、鏈接、執行:
g++ -o A *.cpp -lOpenCL ./A總結
以上是生活随笔為你收集整理的OpenCL学习入门的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 顺序表归并
- 下一篇: 丁洪波 -- 不要“ 总是拿着微不足道