OpenCL编程指南-10.2使用C++包装器API的矢量相加示例

news2024/11/18 15:35:37

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

建立OpenCL的第一步是选择一个平台。第2章介绍过,OpenCL使用了ICD模型,其中可以有多个OpenCL实现在一个系统上并存。类似于HelloWorld示例,这个矢量相加程序展示了选择OpenCL平台的一种最简单的方法:选择第一个可用的平台。

首先,调用cl::Platform::get()得到平台列表:

std::vector<cl::Platform> platformList;
cl::Platform::get(&platformList);

得到平台列表之后,这个例子会调用cl::Context()创建一个上下文。cl::Context ()调用会尝试由一个GPU设备创建上下文。如果失败,程序会产生一个异常,这个程序使用了OpenCL C++包装器异常特性,以一个错误消息终止。创建上下文的代码如下:

cl_context_properties cprops[] = (
   CL_CONTEXT_PLATFORM,
   (cl_context_properties)(platformList[0])(),
   0);
cl::Context context(CL_DEVICE_TYPE_GPU, cprops);   

选择一个设备并创建命令队列

选择一个平台并创建上下文之后,矢量相加应用程序的下一步是选择一个设备,并创建一个命令队列。第一个任务是查询与之前所创建上下文关联的设备集合。可以通过cl::Context::getInfo<CL_CONTEXT_DEVICES >()调用来查询,这会返回与上下文关联的设备std::vector。

在继续学习后面的内容之前,先来了解getInfo()方法,因为它遵循了C++包装器API中通用的一种模式。一般来说,对于一个支持查询接口的CAPl对象(例如,查询接口为clGetXXInfo(),其中xx是所查询C API对象的名),任何表示这样一个CAPI对象的C++包装器API对象都有相应的一个接口,形式如下:

template <cl_int> typename
detail::param_traits<detail::cl_XX_info, name>::param_type
cl::Object::getInfo(void);

乍一看可能会让你有些害怕,因为这里使用了一种称为特征类(traits)的C++模板技术(这里用于关联clGetXXInfo()提供的共享功能),不过,由于使用这些getInfo()函数的程序在实际中从来不需要引用特征类组件,所以对于开发人员编写的代码没有任何影响。需要指出的重要一点是,所有对应一个底层C API对象的C++包装器API对象都有一个模板方法,名为getInfo(),以查询的cl_xx_info枚举值作为其模板参数。其效果是可以静态检查所请求的值是否合法,也就是说,一个特定的getInfo()方法只接受相应cl_xx_info枚举中定义的值。通过使用这种特征类技术,getInfo()函数可以自动推导出结果类型。

再来看矢量相加示例,要为关联的一组设备查询一个上下文,可以用CL_CONTEXT_DEVICES限定相应的cl::Context::getInfo(),返回std::vector<cl::Device>。通过以下代码可以说明:

//Query the set of devices attached to the context
std::vector<cl::Device> device =
   context.getInfo<CL_CONTEXT_DEVICES>();

注意:利用C++包装器API查询方法,现在不再需要先查询上下文来找出需要多大的空间存储设备列表,然后再提供另一个查询调用得到具体的设备。所有这些都隐藏在C++包装器API的一个简单的通用接口中。

选择设备集合之后,可以用cl::CommandQueue()创建一个命令队列,为简单起见,这里选择第一个设备:

//Create command-queue
cl::CommandQueue queue(context, device[0], 0);

创建和构建程序对象

矢量相加示例中的下一步是使用cl::Program()由OpenCL C内核源代码创建一个程序对象(矢量相加示例的内核源代码在本章最后的代码清单2-1中给出,这里不再重复)。程序对象用内核源代码加载,然后使用cl::Program::build()编译这个代码,以便在与上下文关联的设备上执行。下面给出相应的代码:

cl::Program::Sources sources(
   1,
   std::make_pair(kernelSourceCode,
   0));
cl::Program program(context, sources);
program.build(devices);

与其他C++包装器API调用类似,如果出现错误,则会有一个异常,程序将退出。

创建内核和内存对象

要执行OpenCL计算内核,需要在OpenCL设备上可访问的内存中分配内核函数的参数,这里就是缓冲区对象。这些缓冲区对象使用cl::Buffer()创建。对于输入缓冲区,我们使用CL_MEM_COPY_FROM_HOST_PTR来避免额外的调用来移动输人数据。对于输出缓冲区(即矢量相加的结果),则使用CL_MEM_USE_HOST_PTR,这要求将结果缓冲区映射到宿主机内存以便访问结果。可以使用以下代码来分配这些缓冲区:

cl::Buffer aBuffer = cl::Buffer(
   context,
   CL_MEM_READ_ONLY | CL_MEM_COPY_HOST_PTR,
   BUFFER_SIZE * sizeof(int),
   (void *) &A[0]);

