OpenCL简介

    开放计算语言(Open Computing Language, OpenCL)是非盈利技术联盟Khronos Group管理的异构编程框架。该框架充分利用了CPU、DSP、FPGA、GPU的计算能力。OpenCL支持多层次的并行,可以高效的映射到同构或异构的体系结构上。

OpenCL标准

    OpenCL API是按照C 的,由C和C++封装而成,并且有很多第三方语言的绑定。这些语言包括Java、Python以及.NET等。OpenCL是C99语言的子集,并适当地扩展了在众多异构设备上执行数据并行代码的能力。
    OpenCL和许多CPU并发编程模型一样,语法类似于标准的C函数,主要区别在于它拥有额外的一些关键字和OpenCL Kernel实现的执行模型。在OpenCL编程中,编程人员应该考虑的是如何细粒度地表示程序中的并行性。一个典型的OpenCL Kernel应该是这样的:

<span style="font-size:10px;">_kernel void vecadd(_global int *C, _global int *A, _global int *C)
{int tid = get_global_id(0);    //OpenCL intrisic functionC[tid] = A[tid] + B[tid];
}</span>

OpenCL规范

    OpenCL规范由四个模块组成:
  1. 平台模型:定义了一个抽象的硬件模型,描述了宿主机和设备。供编程人员在上面编写在这些设备上执行的Kernel。
  2. 执行模型:定义了如何在宿主机上配置OpenCL环境以及如何在设备上执行Kernel。
  3. 内存模型:定义了Kernel中所使用的内存层次,无需考虑实际的底层架构。
  4. 编程模型:定义了如何将并发模型映射到物理硬件上。

平台模型

    如下图描述,平台模型由一个宿主机以及一个或多个设备组成,一个设备可以分为多个CUs(Compute Units),一个CU又可以进一步划分为多个PEs(Processing Elements)。设备上的计算具体映射到了每个PE之上。
    在平台模型中编程人员需要关注三种版本号:平台版本号、设备版本号、OpenCL支持版本号。
    平台版本号表明了平台所支持的运行时功能,包括了OpenCL运行时所有与宿主机交互的APIs,例如上下文、内存对象、设备、命令队列相关的函数。
    设备版本号表明了设备的能力,可以使用clGetDeviceInfo函数获取。设备信息通常包括资源限制和扩展功能等。
    语言版本号表明了设备最高支持的语言版本。

执行模型

    OpenCL的执行模型分为 两部分,Kernel Program和Host Program,宿主程序定义了Kernel执行的上下文并控制其执行。执行模型中主要定义了kernel是怎样执行的。
    这里首先讲一下执行模型中非常重要的两个概念——work-itemwork-groups。在kernel由宿主机提交给设备执行的同时,定义了一个索引空间。每个work-item可以看做是kernel在设备上单次执行的实例,该实例在索引空间中拥有唯一的索引值。基于索引值的不同,每个work-item虽然执行同样的kernel程序,但却有自己的代码路径和数据路径。work-item被组织为work-groups。在同一work-groups中每个work-item拥有唯一的local ID,如果有多个work-groups,那么在不同的work-groups之间local ID不是唯一的。一个work-item索引必须采用global ID或local ID + work-groups ID才能唯一确定。
    
    在执行模型中另一个非常重要的概念是NDRange,即N维索引空间,这里的N可以定义为1,2或3。它由一个长度为N的数组组成,每个数组元素表示该维度上工作节点的个数。工作节点的global ID、local ID以及work-groups ID都是N维的。上图是一个二维索引空间的实例,NDRange(Gx,Gy),对应的工作组为NDRange(Sx,Sy)。每个维度的工作空间恰好分配为整数个工作组。这里要求Gx必须为Sx的整数倍,Gy必须为Sy的整数倍。

