C++AMP: 遇见C++ AMP:在GPU上做并行计算

本文介绍了如何使用C++AMP在GPU上进行并行计算,包括从CPU到GPU的转换,详细解释了如何在GPU上运行代码以进行并行计算正弦值及矩阵之和的操作。

遇见C++ AMP:在GPU上做并行计算

 

Written by Allen Lee

 

I see all the young believers, your target audience. I see all the old deceivers; we all just sing their song.
– Marilyn Manson, Target Audience (Narcissus Narcosis)

 

从CPU到GPU

      在《遇见C++ PPL:C++的并行和异步》里,我们介绍了如何使用C++ PPL在CPU上做并行计算,这次,我们会把舞台换成GPU,介绍如何使用C++ AMP在上面做并行计算。

      为什么选择在GPU上做并行计算呢?现在的多核CPU一般都是双核或四核的,如果把超线程技术考虑进来,可以把它们看作四个或八个逻辑核,但现在的GPU动则就上百个核,比如中端的NVIDIA GTX 560 SE就有288个核,顶级的NVIDIA GTX 690更有多达3072个核,这些超多核(many-core)GPU非常适合大规模并行计算。

      接下来,我们将会在《遇见C++ PPL:C++的并行和异步》的基础上,对并行计算正弦值的代码进行一番改造,使之可以在GPU上运行。如果你没读过那篇文章,我建议你先去读一读它的第一节。此外,本文也假设你对C++ Lambda有所了解,否则,我建议你先去读一读《遇见C++ Lambda》

 

并行计算正弦值

      首先,包含/引用相关的头文件/命名空间,如代码1所示。amp.h是C++ AMP的头文件,包含了相关的函数和类,它们位于concurrency命名空间之内。amp_math.h包含了常用的数学函数,如sin函数,concurrency::fast_math命名空间里的函数只支持单精度浮点数,而concurrency::precise_math命名空间里的函数则对单精度浮点数和双精度浮点数均提供支持。

代码 1

      把浮点数的类型从double改成float,如代码2所示,这样做是因为并非所有GPU都支持双精度浮点数的运算。另外,std和concurrency两个命名空间都有一个array类,为了消除歧义,我们需要在array前面加上"std::"前缀,以便告知编译器我们使用的是STL的array类。

代码 2

      接着,创建一个array_view对象,把前面创建的array对象包装起来,如代码3所示。array_view对象只是一个包装器,本身不能包含任何数据,必须和真正的容器搭配使用,如C风格的数组、STL的array对象或vector对象。当我们创建array_view对象时,需要通过类型参数指定array_view对象里的元素的类型以及它的维度,并通过构造函数的参数指定对应维度的长度以及包含实际数据的容器。

代码 3

      代码3创建了一个一维的array_view对象,这个维度的长度和前面的array对象的长度一样,这个包装看起来有点多余,为什么要这样做?这是因为在GPU上运行的代码无法直接访问系统内存里的数据,需要array_view对象出来充当一个桥梁的角色,使得在GPU上运行的代码可以通过它间接访问系统内存里的数据。事实上,在GPU上运行的代码访问的并非系统内存里的数据,而是复制到显存的副本,而负责把这些数据从系统内存复制到显存的正是array_view对象,这个过程是自动的,无需我们干预。

      有了前面这些准备,我们就可以着手编写在GPU上运行的代码了,如代码4所示。parallel_for_each函数可以看作C++ AMP的入口点,我们通过extent对象告诉它创建多少个GPU线程,通过Lambda告诉它这些GPU线程运行什么代码,我们通常把这个代码称作Kernel。

代码 4

      我们希望每个GPU线程可以完成和结果集里的某个元素对应的一组操作,比如说,我们需要计算10个浮点数的正弦值,那么,我们希望创建10个GPU线程,每个线程依次完成读取浮点数、计算正弦值和保存正弦值三个操作。但是,每个GPU线程运行的代码都是一样的,如何区分不同的GPU线程,并定位需要处理的数据呢?

      这个时候就轮到index对象出场了,我们的array_view对象是一维的,因此index对象的类型是index<1>,这个维度的长度是10,因此将会产生从0到9的10个index对象,每个GPU线程对应其中一个index对象。这个index对象将会通过Lambda的参数传给我们,而我们将会在Kernel里通过这个index对象找到当前GPU线程需要处理的数据。

      既然Lambda的参数只传递index对象,那Kernel又是如何与外界交换数据的呢?我们可以通过闭包捕获当前上下文的变量,这使我们可以灵活地操作多个数据源和结果集,因此没有必要提供返回值。从这个角度来看,C++ AMP的parallel_for_each函数在用法上类似于C++ PPL的parallel_for函数,如代码5所示,我们传给前者的extent对象代替了我们传给后者的起止索引值。