cl::Buffer bBuffer = cl::Buffer(
   context,
   CL_MEM_READ_ONLY | CL_MEM_COPY_HOST_PTR,
   BUFFER_SIZE * sizeof(int),
   (void *) &B[0]);

cl::Buffer cBuffer = cl::Buffer(
   context,
   CL_MEM_WRITE_ONLY | CL_MEM_USE_HOST_PTR,
   BUFFER_SIZE * sizeof(int),
   (void *) &C[0]);

利用cl::Kernel()调用创建内核对象:

cl::Kernel kernel(program, "vadd");

执行矢量相加内核

既然已经创建了内核和内存对象,矢量相加程序终于可以将内核人队等待执行了。内核函数的所有参数都要使用cl::Kernel:setArg()方法来设置。根据C API中的clSetKernelArg(),这个函数的第一个参数是内核函数参数索引。vadd()内核有3个参数(a、b和c),分别对应索引0、1和2。将之前创建的内存对象传入这个内核对象:

kernel.setArg(0, aBuffer);
kernel.setArg(1, bBuffer);
kernel.setArg(2, cBuffer);

与以往一样,设置内核参数之后,矢量相加示例使用命令队列将内核入队等待在设备上执行。这是通过调用cl::CommandQueue::enqueueNDRangeKernel()完成的。全局和局部工作大小使用cl::Range()传递。

对于局部工作大小,使用cl::Range()对象的一个特殊实例cl::NullRange,顾名思义,它对应于C API中传递NULL,允许运行时为设备确定最佳的工作组大小和请求的全局工作大小。

queue.enqueueNDRangeKernel(
   kernel,
   cl::NullRange,
   cl::NDRange(BUFFER_SIZE),
   cl::NullRange);

将内核入队等待执行并不意味着内核会立即执行。可以使用cl::CommandQueue::flush()或cl::CommandQueue::finish()强制提交到设备立即执行。不过,由于这个矢量相加示例只是要显示结果,所以它使用了一个阻塞的cl::CommandQueue::enqueueMapBuffer(),将输出缓冲区映射到一个宿主机指针:

int * output = (int *)queue.enqueueMapBuffere(
   cBuffer,
   CL_TRUE, //block
   CL_MAP_READ,
   0,
   BUFFER_SIZE * sizeof(int));

宿主机应用程序再处理output 指向的数据,一旦完成,必须用cl::CommandQueue::enqueueUnmapMemObj()调用释放映射的内存:

err = queue.enqueueUnmapMemObject(
   cBuffer,
   (void *)output);

代码示例

#define __CL_ENABLE_EXCEPTIONS


#include <CL/cl.hpp>

#include <cstdio>
#include <cstdlib>
#include <iostream>

#define BUFFER_SIZE 20

int A[BUFFER_SIZE];
int B[BUFFER_SIZE];
int C[BUFFER_SIZE];

static char
kernelSourceCode[] =
"__kernel void                                                               \n"
"vadd(__global int * a, __global int * b, __global int * c)                                                                     \n"
"{                                                                           \n"
"    size_t i =  get_global_id(0);                                           \n"
"                                                                            \n"
"    c[i] = a[i] + b[i];                                                     \n"
"}                                                                           \n"
;

