OpenCL学习笔记——整体流程

OpenCL可以实现混合设备的并行计算,这些设备包括CPU,GPU,以及其他处理器,比如Cell处理器,DSP等。使用OpenCL编程,可以实现可以值得并行加速代码。

OpenCL架构

通常包括以下四部分:
• 平台模型 (Platform Model)
• 执行模型 (Execution Model)
• 内存模型 (Memory Model)
• 编程模型 (Programming Model)

不同厂商的 OpenCL 实施定义了不同的 OpenCL 平台,通过 OpenCL 平台,主机能够和 OpenCL 设备之间进行交互操作。现在主要的 OpenCL 平台有 AMD、Nvdia,intel 等。OpenCL 使用了一种Installable Client Driver 模型,这样不同厂商的平台就能够在系统中共存。

OpenCL 平台通常包括一个主机 (Host) 和多个 OpenCL 设备(device),每个 OpenCL 设备包括一个或多个CU(compute units),每个 CU 包括又一个或多个 PE(process element)。每个 PE 都有自己的程序计数器 (PC)。

环境配置

首先肯定是要先把自己电脑上面有关opencl的环境配置好
查看自己电脑的显卡:打开设备管理器->显示适配器,里面就是电脑拥有的的显卡,我的电脑里面是有Intel和NVIDIA的显卡的。

大家可以去网上下载一下“GPU-Z”这个软件,这个软件可以查看电脑的显卡对OpenCL的支持性,以及支持的版本,我的电脑的如下图可见,有√就是支持的(当你把鼠标移动到单词周围会有适配版本提示)。


接下来就可以根据自己的GPU型号,下载相应的显卡驱动了。
下载完相应SDK,在自己的Visual Studio中配置OpenCL环境就好了,其他博客这方面已经讲的很好了,大家自行搜一下,这里我就不重复了。

每次配环境,环境杀手是谁我不说,好在最终成功了,测试代码成功运行,输出平台信息。

执行流程

流程图如下

下面以两向量相加为例,进行OpenCL的设计

1、平台(Platform)

主机加上OpenCL框架管理下的若干设备构成了这个平台,通过这个平台,应用程序可以与设备共享资源并在设备上执行kernel。实际使用中基本上一个厂商对应一个Platform,比如Intel, AMD都是这样。
我们要查询平台寻找可用平台,我们用下面这个函数:

cl_int clGetPlatformIDs(cl_unit num_entries,cl_platform_id* platforms,cl_uint* num_platforms);

通常这个函数要调用2次,第一次用来查询可用的平台数,第二次选择自己需要的平台。
下面是创建平台、查询平台、选择平台的代码,注释很详细了。

//------------------- 获得并选择可用平台-----------------------------// 获得平台数量cl_int  iStatus = 0;       // 函数返回状态cl_uint    uiNumPlatforms = 0;        // 平台个数iStatus = clGetPlatformIDs(0, NULL,&uiNumPlatforms); // 查询可用的平台个数,并返回状态if (CL_SUCCESS != iStatus){cout << "Error: Getting platforms error" << endl;}cout << "一共有平台:" << uiNumPlatforms << endl;// 获得平台地址if (uiNumPlatforms > 0)  // 如果有可用平台{// 根据平台数为平台分配内存空间cl_platform_id *pPlatforms = (cl_platform_id *)malloc(uiNumPlatforms * sizeof(cl_platform_id));iStatus = clGetPlatformIDs(uiNumPlatforms, pPlatforms, NULL);     // 获得可用的平台gpu.Platform = pPlatforms[0];                                        // 获得第一个平台的地址free(pPlatforms);                                                  // 释放平台占用的内存空间}

2、设备(Device)

官方的解释是计算单元(Compute Units)的集合。举例来说,GPU是典型的device。Intel和AMD的多核CPU也提供OpenCL接口,所以也可以作为Device。
查询设备,我们用下面这个函数:

clGetDeviceIDs(cl_platform_id platform,cl_device_type device_type,cl_unit num_entries,cl_device_id* devices,cl_unit* num_devices);

这个函数通常也是调用2次,第一次查询设备数量,第二次检索得到我们想要的设备。下面代码是查询可用设备数以及选择第一个GPU设备,如果没有GPU设备那就创建CPU设备。