上下文与命令队列

    在OpenCL中,上下文(context)是一个存在于主机端的抽象容器。负责协调主机——设备之间的交互,管理设备上可用的内存对象,跟踪每个设备建立的kernel和程序。
    宿主机负责定义kernel的上下文,上下文包括以下资源:
  1. 设备:平台上可用的设备。
  2. Kernels:运行在设备上的OpenCL C程序。
  3. 程序对象:负责执行和实现Kernels的程序资源。
  4. 内存对象:Kernel和宿主机可见的内存对象的集合,内存对象中包含kernel实例中用于计算的数据。
    新建上下文的API为clCreateContext()。
cl_context
clCreateContext(const cl_context_properties *properties,cl_unit num_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)

API函数clCreateCommandQueue()用来建立命令队列并将其关联到某个设备。

cl_command_queue
clCreateCommandQueue(cl_context contextcl_device_id device,cl_command_queue_properties properties,cl_int *errcode_ret)

clCreateCommandQueue()中的属性参数properties是一个位域,用于开启程序剖析(profiling)命令(CL_QUEUE_PROFILING_ENABLE)、乱序执行命令(CL_QUEUE_OUT_OF_ORDER_EXEC_ENABLE)。

内存对象

    OpenCL应用程序用来处理大型数组或多维矩阵,在kernel开始执行之前需要将这些数据实际映射到设备上。在OpenCL中采用的方法是将这些数据封装为内存对象。OpenCL定义了两种内存对象:buffer和image。buffer类似于C语言中的数组,由malloc()分配,在内存中是连续存放的。而image被设计为不透明的对象,以利于进行数据填充和在特定的设备上进行性能优化。
    创建buffer的内存对象的API为clCreateBuffer(),内存对象作为返回值返回。
cl_mem clCreateBuffer(cl_context context,cl_mem_flags flags,size_t size,void *host_ptr,cl_int *errcode_ret)

将宿主机内存传入或传出设备的API分别为clEnqueueWriteBuffer和clEnqueueReadBuffer。可通过设置参数选项blocking_write为CL_TRUE,使写函数工作在阻塞模式——数据完成到OpenCL buffer的传输后才返回。

image对象在后面用到的时候在详细介绍。

程序对象

    OpenCL中的程序(program)特指kernel函数的集合,是kernel被调度安排到设备上运行的单位。
    OpenCL程序运行时通过调用一系列。API进行编译,编译系统对具体设备进行优化。OpenCL软件仅链接到公共运行层(称为ICD),所有平台特定的SDK通过一个动态链接接口委托给某个厂商的运行时。
    新建kernel的步骤如下:
  1. OpenCL源代码以字符串形式存储。如果代码存放在文件中,则必须读取到内存中,在内存中以字符串数组的形式存放。
  2. 源代码通过调用clCreateProgramWithSource()转化为cl_program对象。
  3. 调用clBuildProgram()在支持OpenCL的设备上编译程序对象。

向量加代码演示

