OpenCL学习入门

2023-11-11

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

#ifndef TOOLH
#define TOOLH

#include <CL/cl.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <iostream>
#include <string>
#include <fstream>
using namespace std;

/** convert the kernel file into a string */
int convertToString(const char *filename, std::string& s);

/**Getting platforms and choose an available one.*/
int getPlatform(cl_platform_id &platform);

/**Step 2:Query the platform and choose the first GPU device if has one.*/
cl_device_id *getCl_device_id(cl_platform_id &platform);

#endif

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 platform

    cl_uint numPlatforms;//the NO. of platforms
    cl_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);
    }
    else
        return -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); ///wait
    clReleaseEvent(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
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

OpenCL学习入门 的相关文章

随机推荐

  • 《数据分析原理》:6步解决业务分析难题

    点击上方卡片关注我 回复 8 加入数据分析 领地 一起学习数据分析 持续更新数据分析学习路径相关资料 精彩数据观点 学习资料 数据课程分享 读书会 分享会等你一起来乘风破浪 回复 小飞象 领取数据分析知识大礼包 读书交流 7期 数据分析原理
  • IntelliJ IDEA 下载安装教程,超详细图文教程

    1 IDEA 下载 1 打开浏览器输入https www jetbrains com 进入 Jetbrains官网 点击 Developer Tools 再点击 Intellij IDEA 2 点击中间的 Download 进入IDEA下载
  • 分割预研 -- 2022.5

    MMSegmentation MMSegmentation 标准统一的语义分割框架 非常好的分割开源集成框架 https link zhihu com target https 3A github com open mmlab mmsegm
  • 基于 FBXSDK-Python 的动画操作

    PythonFBXSKD 01 基础的动画操作 1 0 下载安装 FBXSDK 我这里演示的是 FBXSDK 2020 2 只有 py37 版本的 FBXSDK 2020 1 1 版本有 py27 和py33 两个版本 根据自己的pytho
  • kubernetes的configmap格式错乱问题

    一 问题 最近发现configmap资源在查看 o yaml 或者修改 edit 时 存在格式错乱问题 以nginx配置文件为例 通过
  • 点对点隧道协议—PPTP部署配置

    1 虚拟专用网 1 1 PPTP介绍 PPTP Point to Point Tunneling Protocol 即点对点隧道协议 该协议是在PPP协议的基础上开发的一种新的加强型安全协议 支持多协议虚拟专用网 能够经过密码验证协议 PA
  • 微服务方法论02--服务划分规则01

    背景 现在微服务比较流程 那么对于微服务的拆分方法也比较让人困惑 本文从不同的角度切入后以系统的 全面的 统一的方式为各位介绍服务拆分的问题 问题定义 服务划分具体的问题在哪里 服务划分是对于具体技术的选择 是选择使用纵向切割的方式 还是使
  • Java集合详解——TreeSet集合的介绍及其排序

    一 TreeSet集合的自动排序 TreeSet集合的继承结构图 1 TreeSet集合使用红黑树数据结构实现元素的排序和存储 底层实际上是一个TreeMap集合 2 Tree Map集合底层实际上是一个二叉树 3 放到TreeSet集合中
  • oVirt虚拟化平台下重置windows10虚拟机的一次神奇体验

    前言 公司一台win10虚拟机密码被改掉了 尝试各种方式无解 密码都不对 这台机器上数据还比较多 于是有了下面的探索 1 重启机器 2 ovirt平台控制台进入机器 点击输入密码 3 按5次Shift键 4 文件 运行新任务 输入cmd 5
  • gitee错误failed to push some refs to

    问题说明 当我们在github版本库中发现一个问题后 你在github上对它进行了在线的修改 或者你直接在github上的某个库中添加readme文件或者其他什么文件 但是没有对本地库进行同步 这个时候当你再次有commit想要从本地库提交
  • 【STM32】HAL库-ADC

    12位ADC是一种逐次逼近型模拟数字转换器 它有多达18个通道 可测量16个外部和2个内部信号源 各通道的A D转换可以单次 连续 扫描或间断模式执行 ADC的结果可以左对齐或右对齐方式存储在16位数据寄存器中 模拟看门狗特性允许应用程序检
  • 商业数据分析的模型

    2 1 KANO分析模型 KANO模型是东京理工大学教授狩野纪昭 Noriaki Kano 发明的对用户需求分类和优先排序的有用工具 该模型是受行为科学家赫兹伯格的双因素理论启发而提出的 体现了产品性能和用户满意之间的非线性关系 主要是通过
  • L3HCTF2021几道简单的签到题

    L3HCTF2021几道简单的签到题 Misc Welcome Web Image1 Web EasyPHP 作者 Hopeace Misc Welcome 第一次做misc的题目 迷迷糊糊的注册之后 进去好像是个聊天室类似的东西 随便点点
  • 学习C语言第6天打卡

    练习1 斐波那契数列 include
  • 数学:关于对向量、矩阵求导常见公式

    对向量 矩阵求导 和对标量求导还是有点区别 特别是转置和不转置 在网上参考了其他资料整理一下 介绍 在矩阵求导中 分为两种布局 分别是分子布局 Numerator Layout 和分母布局 Denominator Layout 考虑 x y
  • 2023年程序员八股文-集群

    一 负载均衡 集群中的应用服务器 节点 通常被设计成无状态 用户可以请求任何一个节点 负载均衡器会根据集群中每个节点的负载情况 将用户请求转发到合适的节点上 负载均衡器可以用来实现高可用以及伸缩性 高可用 当某个节点故障时 负载均衡器会将用
  • O-RAN专题系列-35:管理面-WG4.MP.V07-规范解读-第2章-总体架构

    作者主页 文火冰糖的硅基工坊 文火冰糖 王文兵 的博客 文火冰糖的硅基工坊 CSDN博客 本文网址 https blog csdn net HiWangWenBing article details 122473075 目录 第2章 高层的
  • C++不完整类型incomplete type 浅析

    C 不完整类型incomplete type 浅析 类型定义 The following are incomplete types Type void Array of unknown size Arrays of elements tha
  • 跨域的配置

    1 什么是跨域 跨域 指的是浏览器不能执行其他网站的脚本 它是由浏览器的同源策略造成的 是浏览器对javascript施加的安全限制 注意 跨域限制访问 其实是浏览器的限制 同源策略 是指协议 主机 域名 端口都要相同 其中有一个不同都会产
  • OpenCL学习入门

    1 OpenCL概念 OpenCL是一个为异构平台编写程序的框架 此异构平台可由CPU GPU或其他类型的处理器组成 OpenCL由一门用于编写kernels 在OpenCL设备上运行的函数 的语言 基于C99 和一组用于定义并控制平台的A