opencl编程简单的入门知识
Plantform(平台):主机加上OpenCL框架管理下的若干设备构成了这个平台,通过这个平台,应用程序可以与设备共享资源并在设备上执行kernel。
平台通过cl_plantform来展现,可以使用下面的代码来初始化平台
cl_int oclGetPlatformID (cl_platform_id *platforms); // Pointer to the platform object
Device(设备):通过cl_device来表现
cl_int clGetDeviceIDs (cl_platform_id platform,
cl_device_type device_type, // Bitfield identifying the type. For the GPU we use CL_DEVICE_TYPE_GPU
cl_uint num_entries, // Number of devices, typically 1
cl_device_id *devices, // Pointer to the device object
cl_uint *num_devices // Puts here the number of devices matching the device_type
);
Context(上下文):定义了整个OpenCL化境,包括OpenCL kernel、设备、内存管理、命令队列等。上下文使用cl_context来表现
cl_context clCreateContext(const cl_context_properties *properties, // Bitwise with the properties (see specification)
cl_uint num_devices,
const cl_device_id *devices, // Pointer to the devices object
void (*pfn_notify)(const char *errinfo, const void *private_info, size_t cb, void *user_data), // (don't worry about this)
void *user_data, // (don't worry about this)
cl_int *errcode_ret // error code result
);
Command-Queue(指令队列):就像它的名字一样,他是一个存储需要在设备上执行的OpenCL指令的队列。
指令队列建立在一个上下文中的指定设备上 ,多个指令队列允许应用程序在不需要同步的情况下执行多条无关联的指令
cl_command_queue clCreateCommandQueue (
cl_context context,
cl_device_id device,
cl_command_queue_properties properties, // Bitwise with the properties
cl_int *errcode_ret // error code result
);
下面是一个简单的示例
cl_int error = 0; // Used to handle error codes
cl_platform_id platform;
cl_context context;
cl_command_queue queue;
cl_device_id device;
// Platform
error = oclGetPlatformID(&platform);
if (error != CL_SUCCESS) {
cout << "Error getting platform id: " << errorMessage(error) << endl;
exit(error);
}
// Device
error = clGetDeviceIDs(platform, CL_DEVICE_TYPE_GPU, 1, &device, NULL);
if (err != CL_SUCCESS) {
cout << "Error getting device ids: " << errorMessage(error) << endl;
exit(error);
}
// Context
context = clCreateContext(0, 1, &device, NULL, NULL, &error);
if (error != CL_SUCCESS) {
cout << "Error creating context: " << errorMessage(error) << endl;
exit(error);
}
// Command-queue
queue = clCreateCommandQueue(context, device, 0, &error);
if (error != CL_SUCCESS) {
cout << "Error creating command queue: " << errorMessage(error) << endl;
exit(error);
}
在设备上分配内存,我们需要使用cl_mem类型
cl_mem clCreateBuffer (cl_context context, // The context where the memory will be allocated
cl_mem_flags flags,
size_t size, // The size in bytes
void *host_ptr,
cl_int *errcode_ret
);
flags是逐位的,选项如下:
CL_MEM_READ_WRITE
CL_MEM_WRITE_ONLY
CL_MEM_READ_ONLY
CL_MEM_USE_HOST_PTR
CL_MEM_ALLOC_HOST_PTR
CL_MEM_COPY_HOST_PTR – 从 host_ptr处拷贝数据
下面是一个简单的内存分配示例,分配3块内存,两份分别存a,b向量,一份存相加后的结果向量,这里的变量名后的_d表示内存是分配在设备上的
const int mem_size = sizeof(float)*size;
// Allocates a buffer of size mem_size and copies mem_size bytes from src_a_h
cl_mem src_a_d = clCreateBuffer(context, CL_MEM_READ_ONLY | CL_MEM_COPY_HOST_PTR, mem_size, src_a_h, &error);
cl_mem src_b_d = clCreateBuffer(context, CL_MEM_READ_ONLY | CL_MEM_COPY_HOST_PTR, mem_size, src_b_h, &error);
cl_mem res_d = clCreateBuffer(context, CL_MEM_WRITE_ONLY, mem_size, NULL, &error);
Kernel:kernel本质上是一个我们可以从主机上调用的,运行在设备上的函数.所有运行在设备上的代码,包括kernel和kernel调用的其他的函数,
都是在运行的时候编译的。这涉及到下一个概念,Program。
Program:OpenCL Program由kernel函数、其他函数和声明组成。它通过cl_program表示。当创建一个program时,你必须指定它是由哪些文件组成的,然后编译它。
创建program
cl_program clCreateProgramWithSource (
cl_context context,
cl_uint count, // 文件个数
const char **strings, // 字符串数组,每一个字符串代表一个文件
const size_t *lengths, // 指明文件长度的数组
cl_int *errcode_ret // 返回的错误码
);
编译program
cl_int clBuildProgram (cl_program program,
cl_uint num_devices, //设备个数
const cl_device_id *device_list, //设备ID号列表
const char *options, // 编译选项
void (*pfn_notify)(cl_program, void *user_data), //
void *user_data
);
查看编译log,必须使用下面的函数
cl_int clGetProgramBuildInfo (cl_program program,
cl_device_id device,
cl_program_build_info param_name, // 我们想要知道的参数
size_t param_value_size, // 参数值的大小
void *param_value, // 结果
size_t *param_value_size_ret
);
提取program的入口点,使用cl_kernel
cl_kernel clCreateKernel (
cl_program program, // The program where the kernel is
const char *kernel_name, // The name of the kernel, i.e. the name of the kernel function as it's declared in the code
cl_int *errcode_ret
);
下面是一个简单的示例
// Creates the program
// Uses NVIDIA helper functions to get the code string and it's size (in bytes)
size_t src_size = 0;
const char* path = shrFindFilePath("vector_add_gpu.cl", NULL);
const char* source = oclLoadProgSource(path, "", &src_size);
//创建program
cl_program program = clCreateProgramWithSource(context, 1, &source, &src_size, &error);
assert(error == CL_SUCCESS);
//编译program
error = clBuildProgram(program, 1, &device, NULL, NULL, NULL);
assert(error == CL_SUCCESS);
//显示log
char* build_log;
size_t log_size;
// 第一次调用获得恰当的日志大小
clGetProgramBuildInfo(program, device, CL_PROGRAM_BUILD_LOG, 0, NULL, &log_size);
build_log = new char[log_size+1];
// 第二次调用获得日志的内容
clGetProgramBuildInfo(program, device, CL_PROGRAM_BUILD_LOG, log_size, build_log, NULL);
build_log[log_size] = '\0';
cout << build_log << endl;
delete[] build_log;
//提取kernel
//下面的 "vector_add_gpu"对应的就是cl文件里面的函数 vector_add_gpu(...)函数
cl_kernel vector_add_kernel = clCreateKernel(program, "vector_add_gpu", &error);
assert(error == CL_SUCCESS);
运行kernel
一旦我们的kernel建立好,我们就可以运行它。
首先,我们必须设置kernel的参数
cl_int clSetKernelArg (
cl_kernel kernel, // Which kernel
cl_uint arg_index, // 哪一个参数,譬如0,1,2,3代表第1,2,3,4号参数
size_t arg_size, // Size of the next argument (not of the value pointed by it!)
const void *arg_value // Value
)
每个参数都需要调用一次这个函数。
当所有参数设置完毕,我们就可以调用这个kernel
cl_int clEnqueueNDRangeKernel(
cl_command_queue command_queue,
cl_kernel kernel,
cl_uint work_dim, // Choose if we are using 1D, 2D or 3D work-items and work-groups
const size_t *global_work_offset,
const size_t *global_work_size, // The total number of work-items (must have work_dim dimensions)
const size_t *local_work_size, // The number of work-items per work-group (must have work_dim dimensions)
cl_uint num_events_in_wait_list,
const cl_event *event_wait_list,
cl_event *event);
// Enqueuing parameters
// Note that we inform the size of the cl_mem object, not the size of the memory pointed by it
error = clSetKernelArg(vector_add_k, 0, sizeof(cl_mem), &src_a_d);
error |= clSetKernelArg(vector_add_k, 1, sizeof(cl_mem), &src_b_d);
error |= clSetKernelArg(vector_add_k, 2, sizeof(cl_mem), &res_d);
error |= clSetKernelArg(vector_add_k, 3, sizeof(size_t), &size);
assert(error == CL_SUCCESS);
// Launching kernel
const size_t local_ws = 512; // Number of work-items per work-group
// shrRoundUp returns the smallest multiple of local_ws bigger than size
const size_t global_ws = shrRoundUp(local_ws, size); // Total number of work-items
error = clEnqueueNDRangeKernel(queue, vector_add_k, 1, NULL, &global_ws, &local_ws, 0, NULL, NULL);
assert(error == CL_SUCCESS);
读取结果
读取结果非常简单。与之前讲到的写入内存(设备内存)的操作相似,现在我们需要存入队列一个读取缓冲区的操作
cl_int clEnqueueReadBuffer (
cl_command_queue command_queue,
cl_mem buffer, // 从哪个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);
使用示例
float* check = new float[size];
clEnqueueReadBuffer(queue, res_d, CL_TRUE, 0, mem_size, check, 0, NULL, NULL);
清理内存
使用clCreate申请的(缓冲区、kernel、队列)必须使用clRelease释放
delete[] src_a_h;
delete[] src_b_h;
delete[] res_h;
delete[] check;
clReleaseKernel(vector_add_k);
clReleaseCommandQueue(queue);
clReleaseContext(context);
clReleaseMemObject(src_a_d);
clReleaseMemObject(src_b_d);
clReleaseMemObject(res_d);
opencl编程简单的入门知识相关推荐
- 面向对象编程思想及入门知识
这几天在调程序,所以想写写自己对"面向对象编程"的一些理解,希望对打算入门计算机编程的同志们有所帮助.之前,好几个师弟问过我,C++与C有什么区别,学习面向对象语言需要掌握哪些基础 ...
- lua编程简单实用入门教程,用NodeMCU在OLED上显示温湿度
OLED模块介绍 OLED显示屏是指有机电激发光二极管(Organic Light-Emitting Diode,OLED),具备自发光,所以不需背光源,对比度高,厚度薄,视角广,反应速度快等特性,被 ...
- 一、 Python 基础知识笔记 —— 《Python编程:从入门到实践(第二版)》学习笔记
前言 先安利这本书<Python编程:从入门到实践(第二版)>,作者埃里克-马瑟斯,很适合新手入门,我的python入门学习就是以这本书为核心: 再安利一个网站:菜鸟教程-Python3教 ...
- 看漫画学python下载_它被誉为最简单Python入门书,看漫画就学会编程,看了就会...
他认为市面上没有一本书适合Python初学者学习,于是他开始用生动易懂的方式来系统整理Python中的基础编程知识,以此保证,能让更多的初学者快速入门Python编程. 书中近乎漫画的页面风格,让读者 ...
- 《Python编程:从入门到实战》学习笔记(第2版) 第1-2章 起步变量和简单数据类型
[写在前面]为进一步提高自己的python代码能力,打算把几本经典书籍重新过一遍,形成系统的知识体系,同时适当记录一些学习笔记,我尽量及时更新!先从经典的<Python编程:从入门到实战> ...
- Java面向对象程序开发——网络编程入门知识
目录 七.网络编程入门知识 软件结构 网络通信协议 协议分类 网络编程三要素 TCP通信程序 概述 Socket类 构造方法 成员方法 ServerSocket类 构造方法 成员方法 简单的TCP网络 ...
- ARM NEON 编程简单入门1
原文:http://blog.csdn.net/silentob/article/details/72954618 ARM NEON 编程简单入门1 NEON简介 NEON是适用于ARM Corte ...
- 敲代码时如何快速移动光标_数控加工中心编程入门知识,半小时快速入门!
数控加工中心编程入门知识汇总,教你半小时快速入门!不管做哪一行,想要成为个中高手,必然要经得住时间的历练,自身要不断提高工作能力,要想成为一个数控高手,从大学毕业进工厂起,最起码需要6年以上的时间.既 ...
- 【day 1】python编程:从入门到实践学习笔记-安装、变量和简单数据类型
学习笔记目录 [day 1]python编程:从入门到实践学习笔记-安装.变量和简单数据类型 [day 2]python编程:从入门到实践学习笔记-列表以及其操作 [day 3]python编程:从入 ...
- 敲代码时如何快速移动光标_数控加工中心编程入门知识,半小时快速入门!超简洁明了!...
数控加工中心编程入门知识汇总,教你半小时快速入门!不管做哪一行,想要成为个中高手,必然要经得住时间的历练,自身要不断提高工作能力,要想成为一个数控高手,从大学毕业进工厂起,最起码需要6年以上的时间.既 ...
最新文章
- java 获得当月天数_java中 如何获取当月的天数、指定日期的月份天数详解
- npm安装出错Unexpected end of input at 1:2307
- Java 理论与实践: 非阻塞算法简介
- python安装某些库失败的问题解决方案
- RHEL5下构建LVS负载均衡系统详解(二)
- windows 检测无键鼠操作时间_HP快速卡简易操作流程
- 说下网鼎杯第四场的双色块
- boost::mpl模块实现deque相关的测试程序
- ES6新特性之class类的基本语法
- 使用PDF.js实现前端和手机端网页预览PDF文件(可定制,支持本地文件、Base64编码和远程URL跨域方式)
- oracle 创建数据库表 如果此表存在则删除后再重建
- 将asp.net webapi的运行时版本由4.0升级到4.5.1时遇到的问题及解决
- java 获取文件的全路径
- 基于大数据的图书推荐系统
- JAVA编译器的作用
- windows使用Apple的Trackpad
- 写好一份属于自己的简历
- java接入短信容联云通讯
- 路由器连接上但上不了网原因及解决方法
- TortoiseHg的使用
热门文章
- html 图片的缩略图,纯CSS制作缩略图片
- WPS专业版自带字体
- Z=X+Y型概率密度的求解
- 给计算机系统打补丁,为什么我的电脑需要打补丁?
- 李践《行动日志——目标管理》观后感
- TrueCrypt中文教程
- DASCTF2022.07赋能赛 - Pwn easyheap
- 少儿编程app排名_终于明白少儿编程软件哪个好
- linux应用程序跑飞,韦东山嵌入式Linux视频教程_3期项目实战之ALSA声卡_裸板之编译和测试(基于优龙FS2410开发板)...
- 苹果手用计算机解锁手机密码,苹果手机怎么强制解锁 iPhone强制解锁密码教程...