#include<stdio.h>
#include<stdlib.h>
#include<CL/cl.h>const char *programSource =
"_kernel void vecadd(_global int *A, _global int *B, _global int *C) \n"
"{                                                                   \n"
"  int idx = get_global_id(0);                                  \n"
"  C[idx] = A[idx] + B[idx];                                    \n"
"}                                                                   \n"    int main()
{int *A = NULL;int *B = NULL;int *C = NULL;const int elements = 2048;size_t datasize = sizeof(int) * elements;A = (int*)malloc(datasize);B = (int*)malloc(datasize);C = (int*)malloc(datasize);int i;for(i=0; i<elements; i++){A[i] = i;B[i] = i;}//Check return valuecl_int status;//Retrieve the number of platforms cl_uint numPlatforms = 0;status = clGetPlatformIDs(0, NULL, &numPlatforms);//Allocate space for each platformcl_plarform_id *platforms = NULL;platforms =  (cl_platform_id*)malloc(numPlatforms * sizeof(cl_platform_id));//Fill in the platformsstatus = clGetPlatformIDs(numPlatforms, platforms, NULL);//Retrieve the number of devicescl_unit numDevicdes = 0;status = clGetDeviceIDs(platform[0], CL_DEVICE_TYPE_ALL, 0, NULL, &numDevices);//Allocate space for devicescl_device_id *devices;devices = (cl_device_id*)malloc(numDevices * sizeof(cl_device_id));//Fill in the devicesstatus = clGetDeviceIDs(platform[0], CL_DEVICE_TYPE_ALL, numDevices, deivces, NULL);//Create a context and  associate it with devicescl_context context;context = clCreateContext(NULL, numDevices, devices, NULL, NULL, &status);//Create a command queue and associate it with the devicecl_command_queue cmdQueue;cmdQueue = clCreateCommandQueue(context, devices[0], 0, &status);//Create buffer objectcl_mem bufA = clCreateBuffer(context, CL_MEM_READ_ONLY, datasize, NULL, &status);cl_mem bufB = clCreateBuffer(context, CL_MEM_READ_ONLY, datasize, NULL, &status);cl_mem bufC = clCreateBuffer(context, CL_MEM_READ_ONLY, datasize, NULL, &status);//Write to the device bufferstatus = clEnqueueWriteBuffer(cmdQueue, bufA, CL_FALSE, 0, datasize, A, 0, NULL, NULL);status = clEnqueueWriteBuffer(cmdQueue, bufB, CL_FALSE, 0, datasize, B, 0, NULL, NULL);status = clEnqueueWriteBuffer(cmdQueue, bufC, CL_FALSE, 0, datasize, C, 0, NULL, NULL);//Create program from source codecl_program program = clCreateProgramWithSource(context, 1, (const char**)&programSource, NULL, &status);//Compile the programstatus = clBuildProgram(program, "vacadd", &status);//Associate buffers with the kernelstatus = clSetKernelArg(kernel, 0, sizeof(cl_mem), &bufA);status = clSetKernelArg(kernel, 1, sizeof(cl_mem), &bufB);status = clSetKernelArg(kernel, 2, sizeof(cl_mem), &bufC);//Define index spacesize_t globalWorkSize[1];globalWorkSize[0] = elements;//execute the kernelstatus = clEnqueueNDRangeKernel(cmdQueue, kernle, 1, NULL, globalWorkSize, NULL, 0, NULL, NULL);//Read the device output buffer to the host output array clEnqueueReadBuffer(cmdQueue, bufC, CL_TRUE, 0, datasize, C, 0, NULL, NULL);//Verify the outputint result = 1;for(i=0;i<elements;i++){if(C[i] != i+i){result = 0;break;}}if(result){printf("correct!\n");}else{printf("incorrect!\n");}//Release resourcesclReleaseKernel(kernel);clReleaseProgram(program);clReleaseCommandQueue(cmdQueue);clReleaseMemObject(bufA);clReleaseMemObject(bufB);clReleaseMemObject(bufC);clReleaseContext(context);//Free host resourcesfree(A);free(B);free(C);free(platform);free(devices);
}