代码 5

      那么,Kernel右边的restrict(amp)修饰符又是怎么一回事呢?Kernel最终是在GPU上运行的,不管以什么样的形式,restrict(amp)修饰符正是用来告诉编译器这点的。当编译器看到restrict(amp)修饰符时,它会检查Kernel是否使用了不支持的语言特性,如果有,编译过程中止,并列出错误,否则,Kernel会被编译成HLSL,并交给DirectCompute运行。Kernel可以调用其他函数,但这些函数必须添加restrict(amp)修饰符,比如代码4的sin函数

      计算完毕之后,我们可以通过一个for循环输出array_view对象的数据,如代码6所示。当我们在CPU上首次通过索引器访问array_view对象时,它会把数据从显存复制回系统内存,这个过程是自动的,无需我们干预。

代码 6

      哇,不知不觉已经讲了这么多,其实,使用C++ AMP一般只涉及到以下三步:

  1. 创建array_view对象。
  2. 调用parallel_for_each函数。
  3. 通过array_view对象访问计算结果。

其他的事情,如显存的分配和释放、GPU线程的规划和管理,C++ AMP会帮我们处理的。

 

并行计算矩阵之和

      上一节我们通过一个简单的示例了解C++ AMP的使用步骤,接下来我们将会通过另一个示例深入了解array_view、extent和index在二维场景里的用法。

      假设我们现在要计算两个100 x 100的矩阵之和,首先定义矩阵的行和列,然后通过create_matrix函数创建两个vector对象,接着创建一个vector对象用于存放矩阵之和,如代码7所示。

代码 7

      create_matrix函数的实现很简单,它接受矩阵的总容量(行和列之积)作为参数,然后创建并返回一个包含100以内的随机数的vector对象,如代码8所示。

代码 8

      值得提醒的是,当create_matrix函数执行"return matrix;"时,会把vector对象拷贝到一个临时对象,并把这个临时对象返回给调用方,而原来的vector对象则会因为超出作用域而自动销毁,但我们可以通过编译器的Named Return Value Optimization对此进行优化,因此不必担心按值返回会带来性能问题。

      虽然我们通过行和列等二维概念定义矩阵,但它的实现是通过vector对象模拟的,因此在使用的时候我们需要做一下索引变换,矩阵的第m行第n列元素对应的vector对象的索引是m * columns + n(m、n均从0开始计算)。假设我们要用vector对象模拟一个3 x 3的矩阵,如图1所示,那么,要访问矩阵的第2行第0列元素,应该使用索引6(2 * 3 + 0)访问vector对象。

图 1

      接下来,我们需要创建三个array_view对象,分别包装前面创建的三个vector对象,创建的时候先指定行的大小,再指定列的大小,如代码9所示。

代码 9

      因为我们创建的是二维的array_view对象,所以我们可以直接使用二维索引访问矩阵的元素,而不必像前面那样计算对应的索引。还是以3 x 3的矩阵为例,如图2所示,vector对象会被分成三段,每段包含三个元素,第一段对应array_view对象的第一行,第二段对应第二行,如此类推。如果我们想访问矩阵的第2行第0列的元素,可以直接使用索引 (2, 0) 访问array_view对象,这个索引对应vector对象的索引6。

图 2

      考虑到第一、二个array_view对象的数据流动方向是从系统内存到显存,我们可以把它们的第一个类型参数改为const int,如代码10所示,表示它们在Kernel里是只读的,不会对它包装的vector对象产生任何影响。至于第三个array_view对象,由于它只是用来输出计算结果,我们可以在调用parallel_for_each函数之前调用array_view对象的discard_data成员函数,表明我们对它包装的vector对象的数据不感兴趣,不必把它们从系统内存复制到显存。

代码 10

      有了这些准备,我们就可以着手编写Kernel了,如代码11所示。我们把第三个array_view对象的extent传给parallel_for_each函数,由于这个矩阵是100 x 100的,parallel_for_each函数会创建10,000个GPU线程,每个GPU线程计算这个矩阵的一个元素。由于我们访问的array_view对象是二维的,索引的类型也要改为相应的index<2>。