int
main(void)
{
	cl_int err;

	// Initialize A, B, C
	for (int i = 0; i < BUFFER_SIZE; i++) {
		A[i] = i;
		B[i] = i * 2;
		C[i] = 0;
	}

	try {
		std::vector<cl::Platform> platformList;

		// Pick platform
		cl::Platform::get(&platformList);

		// Pick first platform
		cl_context_properties cprops[] = {
			CL_CONTEXT_PLATFORM, (cl_context_properties)(platformList[0])(), 0 };
		cl::Context context(CL_DEVICE_TYPE_GPU, cprops);

		// Query the set of devices attched to the context
		std::vector<cl::Device> devices = context.getInfo<CL_CONTEXT_DEVICES>();

		// Create and program from source
		cl::Program::Sources sources(1, std::make_pair(kernelSourceCode, 0));
		cl::Program program(context, sources);

		// Build program
		program.build(devices);

		// Create buffer for A and copy host contents
		cl::Buffer aBuffer = cl::Buffer(
			context,
			CL_MEM_READ_ONLY | CL_MEM_COPY_HOST_PTR,
			BUFFER_SIZE * sizeof(int),
			(void*)&A[0]);

		// Create buffer for B and copy host contents
		cl::Buffer bBuffer = cl::Buffer(
			context,
			CL_MEM_READ_ONLY | CL_MEM_COPY_HOST_PTR,
			BUFFER_SIZE * sizeof(int),
			(void*)&B[0]);

		// Create buffer for that uses the host ptr C
		cl::Buffer cBuffer = cl::Buffer(
			context,
			CL_MEM_WRITE_ONLY | CL_MEM_USE_HOST_PTR,
			BUFFER_SIZE * sizeof(int),
			(void*)&C[0]);

		// Create kernel object
		cl::Kernel kernel(program, "vadd");

		// Set kernel args
		kernel.setArg(0, aBuffer);
		kernel.setArg(1, bBuffer);
		kernel.setArg(2, cBuffer);

		// Create command queue
		cl::CommandQueue queue(context, devices[0], 0);

		// Do the work
		queue.enqueueNDRangeKernel(
			kernel,
			cl::NullRange,
			cl::NDRange(BUFFER_SIZE),
			cl::NullRange);


		// Map cBuffer to host pointer. This enforces a sync with 
		// the host backing space, remember we choose GPU device.
		int* output = (int*)queue.enqueueMapBuffer(
			cBuffer,
			CL_TRUE, // block 
			CL_MAP_READ,
			0,
			BUFFER_SIZE * sizeof(int));

		for (int i = 0; i < BUFFER_SIZE; i++) {
			std::cout << C[i] << " ";
		}
		std::cout << std::endl;

		// Finally release our hold on accessing the memory
		err = queue.enqueueUnmapMemObject(
			cBuffer,
			(void*)output);

		// There is no need to perform a finish on the final unmap
		// or release any objects as this all happens implicitly with
		// the C++ Wrapper API.
	}
	catch (cl::Error err) {
		std::cerr
			<< "ERROR: "
			<< err.what()
			<< "("
			<< err.err()
			<< ")"
			<< std::endl;

		return EXIT_FAILURE;
	}

	return EXIT_SUCCESS;
}

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/983929.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

7000+客户经验总结,《数字化转型实践指南》重磅发布

数字化转型 2022年1月&#xff0c;国务院印发《“十四五”数字经济发展规划》&#xff0c;明确要求加快企业数字化转型升级&#xff0c;推进数字化转型&#xff0c;自此数字化转型又开启了新篇章。 调研数据显示&#xff0c;成功的数字化转型能让企业效率提升20-25倍&#xff…

微信小程序技术分享,以及项目实战:商城花园

目前移动端开发技术非常多&#xff0c;原生的有如下&#xff1a; 一、原生开发 Android Studio: 是用于开发 Android 应用的集成开发环境 (IDE)。 Xcode: 是用于开发 iOS 应用的 IDE。 鸿蒙HarmonyOS体系&#xff1a;华为官方提供的&#xff0c;HarmonyOS是一款面向万物互联时…

HTTPS加密协议详解:TLS/SSL握手过程

1、握手与密钥协商过程 基于RSA握手和密钥交换的客户端验证服务器为示例详解TLS/SSL握手过程。 (1).client_hello 客户端发起请求&#xff0c;以明文传输请求信息&#xff0c;包含版本信息&#xff0c;加密套件候选列表&#xff0c;压缩算法候选列表&#xff0c;随机数&#…

RabbitMQ: Publish/Subscribe结构