//--------------------------查询GPU设备,并选择可用设备------------------------------iStatus = clGetDeviceIDs(gpu.Platform, CL_DEVICE_TYPE_GPU, 0, NULL, &gpu.uiNumDevices);     // 获得GPU设备数量if (0 == gpu.uiNumDevices)            // 如果没有GPU设备{cout << "No GPU device available." << endl;cout << "Choose CPU as default device." << endl;iStatus = clGetDeviceIDs(gpu.Platform,CL_DEVICE_TYPE_CPU, 0,NULL, &gpu.uiNumDevices);  // 选择CPU作为设备,获得设备数gpu.pDevices = (cl_device_id *)malloc(gpu.uiNumDevices * sizeof(cl_device_id));            // 为设备分配空间iStatus = clGetDeviceIDs(gpu.Platform, CL_DEVICE_TYPE_CPU, gpu.uiNumDevices, gpu.pDevices,NULL);  // 获得CPU平台}else{gpu.pDevices = (cl_device_id *)malloc(gpu.uiNumDevices * sizeof(cl_device_id));//选择GPU作为设备iStatus = clGetDeviceIDs(gpu.Platform, CL_DEVICE_TYPE_GPU, gpu.uiNumDevices, gpu.pDevices, NULL);}

3、上下文(Context)

OpenCL的Platform上共享和使用资源的环境,包括kernel、device、memory objects、command queue等。使用中一般一个Platform对应一个Context。
用下面这个函数进行上下文创建

cl_context clCreateContest(const cl_context_properties* properties,cl_uint num_devices,const cl_device_id* devices,void (CL_CALLBACK *pfn_notify)(const char* errinfo,const void* private_info,size_t cb,void* user_data),void* user_data,cl_int* errcode_ret);

下面为根据已有设备创建上下文的代码

// ---------------------创建设备环境---------------------------------cl_context_properties  contextProperties[] = { CL_CONTEXT_PLATFORM,(cl_context_properties)gpu.Platform,0 };gpu.Context = clCreateContext(contextProperties, 1, gpu.pDevices, NULL, NULL, NULL); // 创建设备环境if (NULL == gpu.Context){cout << "Error: Can not create context" << endl;}

4、指令队列(Command Queue)

对于不同的设备,它们都有自己的独立的命令队列;命令队列中的命令 (kernel 函数)可能是同步的,也可能是异步的,它们的执行顺序可以是有序的,也可以是乱序的。命令队列就是主机的请求,在设备上执行的一种机制,他把Device和Context联系起来。
函数定义

cl_command_queue clCreateCommandQueue(cl_context context,cl_device_id device,cl_command_queue_properties properties,cl_int* errcode_ret);

我的使用

// -------------------创建命令队列--------------------------------------gpu.CommandQueue = clCreateCommandQueue(gpu.Context, gpu.pDevices[0], 0, NULL);   // 创建第1个设备的命令队列if (NULL == gpu.CommandQueue){cout << "Error: Can not create CommandQueue" << endl;}

5、程序对象(Program)

OpenCL Program由kernel函数、其他函数和声明组成。它通过cl_program表示。当创建一个program时,你必须指定它是由哪些文件组成的,然后编译它。
创建函数、编译函数如下:

cl_program clCreateProgramWithSource(cl_context context,cl_uint count,const char** strings,const size_t* lengths,cl_int* errcode_ret);
cl_int clBuildProgram(cl_program program,cl_uint num_devices,const cl_device_id* device_list,const char* options,void (CL_CALLBACK *pfn_notify)(cl_program program,void* user_data),void* user_data);

我的实现:

// ----------------------创建程序对象------------------------------string      strSource = "";      // 用于存储cl文件中的代码const char   *pSource = NULL;       // 代码字符串指针size_t        uiArrSourceSize[] = { 0 };     // 代码字符串长度iStatus = ConvertToString(gpu.pFileName, strSource);// 将cl文件中的代码转为字符串pSource = strSource.c_str();       // 获得strSource指针uiArrSourceSize[0] = strlen(pSource);  // 字符串大小gpu.Program = clCreateProgramWithSource(gpu.Context, 1, &pSource, uiArrSourceSize, NULL);// 创建程序对象if (NULL == gpu.Program){cout << "Error: Can not create program" << endl;}// ----------------------------编译程序--------------------------------iStatus = clBuildProgram(gpu.Program, 1, gpu.pDevices, NULL, NULL, NULL);  // 编译程序if (CL_SUCCESS != iStatus)   // 编译错误{cout << "Error: Can not build program" << endl;char szBuildLog[16384];clGetProgramBuildInfo(gpu.Program, *gpu.pDevices, CL_PROGRAM_BUILD_LOG, sizeof(szBuildLog), szBuildLog, NULL);cout << "Error in Kernel: " << endl << szBuildLog;//输出报错信息clReleaseProgram(gpu.Program);}