OpenCL编程入门(一)相关推荐

  1. 【浅墨著作】 OpenCV3编程入门 内容简介 勘误 配套源代码下载

    分享一下我老师大神的人工智能教程!零基础,通俗易懂!http://blog.csdn.net/jiangjunshow 也欢迎大家转载本篇文章.分享知识,造福人民,实现我们中华民族伟大复兴! 经过近一 ...

  2. 【OpenCV学习】 《OpenCV3编程入门》--毛星云 01 邂逅OpenCV(OpenCV基本概念与基本架构) ROS系统上的运用(python实现)

    对 <OpenCV3编程入门>第一章的学习笔记:理解什么是计算机视觉,什么是OpenCV,以及其中的联系等等. PS:此书为2014年出版,opencv的版本和接口也与现在有些不一致了,作 ...

  3. 【《OpenCV3编程入门》内容简介勘误配套源代码下载

     录 (?) [+] 一前言 二内容安排 三适合阅读本书的读者 四书本目录 第一部分 快速上手OpenCV 1 第1章 邂逅OpenCV 3 1 OpenCV周边概念认知 4 2 OpenCV基本 ...

  4. OpenCL编程详细解析与实例

    OpenCL编程详细解析与实例 C语言与OpenCL的编程示例比较 参考链接: https://www.zhihu.com/people/wujianming_110117/posts 先以图像旋转的 ...

  5. python编程求圆的面积案例_Python实用案例编程入门:第七章 调式手段

    本章的主题为调试手段,这是程序开发必不可少的步骤,也是占用时间最多的环节.在程序员的正常开发工作中,调试工作至少占据1/3的时间,而实际编码工作相对占用实际比较少.因此,无论您是初学者,还是编程兴趣爱 ...

  6. 《C++游戏编程入门(第4版)》——1.12 习题

    本节书摘来自异步社区出版社<C++游戏编程入门(第4版)>一书中的第1章,第1.1节,作者:[美]Michael Dawson(道森),更多章节内容可以访问云栖社区"异步社区&q ...

  7. 编程入门到进大厂,你需要这套学习架构

    我相信大多数学习编程的同学都有着对大公司的憧憬.技术.声望.薪资.福利,这些都足以成为吸引你进入大厂的理由. 但是,如何进入大厂呢? 对于很多同学来说,通往大厂的道路并不明朗,不知道是否有希望,也不知 ...

  8. 《C++游戏编程入门(第4版)》——1.8 Lost Fortune简介

    本节书摘来自异步社区出版社<C++游戏编程入门(第4版)>一书中的第1章,第1.8节,作者:[美]Michael Dawson(道森),更多章节内容可以访问云栖社区"异步社区&q ...

  9. [译]函数式响应编程入门指南

    原文地址:An Introduction to Functional Reactive Programming 原文作者:Daniel Lew 译文出自:掘金翻译计划 本文永久链接:github.co ...

  10. 《C++游戏编程入门(第4版)》——2.4 使用带else子句的if语句序列

    本节书摘来自异步社区出版社<C++游戏编程入门(第4版)>一书中的第2章,第2.4节,作者:[美]Michael Dawson(道森),更多章节内容可以访问云栖社区"异步社区&q ...

最新文章

  1. 2018 年将打响 AI 战争,7 条实战经验帮你战胜恐惧
  2. Java入门系列-16-继承
  3. MySQL------MySQL与SQLServer数据类型的转换
  4. python web flask开发框架_Python Web 开发框架,Flask 与 Django那个更好
  5. 转: 通过Servlet生成验证码图片
  6. 深度学习(二十六)——VAE
  7. 产品原型设计的参考步骤
  8. 作者:廖小飞,博士,华中科技大学计算机科学与技术学院教授、博士生导师。...
  9. PHP内核通用网站后台权限管理系统源码
  10. OutOfMemoryError(内存溢出)解决办法
  11. 内部存储_Mongodb存储特性与内部原理
  12. 深入探讨apply()方法的作用
  13. 为什么图灵奖获得者戴克斯特拉痛恨 BASIC 语言
  14. 如何免费搭建个人网站
  15. 最新图片交替闪现效果代码
  16. 如何拉取钉钉的外出、出差审批单
  17. Excel笔记(持续更新)
  18. 数据结构(C语言) 实验---图及其应用
  19. 海康IPC摄像头通过ONVIF协议接入VCN离线
  20. linux下socket编程带来的尴尬

热门文章

  1. gradle排除jar依赖
  2. jar包解压后再打包为jar
  3. linux下载ed2k资源,linux下迅雷替代方案-linux下载工具
  4. 下载的ContentType设置
  5. 文献阅读(245)Roller
  6. Restlet Introduction
  7. k3导入账套_金蝶k3凭证导入导出的操作方法 金蝶k3操作指南
  8. 2020年值得收藏与学习280多款H5小游戏,从入门到彻底了解它(附源码)
  9. python抓取微博数据_技术入门 | python利用微博api获取数据
  10. [Linux基础]读书笔记-老男孩核心系统命令实战1