代码 11

      看到这里,你可能会问,GPU真能创建这么多个线程吗?这取决于具体的GPU,比如说,NVIDIA GTX 690有16个多处理器(Kepler架构,每个多处理器有192个CUDA核),每个多处理器的最大线程数是2048,因此可以同时容纳最多32,768个线程;而NVIDIA GTX 560 SE拥有9个多处理器(Fermi架构,每个多处理器有32个CUDA核),每个多处理器的最大线程数是1536,因此可以同时容纳最多13,824个线程。

      计算完毕之后,我们可以在CPU上通过索引器访问计算结果,代码12向控制台输出结果矩阵的第14行12列元素。

代码 12

 

async + continuation

      掌握了C++ AMP的基本用法之后,我们很自然就想知道parallel_for_each函数会否阻塞当前CPU线程。parallel_for_each函数本身是同步的,它负责发起Kernel的运行,但不会等到Kernel的运行结束才返回。以代码13为例,当parallel_for_each函数返回时,即使Kernel的运行还没结束,checkpoint 1位置的代码也会照常运行,从这个角度来看,parallel_for_each函数是异步的。但是,当我们通过array_view对象访问计算结果时,如果Kernel的运行还没结束,checkpoint 2位置的代码会卡住,直到Kernel的运行结束,array_view对象把数据从显存复制到系统内存为止。

代码 13

      既然Kernel的运行是异步的,我们很自然就会希望C++ AMP能够提供类似C++ PPL的continuation。幸运的是,array_view对象提供一个synchronize_async成员函数,它返回一个concurrency::completion_future对象,我们可以通过这个对象的then成员函数实现continuation,如代码14所示。事实上,这个then成员函数就是通过C++ PPL的task对象实现的。

代码 14

 

你可能会问的问题

      1. 开发C++ AMP程序需要什么条件?

      你需要Visual Studio 2012以及一块支持DirectX 11的显卡,Visual C++ 2012 Express应该也可以,如果你想做GPU调试,你还需要Windows 8操作系统。运行C++ AMP程序需要Windows 7/Windows 8以及一块支持DirectX 11的显卡,部署的时候需要把C++ AMP的运行时(vcamp110.dll)放在程序可以找到的目录里,或者在目标机器上安装Visual C++ 2012 Redistributable Package

      2. C++ AMP是否支持其他语言?

      C++ AMP只能在C++里使用,其他语言可以通过相关机制间接调用你的C++ AMP代码:

      3. C++ AMP是否支持其他平台?

      目前C++ AMP只支持Windows平台,不过,微软发布了C++ AMP开放标准,支持任何人在任何平台上实现它。如果你希望在其他平台上利用GPU做并行计算,你可以考虑其他技术,比如NVIDIA的CUDA(只支持NVIDIA的显卡),或者OpenCL,它们都支持多个平台。

      4. 能否推荐一些C++ AMP的学习资料?

      目前还没有C++ AMP的书,Kate Gregory和Ade Miller正在写一本关于C++ AMP的书,希望很快能够看到它。下面推荐一些在线学习资料:

 

*声明:本文已经首发于InfoQ中文站,版权所有,《遇见C++ AMP:在GPU上做并行计算》,如需转载,请务必附带本声明,谢谢。

http://www.cnblogs.com/allenlooplee/archive/2012/08/15/2640644.html