6、内存对象(Buffer 、Image)

OpenCL 内存对象就是一些 OpenCL 数据,这些数据一般在设备内存中,能够被拷入也能够被拷出。OpenCL 内存对象包括 buffer 对象和 image 对象。
• Buffer 对象:连续的内存块 ----顺序存储,能够通过指针、行列式等直接访问。
• Image 对象:是 2 维或 3 维的内存对象,只能通过 read_image() 或 write_image() 来读取。image 对象可以是可读或可写的,但不能同时既可读又可写。
下面的函数会在指定的context上创建一个buffer对象,Buffer由上下文conetxt创建,这样上下文管理的多个设备就会共享Buffer中的数据。

cl_mem clCreateBuffer(cl_context context,cl_mem_flags flags,size_t size,void* host_ptr,cl_int* errcode_ret);

具体应用如下:

// 创建输入内存对象Acl_mem memInputBuffer = clCreateBuffer(gpu.Context,CL_MEM_READ_ONLY | CL_MEM_COPY_HOST_PTR, // 输入内存为只读,并可以从宿主机内存复制到设备内存sizeof(cl_float)*ARRAY_SIZE,        // 输入内存空间大小(void*)arrInA,NULL);// 创建输入内存对象Bcl_mem memmaskBuffer = clCreateBuffer(gpu.Context,CL_MEM_READ_ONLY | CL_MEM_COPY_HOST_PTR,  // 输入内存为只读,并可以从宿主机内存复制到设备内存sizeof(cl_float)*ARRAY_SIZE,              // 输入内存空间大小(void*)arrInB,NULL);// 创建输出内存对象cl_mem memOutputBuffer = clCreateBuffer(gpu.Context,CL_MEM_WRITE_ONLY,                         // 输出内存只能写sizeof(cl_float)*ARRAY_SIZE,                  // 输出内存空间大小NULL,NULL);if ((NULL == memInputBuffer) || (NULL == memmaskBuffer) || (NULL == memOutputBuffer)){cout << "Error creating memory objects" << endl;}

7、核函数(Kernel)

可以从主机端调用,运行在设备端的函数。Kernel 对象通过程序对象以及指定的函数名字创建。注意:函数必须是程序源代码中存在的函数。

cl_kernel clCreateKernel(cl_program program,const char* kernel_name,cl_int* errcode_ret);

创建 Kernel 后,运行 Kernel 之前,我们还要为 Kernel 对象设置参数。我们可以在 Kernel 运行后,重新设置参数再次运行。

cl_int clSetKernelArg(cl_kernel kernel,cl_uint arg_index,size_t arg_size,const void* arg_value);

arg_index 指定该参数为 Kernel 函数中的第几个参数 (比如第一个参数为 0,第二个为 1,…)。内存对象和单个的值都可以作为 Kernel 参数。
以两个向量相加为例:

     //--------------------------创建内核对象-------------------------------------gpu.Kernel = clCreateKernel(gpu.Program,"Vecadd",  // cl文件中的入口函数NULL);if (NULL == gpu.Kernel){cout << "Error: Can not create kernel" << endl;}//----------------------------设置内核参数----------------------------------cl_int    iStatus = 0;       // 函数返回状态iStatus = clSetKernelArg(gpu.Kernel,0,        // 参数索引sizeof(cl_mem),(void *)&memInputBuffer);iStatus = clSetKernelArg(gpu.Kernel,1,      // 参数索引sizeof(cl_mem),(void *)&memmaskBuffer);iStatus |= clSetKernelArg(gpu.Kernel, 2, sizeof(cl_mem), (void *)&memOutputBuffer);if (CL_SUCCESS != iStatus){cout << "Error setting kernel arguments" << endl;}

