1. 选择OpenCL平台并创建一个上下文

平台(Platform)是指主机和OpenCL管理框架下的若干个设备构成的可以运行OpenCL程序的完整硬件系统,这个是跑OpenCL程序的基础,所以第一步要选择一个可用的OpenCL品台。一台机器上可以有不止一个这样的品台,一个平台也可以有不止一个GPU。

主要涉及的函数: clGetPlatformIDs() ,用于获取可用的平台;

clCreateContextFromType(), 创建一个OpenCL运行时山下文环境;

2. 选择设备并创建命令队列

选择平台并创建好OpenCL上下文环境之后,要做的事选择运行时用到的设备,还要创建一个命令队列,命令队列里定义了设备要完成的操作,以及各个操作的运行次序。

主要涉及的函数:clCreateCommandQueue(),用于创建一个指定设备上的上下文环境,第二个参数定义了选择的设备。

3. 创建和构建程序对象

程序对象用来存储与上下文相关联的设备的已编译可执行代码,同时也完成内核源代码的加载编译工作。

主要涉及的函数:clCreateProgramWithSource(), 这个函数会创建一个程序对象,在创建的同时,把已经转化成字符串形式的内核源代码加载到该程序对象中。

clBuildProgram()用于编译指定程序对象中的内核源代码,编译成功之后,再把编译代码存储在程序对象中。

4. 创建内核和内存对象

要执行程序对象中的已编译成功的内核运算,需要在内存中创建内核并分配内核函数的参数,在GPU上定义内存对象并分配存储空间。

主要涉及的函数:clCreateKernel(), 创建内核;

clCreateBuffer(),分配内存对象的存储空间,这些对象可以由内核函数直接访问。

5. 设置内核数据并执行内核

创建内核和内存对象之后,接下来要设置核函数的数据,并将要执行的内核排队。

主要涉及的函数:clEnqueueNDRangeKernel(),用于设置内核函数的所有参与运算的数据。    利用命令队列对要在设备上执行的内核排队。需要注意的是,执行内核排队之后并不意味着这个内核一定会立即执行,只是排队到了执行队列中。

6. 读取执行结果并释放OpenCL资源

内核执行完成之后,需要把数据从GPU拷贝到CPU中,供主机进一步处理,所有者写工作完成之后需要释放所有的OpenCL资源。

主要涉及的函数:clEnqueueReadBuffer(),读取设备内存数据到主机内存;

  clReleaseXXX(),释放OpenCL资源。

以下程序包含了以上所有6个步骤,功能很简单,实现两个数组求和。

主程序:

#include <iostream>
#include <fstream>
#include <sstream>
#include <CL/cl.h> const int ARRAY_SIZE = 1000; //一、 选择OpenCL平台并创建一个上下文
cl_context CreateContext()
{
cl_int errNum;
cl_uint numPlatforms;
cl_platform_id firstPlatformId;
cl_context context = NULL; //选择可用的平台中的第一个
errNum = clGetPlatformIDs(1, &firstPlatformId, &numPlatforms);
if (errNum != CL_SUCCESS || numPlatforms <= 0)
{
std::cerr << "Failed to find any OpenCL platforms." << std::endl;
return NULL;
} //创建一个OpenCL上下文环境
cl_context_properties contextProperties[] =
{
CL_CONTEXT_PLATFORM,
(cl_context_properties)firstPlatformId,
0
};
context = clCreateContextFromType(contextProperties, CL_DEVICE_TYPE_GPU,
NULL, NULL, &errNum); return context;
} //二、 创建设备并创建命令队列
cl_command_queue CreateCommandQueue(cl_context context, cl_device_id *device)
{
cl_int errNum;
cl_device_id *devices;
cl_command_queue commandQueue = NULL;
size_t deviceBufferSize = -1; // 获取设备缓冲区大小
errNum = clGetContextInfo(context, CL_CONTEXT_DEVICES, 0, NULL, &deviceBufferSize); if (deviceBufferSize <= 0)
{
std::cerr << "No devices available.";
return NULL;
} // 为设备分配缓存空间
devices = new cl_device_id[deviceBufferSize / sizeof(cl_device_id)];
errNum = clGetContextInfo(context, CL_CONTEXT_DEVICES, deviceBufferSize, devices, NULL); //选取可用设备中的第一个
commandQueue = clCreateCommandQueue(context, devices[0], 0, NULL); *device = devices[0];
delete[] devices;
return commandQueue;
} // 三、创建和构建程序对象
cl_program CreateProgram(cl_context context, cl_device_id device, const char* fileName)
{
cl_int errNum;
cl_program program; std::ifstream kernelFile(fileName, std::ios::in);
if (!kernelFile.is_open())
{
std::cerr << "Failed to open file for reading: " << fileName << std::endl;
return NULL;
} std::ostringstream oss;
oss << kernelFile.rdbuf(); std::string srcStdStr = oss.str();
const char *srcStr = srcStdStr.c_str();
program = clCreateProgramWithSource(context, 1,
(const char**)&srcStr,
NULL, NULL); errNum = clBuildProgram(program, 0, NULL, NULL, NULL, NULL); return program;
} //创建和构建程序对象
bool CreateMemObjects(cl_context context, cl_mem memObjects[3],
float *a, float *b)
{
memObjects[0] = clCreateBuffer(context, CL_MEM_READ_ONLY | CL_MEM_COPY_HOST_PTR,
sizeof(float) * ARRAY_SIZE, a, NULL);
memObjects[1] = clCreateBuffer(context, CL_MEM_READ_ONLY | CL_MEM_COPY_HOST_PTR,
sizeof(float) * ARRAY_SIZE, b, NULL);
memObjects[2] = clCreateBuffer(context, CL_MEM_READ_WRITE,
sizeof(float) * ARRAY_SIZE, NULL, NULL);
return true;
} // 释放OpenCL资源
void Cleanup(cl_context context, cl_command_queue commandQueue,
cl_program program, cl_kernel kernel, cl_mem memObjects[3])
{
for (int i = 0; i < 3; i++)
{
if (memObjects[i] != 0)
clReleaseMemObject(memObjects[i]);
}
if (commandQueue != 0)
clReleaseCommandQueue(commandQueue); if (kernel != 0)
clReleaseKernel(kernel); if (program != 0)
clReleaseProgram(program); if (context != 0)
clReleaseContext(context);
} int main(int argc, char** argv)
{
cl_context context = 0;
cl_command_queue commandQueue = 0;
cl_program program = 0;
cl_device_id device = 0;
cl_kernel kernel = 0;
cl_mem memObjects[3] = { 0, 0, 0 };
cl_int errNum; // 一、选择OpenCL平台并创建一个上下文
context = CreateContext(); // 二、 创建设备并创建命令队列
commandQueue = CreateCommandQueue(context, &device); //创建和构建程序对象
program = CreateProgram(context, device, "HelloWorld.cl"); // 四、 创建OpenCL内核并分配内存空间
kernel = clCreateKernel(program, "hello_kernel", NULL); //创建要处理的数据
float result[ARRAY_SIZE];
float a[ARRAY_SIZE];
float b[ARRAY_SIZE];
for (int i = 0; i < ARRAY_SIZE; i++)
{
a[i] = (float)i;
b[i] = (float)(ARRAY_SIZE - i);
} //创建内存对象
if (!CreateMemObjects(context, memObjects, a, b))
{
Cleanup(context, commandQueue, program, kernel, memObjects);
return 1;
} // 五、 设置内核数据并执行内核
errNum = clSetKernelArg(kernel, 0, sizeof(cl_mem), &memObjects[0]);
errNum |= clSetKernelArg(kernel, 1, sizeof(cl_mem), &memObjects[1]);
errNum |= clSetKernelArg(kernel, 2, sizeof(cl_mem), &memObjects[2]); size_t globalWorkSize[1] = { ARRAY_SIZE };
size_t localWorkSize[1] = { 1 }; errNum = clEnqueueNDRangeKernel(commandQueue, kernel, 1, NULL,
globalWorkSize, localWorkSize,
0, NULL, NULL); // 六、 读取执行结果并释放OpenCL资源
errNum = clEnqueueReadBuffer(commandQueue, memObjects[2], CL_TRUE,
0, ARRAY_SIZE * sizeof(float), result,
0, NULL, NULL); for (int i = 0; i < ARRAY_SIZE; i++)
{
std::cout << result[i] << " ";
}
std::cout << std::endl;
std::cout << "Executed program succesfully." << std::endl;
getchar();
Cleanup(context, commandQueue, program, kernel, memObjects); return 0;
}

核函数文件“HelloWorld.cl”:

__kernel void hello_kernel(__global const float *a,
__global const float *b,
__global float *result)
{
int gid = get_global_id(0); result[gid] = a[gid] + b[gid];
}

执行结果:

OpenCL编程基本流程及完整示例的更多相关文章

  1. Spring 3 AOP 概念及完整示例

    AOP概念 AOP(Aspect Oriented Programming),即面向切面编程(也叫面向方面编程,面向方法编程).其主要作用是,在不修改源代码的情况下给某个或者一组操作添加额外的功能.像 ...

  2. C连接MySQL数据库开发之Linux环境完整示例演示(增、删、改、查)

    一.开发环境 ReadHat6.3 32位.mysql5.6.15.gcc4.4.6 二.编译 gcc -I/usr/include/mysql -L/usr/lib -lmysqlclient ma ...

  3. springmvc 项目完整示例04 整合mybatis mybatis所需要的jar包 mybatis配置文件 sql语句 mybatis应用

    百度百科: MyBatis 本是apache的一个开源项目iBatis, 2010年这个项目由apache software foundation 迁移到了google code,并且改名为MyBat ...

  4. Slickflow.NET 开源工作流引擎快速入门之一: 简单序列流程代码编写示例

    前言:对于急切想了解引擎功能的开发人员,在下载版本后,就想尝试编写代码,完成一个流程的开发和测试.本文试图从一个最简单的流程来示例说明,如何快速了解引擎代码的编写. 版本: .NETCore 2.1 ...

  5. Eclipse、MinGW、JNI编写C++生成dll, Java端调用的完整示例(附java.lang.UnsatisfiedLinkError解决方法)

     Eclipse.MinGW.JNI编写C++生成dll, Java端调用的完整示例(附java.lang.UnsatisfiedLinkError解决方法) 问题背景:之前的JNI编程都是基于And ...

  6. springboot2.2 集成 activity6 请假完整示例

    新手学习记录.写在springboot test 示例  示例代码地址看结尾.后面有带页面的示例. SpringBoot Test无页面简单示例 员工请假流程 员工发起申请,附带请假信息(请假几天) ...

  7. 【第四篇】ASP.NET MVC快速入门之完整示例(MVC5+EF6)

    目录 [第一篇]ASP.NET MVC快速入门之数据库操作(MVC5+EF6) [第二篇]ASP.NET MVC快速入门之数据注解(MVC5+EF6) [第三篇]ASP.NET MVC快速入门之安全策 ...

  8. WCF服务开发与调用的完整示例

    WCF服务开发与调用的完整示例 开发工具:VS2008 开发语言:C# 开发内容:简单的权限管理系统 第一步.建立WCF服务库 点击确定,将建立一个WCF 服务库示例程序,自动生成一个包括IServi ...

  9. springmvc 项目完整示例06 日志–log4j 参数详细解析 log4j如何配置

    Log4j由三个重要的组件构成: 日志信息的优先级 日志信息的输出目的地 日志信息的输出格式 日志信息的优先级从高到低有ERROR.WARN. INFO.DEBUG,分别用来指定这条日志信息的重要程度 ...

随机推荐

  1. StackExchange.Redis 官方文档(五) Keys, Values and Channels

    原文:StackExchange.Redis 官方文档(五) Keys, Values and Channels Keys, Values and Channels 在使用redis的过程中,要注意到 ...

  2. 【AtCoder Beginner Contest 074 B】Collecting Balls (Easy Version)

    [链接]h在这里写链接 [题意] 看懂题目之后就会发现是道大水题. [题解] 在这里写题解 [错的次数] 0 [反思] 在这了写反思 [代码] #include <bits/stdc++.h&g ...

  3. Log4net.confager配置

    <?xml version="1.0" encoding="utf-8"?><log4net>  <level value=&qu ...

  4. 表单提交数据格式form data

    前言: 最近遇到的最多的问题就是表单提交数据格式问题了. 常见的三种表单提交数据格式,分别举例说明:(项目是vue的框架) 1.application/x-www-form-urlencoded 提交 ...

  5. ios sqlite数据库操作

    @interface MyViewController () { // 数据库实例,代表着整个数据库 sqlite3 *_db; } @end @implementation MyViewContro ...

  6. tomcat总体架构

    Tomcat 总体结构图 从上图中可以看出Tomcat的心脏是两个组件:Connector 和 Container,关于这两个组件将在后面详细介绍.Connector 组件是可以被替换,这样可以提供给 ...

  7. PHP通用非法字符检测函数集锦

    <? // [变量定义规则]:‘C_’=字符型,‘I_’=整型,‘N_’=数字型,‘L_’=布尔型,‘A_’=数组型 // ※CheckMoney($C_Money) 检查数据是否是 99999 ...

  8. oracle 重置序列从指定数字开始的方法详解

    原文 oracle 重置序列从指定数字开始的方法详解 重置oracle序列从指定数字开始 declare n ); v_startnum ):;--从多少开始 v_step ):;--步进 tsql ...

  9. Tricks(四十九)—— 按 batch 访问越界的解决办法

    使用 min 函数指定访问的最终位置,本质上是增加一个条件判断: done = false; batch_size = 10000; idx = 1; while ~done idx_end = mi ...

  10. SetProcessWorkingSetSize() 方法使内存降低了很多(把内存放到交换区,其实会降低性能)——打开后长时间不使用软件,会有很长时间的加载过程,原来是这个!

    在项目中对程序性能优化时,发现用SetProcessWorkingSetSize() 方法使内存降低了很多,于是查阅了相关的资料如下: 我的程序为什么能够将占用的内存移至虚拟内存呢? 其实,你也可以, ...