代码转载自:https://pan.quark.cn/s/8ce4326d996e 对于在 CentOS 7 系统中修改网卡配置文件后无法使设置生效的情况,经过实践验证,可以通过使用 nmcli 命令来进行调整。完成修改之后,需要重新启动虚拟机以使更改生效,这样操作流程即告完成。如果设置仍然无法生效,则表明虚拟机在启动过程中所获取的 IP 地址配置并非针对 eth0,此时可以对其它网卡的配置文件进行修改或将其移除。在 CentOS 7 系统中,网络配置的管理机制早期版本存在差异,主要体现为采用了 Network Manager 服务来负责网络接口的管理。在某些情形下,尽管修改了 `/etc/sysconfig/network-scripts` 目录下的 `ifcfg-eth0` 文件,但网络配置却未能即时生效。此类问题的发生通常源于 CentOS 7 采用了不同于以往的配置读取方法。接下来将具体阐述如何借助 nmcli 命令来处理这一挑战。 以 root 用户身份登录系统并打开终端界面。nmcli 是 Network Manager 提供的命令行界面工具,它支持在命令行环境下执行网络连接的建立、编辑、查询及管理任务。针对修改 eth0 网卡配置的需求,可以遵循以下步骤进行操作: 1. 导航至 `/etc/sysconfig/network-scripts` 目录: ``` cd /etc/sysconfig/network-scripts ``` 2. 检查该目录内是否存在 `ifcfg-eth0.bak` 文件,该备份文件可能是先前调整配置时遗留下来的,若存在可能造成冲突。若发现该文件,可以选择将其删除: ``` [root@localhost netw...
代码转载自:https://pan.quark.cn/s/46fd08fb879c 网管教程 从入门到精通软件篇 ★一。★详尽的xp修复控制台指令及其应用!!! 放入xp(2000)的光盘,安装时选择R,执行修复! Windows XP(涵盖 Windows 2000)的控制台指令是在系统遭遇某些意外状况时的一种极具效用的诊断、检测以及恢复系统功能的工具。笔者确实一直期望能够将这方面的指令进行归纳,此次由老范辛苦整理了这份极具价值的秘籍。 Bootcfg bootcfg 命令用于启动配置故障恢复(对大多数计算机而言,即 boot.ini 文件)。 带有特定参数的 bootcfg 命令仅在运用故障恢复控制台时方可使用。能够在命令行界面下运用带有不同参数的 bootcfg 命令。 用法: bootcfg /default 设定默认引导选项。 bootcfg /add 向引导清单中增添 Windows 安装。 bootcfg /rebuild 重复整个 Windows 安装流程并让用户选择需添加的项目。 注意:运用 bootcfg /rebuild 之前,应先借助 bootcfg /copy 命令备份 boot.ini 文件。 bootcfg /scan 探查用于 Windows 安装的全部磁盘并展示结果。 注意:这些结果被静态存储,并用于当前会话。若在当前会话期间磁盘配置发生变动,为获取更新的探查结果,必须先重启计算机,然后再次探查磁盘。 bootcfg /list 列示引导清单中已有的项目。 bootcfg /disableredirect 在启动引导程序中禁用重定向。 bootcfg /redirect [ PortBaudRrate] |[ useBio...
代码下载链接: https://pan.quark.cn/s/fc524f791b68 AA制程,即Active Alignment,被理解为主动对准,是一种用于确定零部件装配中相对位置的方法。在摄像头封装阶段,涉及图像传感器、镜座、马达、镜头、线路板等多个部件的重复组装,而传统的封装设备如CSP及COB等,均是依据设备设定的参数进行零部件的移动装配,因而零部件的叠加误差会逐渐增大,最终在摄像头上表现为拍照最清晰的位置可能偏离画面中心、四边清晰度不均等现象。伴随智能手机和其他高端电子产品的普及,摄像头模组的性能正日益受到重视。高分辨率、卓越的低光表现以及稳定视频输出是现代用户所期望的。在摄像头模组的制造环节,各部件的精准定位对成像质量具有决定性作用。因此,一种名为“AA制程”(Active Alignment)的前沿技术被开发出来,成为摄像头精密对准的核心技术。 AA制程,即Active Alignment,是一种在摄像头封装过程中应用的主动对准方法。该方法在多个组件装配阶段发挥作用,涵盖图像传感器、镜座、马达、镜头和线路板等部件。传统的封装方式,例如CSP(Chip Scale Package)和COB(Chip On Board),依赖于设备预设的参数进行组装,但随着组件数量的增加,误差也会累积,最终影响摄像头的表现。例如在成像质量上可能出现中心位置偏移、四角清晰度不一致等问题。 AA制程技术的核心在于实时监测主动调整。在组装过程中,它借助先进的检测设备持续监控半成品的状态,并根据实时信息对组装部件进行精确修正,从而显著降低装配误差。通过这种技术,能够确保摄像头模组中各组件的相对位置准确无误,从而使得最终的成像效果更加稳定,特别是在中心区域和四角的清晰度上...
内容概要:本文介绍了一套基于Matlab实现的光子晶体90度弯曲波导的二维时域有限差分法(2D FDTD)仿真代码,旨在通过数值模拟手段深入研究光子晶体波导中的光传播特性。该资源聚焦于电磁场光子学领域的仿真技术应用,系统实现了FDTD算法在复杂介质结构中的建模过程,涵盖空间网格剖分、时间步进迭代、完美匹配层(UPML)边界条件处理、总场散射场(TFSF)激励源设置、介电常数分布定义及电磁场演化可视化等核心模块,能够有效分析光在90度弯曲波导中的传输效率、模式分布反射损耗等关键性能指标。; 适合人群:具备电磁场理论基础和Matlab编程能力的研究生、科研人员以及从事光子晶体器件设计仿真的工程技术人员。; 使用场景及目标:①用于教学演示FDTD方法的基本原理算法流程,帮助理解麦克斯韦方程的离散化求解过程;②支撑科研工作中对光子晶体弯曲波导结构的传输特性进行仿真分析性能优化;③作为开发更复杂光子集成器件(如分束器、滤波器)数值仿真工具的基础框架; 阅读建议:建议使用者结合经典FDTD教材(如Taflove著作)深入理解算法理论,并在Matlab环境中逐模块调试代码,重点关注电场磁场的交替更新过程、UPML吸收边界的设计实现以及TFSF源的引入方式,从而全面提升对时域电磁仿真机制的掌握应用能力。
内容概要:本文围绕直驱式永磁同步电机(PMSM)的矢量控制仿真模型展开研究,基于Simulink平台构建了完整的电机控制系统仿真模型,涵盖电机本体建模、坐标变换(如Clark变换Park变换)、磁场定向控制(FOC)、电流环速度环的PI调节、空间矢量脉宽调制(SVPWM)等核心技术环节,旨在实现对电机转矩转速的高精度、动态响应良好的控制。通过系统化仿真验证控制策略的有效性鲁棒性,深入分析各模块间的信号流向控制逻辑,为电机驱动系统的设计优化提供理论依据和技术支撑,是理论联系工程实践的重要桥梁。; 适合人群:具备电机学、电力电子自动控制基础知识,熟悉Simulink/MATLAB仿真环境,从事电气工程、自动化、新能源车辆、智能制造等方向的研究生、科研人员及工程技术人员。; 使用场景及目标:①深入理解永磁同步电机矢量控制的核心原理系统架构;②掌握在Simulink中从零开始搭建复杂电机控制系统的方法技巧;③应用于课程设计、毕业论文、科研项目中的控制算法验证、参数整定性能优化;④为后续的硬件在环(HIL)测试或实物系统开发奠定仿真基础。; 阅读建议:建议结合经典电机控制理论教材同步学习,注重理论推导仿真实现的对应关系,动手实践模型搭建、参数调试波形分析,特别关注PI控制器参数整定对系统稳定性、动态响应速度和抗干扰能力的影响,通过反复仿真迭代加深对控制机理的理解。
代码下载地址: https://pan.quark.cn/s/a4b39357ea24 Subversion,即 SVN,是一种在软件开发行业中普遍应用的版本管理工具。它支持团队成员之间的协作,用于管理和监控项目文件的历史版本,并保证多人同时编辑时的数据一致性。本指南将深入讲解 SVN 的核心概念、主要目录的权限设置、用户身份验证方式以及基础操作步骤,是初学者入门的理想学习资料。 一、SVN概述 SVN的中心是版本库,它负责存储所有文件和目录,并构建成文件树的结构。版本库能够允许多个客户端进行连接,执行数据的读取或写入。用户可以通过写操作将自己的修改同步至版本库,而其他用户则可以通过读操作来查看这些变更。这种集中式的版本管理机制使团队协作更加高效和有序。 二、SVN的访问权限配置 在 SVN 系统中,不同的用户或用户团队会被分配不同的访问权限。以质量管理部门的 SVN 实例为例: - 主管朱猛、张凯峰、吕鑫、张颂、马凌具备读写权限。 - 员工陈玲及其他成员仅拥有读权限。 - 项毓毅享有读写权限,主管团队则只有读权限。 - 张凯峰同样拥有读写权限,而其他同事仅能进行读取操作。 三、登录凭证 用户在访问 SVN 时,需要使用基于姓名拼音的用户名和符合特定规则的密码。例如,用户张三的登录名设定为"zhangs",密码为"zhangs#123",这样的设置旨在简化记忆和管理工作。 四、基础操作指南 1. 安装 SVN 客户端:本教程推荐采用 TortoiseSVN 进行安装,可以从指定的 FTP 地址获取安装包。 2. 读取操作: - 项毓毅和管理团队可以直接检出到"质量管理部"目录。 - 其他员工需要分别检出到"部门财富库"和"产品线管理"子目录,因为他们无法访问"部...
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值