8、入队执行(NDRange)、读取结果

主机端运行设备端kernel函数的主要接口。实际上还有其他的,NDRange是非常常见的,用于分组运算。
首先要设置线程索引空间的维度以及workgroup大小等。通过函数clEnqueueNDRangeKernel把Kernel放在一个队列里,但不保证他马上执行,OpenCL driver 会管理队列,调度 Kernel 的执行。

cl_int clEnqueueNDRangeKernel(cl_command_queue command_queue,cl_kernel kernel,cl_uint work_dim,const size_t* global_work_offset,const size_t* global_work_size,const size_t* local_work_size,cl_uint num_events_in_wait_list,const cl_event* event_wait_list,cl_event* event);

该函数把要执行的 Kernel 函数放在指定的命令队列中,global_work_size大小(线程索引空间)必须指定,local_work_size大小可以指定,也可以为空。如果为空,则系统会自动根据硬件选择合适的大小。event_wait_list 用来选定一些 events,只有这些 events 执行完后,该 kernel 才可能被执行,也就是通过事件机制来实现不同 kernel 函数之间的同步。

当 Kernel 函数执行完毕后,我们要把数据从 device memory 中拷贝到 host memory 中去。可以用下面这个函数

cl_int clEnqueueReadBuffer(cl_command_queue command_queue,cl_mem buffer,cl_bool blocking_read,size_t offset,size_t cb,void* ptr,cl_uint num_events_in_wait_list,const cl_event* event_wait_list,cl_event* event);

具体实现:

 // --------------------------运行内核---------------------------------const size_t global_Work_Size[1] = {ARRAY_SIZE};const size_t local_Work_Size[1] = { 1 };// 利用命令队列使将再设备上执行的内核排队iStatus = clEnqueueNDRangeKernel(gpu.CommandQueue,gpu.Kernel,1,NULL,global_Work_Size,  // 确定内核在设备上的多个处理单元间的分布local_Work_Size,                 // 确定内核在设备上的多个处理单元间的分布0,NULL,NULL);if (CL_SUCCESS != iStatus){cout << "Error: Can not run kernel" << endl;}// -------------------将输出读取到主机内存-----------------------iStatus = clEnqueueReadBuffer(gpu.CommandQueue,      // 命令队列memOutputBuffer, // 输出内存对象CL_TRUE,           // 内核读取结束之前该函数不会返回0,ARRAY_SIZE * sizeof(int),arrOut,0,NULL,NULL);if (CL_SUCCESS != iStatus){cout << "Error: Can not reading result buffer" << endl;}

9、释放资源

大多数的 OpenCL 资源都是指针,不使用的时候需要释放掉。当然,程序关闭的时候这些对象也会被自动释放掉。
释放资源的函数是:clRelase{Resource} ,比如: clReleaseProgram(), clReleaseMemObject() 等。

// -----------------------释放资源--------------------------------iStatus = clReleaseKernel(gpu.Kernel);iStatus = clReleaseProgram(gpu.Program);iStatus = clReleaseMemObject(memInputBuffer);iStatus = clReleaseMemObject(memOutputBuffer);iStatus = clReleaseMemObject(memOutputBuffer);iStatus = clReleaseCommandQueue(gpu.CommandQueue);iStatus = clReleaseContext(gpu.Context);if (NULL != gpu.pDevices){free(gpu.pDevices);gpu.pDevices = NULL;}

程序实例,向量相加

add.cl

__kernel void Vecadd(__global const float *a,__global const float *b,__global float *c)
{int id=get_global_id(0);c[id]=a[id]+b[id];
}

这里在头文件里定义了一个结构体,这样用起来清晰一点

test.h