生产者 package com.qf.mq2302.publishSub;import com.qf.mq2302.utils.MQUtils;import com.rabbitmq.client.Channel; import com.rabbitmq.client.Connection;public class EmitLog {private static final String EXCHANGE_NAME "logs";public static void main…

word转PDF文件变小,图片模糊

word论文29M&#xff0c;文件——另存为——只有1.5M左右&#xff0c;图片压缩严重&#xff0c;图片看不清。 word中很多大图&#xff0c;5M一张的图&#xff0c;所以word很大。 找了很多方法&#xff0c;转换后都在2M左右&#xff0c;勉强可以。 直到找到了这个&#xff0c…

机器学习基础之《分类算法(6)—决策树》

一、决策树 1、认识决策树 决策树思想的来源非常朴素&#xff0c;程序设计中的条件分支结构就是if-else结构&#xff0c;最早的决策树就是利用这类结构分割数据的一种分类学习方法 2、一个对话的例子 想一想这个女生为什么把年龄放在最上面判断&#xff01;&#xff01;&…

PC调试手机微信浏览器

准备工具 一部手机 一台电脑 一条数据线 首先用数据线把手机与电脑连接 然后手机进入到开发者模式并且要是开启状态&#xff0c;具体进入方式&#xff0c;根据机型不同&#xff0c;进入方式不同&#xff0c;自行百度。 进入到开发者选项之后&#xff0c;开启USB调试模式。 然…

2023年最全ins商店开通运营攻略

借助 Instagram 商店&#xff0c;品牌可以策划一系列可购物的商品&#xff0c;这些商品可通过其 Instagram 个人资料直接访问。这使得在应用程序上销售更容易&#xff0c;也被潜在客户发现。 一、什么是Instagram Shop&#xff1f; Instagram 商店为商家提供了一种在 Instagra…

【Liunx】进程概念,查看进程,进程调用,创建子进程

进程 1.什么是进程2.查看进程3.常见进程调用4.创建子进程 1.什么是进程 以前我们在书上或者其他途径了解到进程的概念。 一个运行起来&#xff08;加载到内存&#xff09;的的程序叫做进程。 在内存中的程序叫做进程。 进程与程序相比具有动态属性。 这里的概念比较抽象&#…

Task :app:compileDebugKotlin FAILED

gradle.properties 里面加上 android.enableJetifiertrue

PyCharm下载安装

PyCharm下载链接 点击下载PyCharm Community Edition社区版&#xff08;PyCharm Professional专业版需要收费&#xff0c;但可以免费试用 30 天&#xff0c;也可以找到激活方式&#xff1b;而社区版是完全免费的&#xff0c;初学者学习 Python建议使用社区版&#xff0c;不会有…

Android studio自定义输出编译apk的名称

//输出apk名称android.applicationVariants.all { variant ->variant.outputs.all {//com.android.app-debug-1.0.apkoutputFileName "${variant.applicationId}-${variant.name}-${variant.versionName}.apk"//debug-1.0.apkoutputFileName "${variant.na…

内网穿透的应用-不再依赖iCloud!利用群晖生态,自己掌控本地SSD的云存储!

文章目录 前言本教程解决的问题是&#xff1a;按照本教程方法操作后&#xff0c;达到的效果是想使用群晖生态软件&#xff0c;就必须要在服务端安装群晖系统&#xff0c;具体如何安装群晖虚拟机请参考&#xff1a; 1. 安装并配置synology drive1.1 安装群辉drive套件1.2 在局域…

2023-python-解释器是什么东西?

传送门 对比学习一下&#xff1a; 下面的是编译型&#xff0c;比如c等&#xff1b; 先compiler编译成二进制形式的目标文件&#xff08;Object File&#xff09;&#xff0c;然后链接起来&#xff1b; 解释型python: 解释器的compiler对正在运行中的文件中的代码进行一个 词法…

一文彻底理解什么是同步和异步!

相信很多同学遇到同步异步这两个词的时候大脑瞬间就像红绿灯失灵的十字路口一样陷入一片懵逼的状态&#xff1a; 是的&#xff0c;这两个看上去很像实际上也很像的词汇给博主造成过很大的困扰&#xff0c;这两个词背后所代表的含义到底是什么呢&#xff1f; 我们先从工作场景…

C++项目实战——基于多设计模式下的同步异步日志系统-②-相关技术补充(不定参函数)

文章目录 专栏导读不定参函数C风格不定参函数不定参宏函数 专栏导读 &#x1f338;作者简介&#xff1a;花想云 &#xff0c;在读本科生一枚&#xff0c;C/C领域新星创作者&#xff0c;新星计划导师&#xff0c;阿里云专家博主&#xff0c;CSDN内容合伙人…致力于 C/C、Linux 学…

【autodesk】浏览器中渲染rvt模型

使用Forge完成渲染 Forge是什么 为什么能够渲染出来rvt模型 Forge是由Autodesk开发的一套云端开发平台和工具集。在Forge平台中&#xff0c;有一个名为"Model Derivative"的服务&#xff0c;它可以将包括RVT&#xff08;Revit&#xff09;在内的多种BIM&#xff08…

【无线电力传输】12 V 直流风扇无线电力传输系统的实现(Simulink)

&#x1f4a5;&#x1f4a5;&#x1f49e;&#x1f49e;欢迎来到本博客❤️❤️&#x1f4a5;&#x1f4a5; &#x1f3c6;博主优势&#xff1a;&#x1f31e;&#x1f31e;&#x1f31e;博客内容尽量做到思维缜密&#xff0c;逻辑清晰&#xff0c;为了方便读者。 ⛳️座右铭&a…

华为OD机试 - 最长的指定瑕疵度的元音子串 - 正则表达式(Java 2023 B卷 200分)

目录 专栏导读一、题目描述二、输入描述三、输出描述四、解题思路五、Java算法源码六、效果展示1、输入2、输出3、说明 华为OD机试 2023B卷题库疯狂收录中&#xff0c;刷题点这里 专栏导读 本专栏收录于《华为OD机试&#xff08;JAVA&#xff09;真题&#xff08;A卷B卷&#…

高速电路设计笔记----第一章

一、需求。 1、电路设计首先要做的是明确需求。 2、明确需求后需要对CPU进行选型、电源的选型、退耦电容选型。 3、画原理图需要兼顾PCB上的器件布局。中间电阻电容如果是靠近下一级&#xff0c;在原理图中画的时候也应该是靠近下一级处画。 4、按照PCB上电容的排列顺序绘制…