#pragma once
#include "CL/cl.h"
#include<string>
#include<opencv2/opencv.hpp>
#include<math.h>using namespace std;
using namespace cv;#define ARRAY_SIZE  10struct GPUInfo
{cl_platform_id Platform = NULL;       // 选择的平台    char            *pName = NULL;     // 平台版本名cl_uint         uiNumDevices = 0;      // 设备数量cl_device_id *pDevices = NULL;      // 设备cl_context     Context = NULL;        // 设备环境cl_command_queue CommandQueue = NULL;       // 命令队列const char       *pFileName = "add.cl";   // cl文件名cl_program      Program = NULL;        // 程序对象cl_kernel        Kernel = NULL;     // 内核对象size_t           uiGlobal_Work_Size[1] = { 0 }; // 用于设定内核分布
};//将文件内容转换成字符串
cl_int ConvertToString(const char *pFileName, std::string &Str);
//GPU 前期初始化
void gpuinfo(GPUInfo &gpu);
//向量相加
void vecadd(GPUInfo &gpu);

learn.cpp

// OpenCl-1.cpp : Defines the entry point for the console application.
//
#include<math.h>
#include <CL/cl.h>
#include <iostream>
#include <fstream>
#include "test.h"
#include<opencv2/opencv.hpp>
#include<opencv.hpp>using namespace std;
using namespace cv;
#pragma warning( disable : 4996 )
#define ARRAY_SIZE  10cl_int ConvertToString(const char *pFileName, std::string &Str)
{size_t     uiSize = 0;size_t      uiFileSize = 0;char        *pStr = NULL;std::fstream fFile(pFileName, (std::fstream::in | std::fstream::binary));if (fFile.is_open()){fFile.seekg(0, std::fstream::end);uiSize = uiFileSize = (size_t)fFile.tellg();  // 获得文件大小fFile.seekg(0, std::fstream::beg);pStr = new char[uiSize + 1];if (NULL == pStr){fFile.close();return 0;}fFile.read(pStr, uiFileSize);                // 读取uiFileSize字节fFile.close();pStr[uiSize] = '\0';Str = pStr;delete[] pStr;return 0;}cout << "Error: Failed to open cl file\n:" << pFileName << endl;return -1;
}void gpuinfo(GPUInfo &gpu)
{//-------------------1. 获得并选择可用平台-----------------------------// 获得平台数量cl_int          iStatus = 0;       // 函数返回状态cl_uint            uiNumPlatforms = 0;        // 平台个数iStatus = clGetPlatformIDs(0, NULL,&uiNumPlatforms); // 查询可用的平台个数,并返回状态cout << "一共有平台:" << uiNumPlatforms << endl;if (CL_SUCCESS != iStatus){cout << "Error: Getting platforms error" << endl;}// 获得平台地址if (uiNumPlatforms > 0)  // 如果有可用平台{// 根据平台数为平台分配内存空间cl_platform_id *pPlatforms = (cl_platform_id *)malloc(uiNumPlatforms * sizeof(cl_platform_id));iStatus = clGetPlatformIDs(uiNumPlatforms, pPlatforms, NULL);     // 获得可用的平台//---------------------输出所有平台信息------------------------------//for (int i = 0; i < uiNumPlatforms; i++)//{//    iErr = clGetPlatformInfo(pPlatforms[i], CL_PLATFORM_VERSION, 0, NULL, &uiSize);            // 获得平台版本名的字节数//    pName = (char *)alloca(uiSize * sizeof(char));                                     // 根据字节数为平台版本名分配内存空间//  iErr = clGetPlatformInfo(pPlatforms[i], CL_PLATFORM_VERSION, uiSize, pName, NULL);     // 获得平台版本名字//   cout << pName << endl;//}//--------------------------------------------------------------------gpu.Platform = pPlatforms[0];                                           // 获得第一个平台的地址free(pPlatforms);                                                  // 释放平台占用的内存空间}// 获得平台版本名cl_int         iErr = 0;      // 返回参数size_t           uiSize = 0;        // 平台版本名字字节数    iErr = clGetPlatformInfo(gpu.Platform, CL_PLATFORM_VERSION, 0, NULL, &uiSize);         // 获得平台版本名的字节数gpu.pName = (char *)alloca(uiSize * sizeof(char));                                       // 根据字节数为平台版本名分配内存空间iErr = clGetPlatformInfo(gpu.Platform, CL_PLATFORM_VERSION, uiSize, gpu.pName, NULL);      // 获得平台版本名字cout << "所选平台OpenCL版本:" << gpu.pName << endl;//--------------2. 查询GPU设备,并选择可用设备------------------------iStatus = clGetDeviceIDs(gpu.Platform, CL_DEVICE_TYPE_GPU, 0, NULL, &gpu.uiNumDevices);        // 获得GPU设备数量if (0 == gpu.uiNumDevices)            // 如果没有GPU设备{cout << "No GPU device available." << endl;cout << "Choose CPU as default device." << endl;iStatus = clGetDeviceIDs(gpu.Platform, CL_DEVICE_TYPE_CPU, 0, NULL, &gpu.uiNumDevices);  // 选择CPU作为设备,获得设备数gpu.pDevices = (cl_device_id *)malloc(gpu.uiNumDevices * sizeof(cl_device_id));          // 为设备分配空间iStatus = clGetDeviceIDs(gpu.Platform, CL_DEVICE_TYPE_CPU, gpu.uiNumDevices, gpu.pDevices, NULL);  // 获得平台}else{gpu.pDevices = (cl_device_id *)malloc(gpu.uiNumDevices * sizeof(cl_device_id));//选择GPU作为设备iStatus = clGetDeviceIDs(gpu.Platform, CL_DEVICE_TYPE_GPU, gpu.uiNumDevices, gpu.pDevices, NULL);}// -------------------3.创建设备环境---------------------------------cl_context_properties  contextProperties[] = { CL_CONTEXT_PLATFORM,(cl_context_properties)gpu.Platform,0 };gpu.Context = clCreateContext(contextProperties, 1, gpu.pDevices, NULL, NULL, NULL);  // 创建设备环境if (NULL == gpu.Context){cout << "Error: Can not create context" << endl;}// -------------------4.创建命令队列--------------------------------------gpu.CommandQueue = clCreateCommandQueue(gpu.Context, gpu.pDevices[0], 0, NULL); // 创建第1个设备的命令队列if (NULL == gpu.CommandQueue){cout << "Error: Can not create CommandQueue" << endl;}// ----------------------5. 创建程序对象------------------------------string           strSource = "";      // 用于存储cl文件中的代码const char       *pSource = NULL;       // 代码字符串指针size_t            uiArrSourceSize[] = { 0 };     // 代码字符串长度iStatus = ConvertToString(gpu.pFileName, strSource);// 将cl文件中的代码转为字符串pSource = strSource.c_str();       // 获得strSource指针uiArrSourceSize[0] = strlen(pSource);  // 字符串大小gpu.Program = clCreateProgramWithSource(gpu.Context, 1, &pSource, uiArrSourceSize, NULL);  // 创建程序对象if (NULL == gpu.Program){cout << "Error: Can not create program" << endl;}// -----------------------------6. 编译程序--------------------------------iStatus = clBuildProgram(gpu.Program, 1, gpu.pDevices, NULL, NULL, NULL);    // 编译程序if (CL_SUCCESS != iStatus)   // 编译错误{cout << "Error: Can not build program" << endl;char szBuildLog[16384];clGetProgramBuildInfo(gpu.Program, *gpu.pDevices, CL_PROGRAM_BUILD_LOG, sizeof(szBuildLog), szBuildLog, NULL);cout << "Error in Kernel: " << endl << szBuildLog;clReleaseProgram(gpu.Program);}
}void vecadd(GPUInfo &gpu){//-------------------------7. 并创建输入输出内核内存对象--------------------------------// 设置数组数据int              arrInA[ARRAY_SIZE];         // 输入数组Aint             arrInB[ARRAY_SIZE];         // 输入数组Bint             arrOut[ARRAY_SIZE];         // 输出数组for (int i = 0; i < ARRAY_SIZE; i++){arrInA[i] = i;arrInB[i] = 2 * i;}// 创建输入内存对象Acl_mem memInputBuffer = clCreateBuffer(gpu.Context,CL_MEM_READ_ONLY | CL_MEM_COPY_HOST_PTR,  // 输入内存为只读,并可以从宿主机内存复制到设备内存sizeof(cl_float)*ARRAY_SIZE,         // 输入内存空间大小(void*)arrInA,NULL);// 创建输入内存对象Bcl_mem memmaskBuffer = clCreateBuffer(gpu.Context,CL_MEM_READ_ONLY | CL_MEM_COPY_HOST_PTR,  // 输入内存为只读,并可以从宿主机内存复制到设备内存sizeof(cl_float)*ARRAY_SIZE,              // 输入内存空间大小(void*)arrInB,NULL);// 创建输出内存对象cl_mem memOutputBuffer = clCreateBuffer(gpu.Context,CL_MEM_WRITE_ONLY,                         // 输出内存只能写sizeof(cl_float)*ARRAY_SIZE,                  // 输出内存空间大小NULL,NULL);if ((NULL == memInputBuffer) || (NULL == memmaskBuffer) || (NULL == memOutputBuffer)){cout << "Error creating memory objects" << endl;}//--------------------------8. 创建内核对象-------------------------------------gpu.Kernel = clCreateKernel(gpu.Program,"Vecadd",  // cl文件中的入口函数NULL);if (NULL == gpu.Kernel){cout << "Error: Can not create kernel" << endl;}//----------------------------9. 设置内核参数----------------------------------cl_int         iStatus = 0;       // 函数返回状态iStatus = clSetKernelArg(gpu.Kernel,0,        // 参数索引sizeof(cl_mem),(void *)&memInputBuffer);iStatus = clSetKernelArg(gpu.Kernel,1,      // 参数索引sizeof(cl_mem),(void *)&memmaskBuffer);iStatus |= clSetKernelArg(gpu.Kernel, 2, sizeof(cl_mem), (void *)&memOutputBuffer);if (CL_SUCCESS != iStatus){cout << "Error setting kernel arguments" << endl;}// --------------------------10.运行内核---------------------------------const size_t global_Work_Size[1] = {ARRAY_SIZE};            // 输入数组大小const size_t local_Work_Size[1] = { 1 };// 利用命令队列使将再设备上执行的内核排队iStatus = clEnqueueNDRangeKernel(gpu.CommandQueue,gpu.Kernel,1,NULL,global_Work_Size,  // 确定内核在设备上的多个处理单元间的分布local_Work_Size,               // 确定内核在设备上的多个处理单元间的分布0,NULL,NULL);if (CL_SUCCESS != iStatus){cout << "Error: Can not run kernel" << endl;}// -------------------11. 将输出读取到主机内存-----------------------iStatus = clEnqueueReadBuffer(gpu.CommandQueue,      // 命令队列memOutputBuffer, // 输出内存对象CL_TRUE,           // 内核读取结束之前该函数不会返回0,ARRAY_SIZE * sizeof(int),arrOut,0,NULL,NULL);if (CL_SUCCESS != iStatus){cout << "Error: Can not reading result buffer" << endl;}// ---------------------12--输出计算结果---------------cout << "ArrayC = ArrayA + ArrAyB" << endl;for (int i = 0; i < ARRAY_SIZE; i++){cout << arrOut[i] << " = " << arrInA[i] << "  +  " << arrInB[i] << endl;}// -----------------------13. 释放资源--------------------------------iStatus = clReleaseKernel(gpu.Kernel);iStatus = clReleaseProgram(gpu.Program);iStatus = clReleaseMemObject(memInputBuffer);iStatus = clReleaseMemObject(memOutputBuffer);iStatus = clReleaseMemObject(memOutputBuffer);iStatus = clReleaseCommandQueue(gpu.CommandQueue);iStatus = clReleaseContext(gpu.Context);if (NULL != gpu.pDevices){free(gpu.pDevices);gpu.pDevices = NULL;}}

main.cpp

#include <CL/cl.h>
#include <iostream>
#include <fstream>
#include "test.h"
#include<opencv2/opencv.hpp>
#include<math.h>using namespace std;
using namespace cv;
#pragma warning( disable : 4996 )int main(int argc, char* argv[])
{GPUInfo gpu1;gpuinfo(gpu1);vecadd(gpu1);
}

定义了一维数组的大小为10,运行结果如下

OpenCL学习笔记——整体流程(向量相加)相关推荐

  1. 计算机图形学基础学习笔记-其一:向量与线性代数

    计算机图形学基础学习笔记-其一:向量与线性代数 前言 计算机图形学概述 向量(矢量) 点乘 叉乘 点乘,叉乘与直角坐标系 矩阵 前言 GAMES101现代计算机图形学入门的学习笔记 正在为TA实习攒作 ...

  2. 狂神说学习笔记 Java流程控制

    目录 Java流程控制 1.用户交互Scanner Scanner对象 next() nextLine(): 2.顺序结构 3.选择结构 4.循环结构 5.Break & Continue 6 ...

  3. 小梅哥FPGA学习笔记——开发流程及仿真示例

    开发流程及仿真示例 FPGA整体设计开发流程 1. 设计定义 2. 设计输入(Quartus II) 3. 分析和综合(Quartus II) 4. 功能仿真(modelsim-altera/mode ...

  4. OpenCL学习笔记(三):OpenCL安装,编程简介与helloworld

    欢迎转载,转载请注明:本文出自Bin的专栏blog.csdn.net/xbinworld. 技术交流QQ群:433250724,欢迎对算法.技术.应用感兴趣的同学加入. OpenCL安装 安装我不打算 ...

  5. OpenCL学习笔记(一):摩尔定律,异构计算与OpenCL初印象

    欢迎转载,转载请注明:本文出自Bin的专栏blog.csdn.net/xbinworld. 技术交流QQ群:433250724,欢迎对算法.技术.应用感兴趣的同学加入. 关于摩尔定律: 摩尔定律196 ...

  6. python中向量长度_Python线性代数学习笔记——什么是向量?实现我们自己的向量...

    上一篇已经说明了为什么要学习线性代数? 下面我们正式开始学习 一切从向量开始 什么是向量,究竟为什么引⼊向量? 从研究一个数,变成了研究一组数,向量表示的就是一组数 学过中学物理都知道力,速度,加速度 ...

  7. OpenCL学习笔记一

    什么是OpenCL 百度百科飞机票✈ OpenCL是一个为异构平台编写程序的框架,此异构平台可由CPU,GPU或其他类型的处理器组成.OpenCL由一门用于编写kernels (在OpenCL设备上运 ...

  8. PaddlePaddle NLP学习笔记1 词向量

    文章目录 1.语言模型 Language Model 1.1 语言模型是什么 1.2 语言模型计算什么 1.3 n-gram Language Model 2.神经网络语言模型NNLM 2.1 N-g ...

  9. Python学习笔记3 流程控制、迭代器、生成器

    第3章 流程控制.迭代器.生成器 3.1 选择语句 1.语法:(1)if -else (2)if-elif-else 2.注意:(1)每个条件后面要使用冒号:(2)使用缩进划分语句块(3)python ...

最新文章

  1. [解决方案记录]No module named fused(stylegan2的bug,已更新)
  2. 5G NGC — 关键技术 — R15 SBA
  3. cuda 安装_win10+VS 2017 安装 CUDA(Visual Studio Integration失败)
  4. Lua coroutine 不一样的多线程编程思路
  5. python序列_什么是Python的序列协议?
  6. asp.net core 腾讯验证码的接入
  7. 「工具」IndexDB 版备忘录
  8. 【云栖大会】用爱成就彼此 距云栖大会还有10天
  9. 机器分配(信息学奥赛一本通-T1266)
  10. 1 利用Anaconda完美解决Python 2与python 3的共存问题
  11. html字段值换行代码怎么写,HTML段落,换行,字符实体
  12. 【thinkphp3.x】ThinkPHP/Lib/Core/Model.class.php文件分析
  13. 二分法求函数方程根的matlab实现(内附例题和代码)
  14. java 反射如何给属性赋值_关于Java属性反射的异常问题
  15. 敲笨钟(古诗词押 ong 韵)
  16. 八爪鱼抓取html,网页图片采集和抓取方法详解 - 八爪鱼采集器
  17. Microsoft Visual SourceSafe 6.0 关联VS
  18. Python语言程序设计基础_实验四_函数(二)_答案_通识教育必修课程_上海师范大学
  19. 谈谈Java的学习方法
  20. k8s——pv(静态+动态storageclass)与pvc

热门文章

  1. 将Spine动画导入unity
  2. java权限控制详解,比较全
  3. win10装系统时分区报错解决方法 无法找到新的分区也找不到现有分区
  4. P1981 [NOIP2013 普及组] 表达式求值
  5. 树莓派4B安装OpenCV教程
  6. 全国主要地级市按拼音排序json数据
  7. 回调函数,回调函数,回调函数,什么是回调函数
  8. nvm下载node版本缓慢问题
  9. json数据的两种格式及两种json方法
  10. 线性反馈移位寄存器(LSFR)