基于c++ amp的gpu编程
目录
参考资料
http://ceur-ws.org/Vol-1746/paper-23.pdf
摘要:
如今,程序员面临的挑战是使他们的程序变得更好。“更好”一词意味着更简单,可移植且执行速度更快。 异构计算是计算机科学领域的一种新方法。
GPGPU(general purpose gpu)编程是一项具有挑战性的新技术,用于解决具有数据并行性的问题。 在本文中,我们将重点介绍使用C ++ AMP语言进行GPU编程的这种新编程方法,以及使用这些并行技术适合加速的问题类型。
最后,我们描述使用C ++ AMP解决一个简单问题的解决方案以及该解决方案的优点。
1 简介
作为解决难题的算法的实现过程,需要进行深入的分析。 虽然,今天有许多工具可以帮助分析人员完成这项工作,并为程序员提供将其转换为编程语言的过程。 当执行速度很重要时,总是会遇到困难。 如果执行速度不是主要条件,那么对于程序员来说更容易,他们可以通过构建包含顺序执行的指令的源代码来更快地找到解决方案。 当所提出算法的主要条件是执行速度时,并行编程就变得更加重要。
除了并行源代码(其指令是从CPU(中央处理单元)并行执行)之外,GPGPU编程是一种新方法。
图形处理单元(GPGPU,很少使用GPGP或GP²U)上的通用计算是图形处理单元(GPU)的使用,通常仅处理计算机图形的计算单元,执行在传统上由中央处理单元(CPU)处理的应用程序中的计算任务。 图形处理单元(GPU)的体系结构非常适合于数据并行问题。 它们通过许多并行处理单元和极高的内存带宽支持极高的吞吐量。 对于与GPU架构非常匹配的问题,通常可以轻松实现相同问题的CPU实施2倍的加速,调整后的实施可以使CPU的性能提高10到100倍。但是,对这些处理器进行编程仍然很困难。因为gpu的架构与CPU有很大差异。 本文描述了使用C ++ AMP语言进行GPU编程的好处,以及哪些问题适合使用这些并行技术进行加速。
2 性能改进
1975年,世界首次引入了“个人计算机”。几十年来,拥有个人计算机的想法变得可能且真实。 如今,每个人都拥有从台式计算机,笔记本电脑到智能手机的各种电子机器。 多年来,随着技术的发展,这些电子机器的运行速度大大提高。制造商继续增加单个芯片上的晶体管数量,但这面临着由该芯片产生的热量的问题。 由于这个问题,制造商开始在计算机上生产具有两个或更多CPU的多核计算机。但是,添加CPU内核并不能使所有操作变得更快。
我们可以将软件分为两组:并行感知和并行非感知。 不支持并行的软件几乎使用了可用CPU内核的1/4或1/8,而并行感知的软件的执行速度是第二类软件的2倍或4倍,与CPU内核的数量成正比。
2.1 异构平台
在最近几年中,图形卡也经历了强大的发展。 图形处理单元(GPU)是执行快速数学计算的计算机芯片,主要用于渲染图像。 GPU具有强大的并行处理架构,因此它可以比CPU更快地渲染图像。 GPU本身就是一种可编程且功能强大的计算设备。 由此带来的性能改进使GPU成为了与图形无关的其他资源密集型任务的流行芯片。
GPU加速计算是将图形处理单元(GPU)与CPU结合使用,以加速深度学习,分析和工程应用程序。 如果说CPU是PC的大脑,那么GPU就被称为灵魂。 如今,我们可以找到具有两个,四个,七个CPU内核的机器,但是GPU可以具有数百个内核。 如果我们想知道GPU和CPU之间的区别,让我们看看它们如何处理任务。 一个CPU包含几个为顺序串行处理而优化的内核,而一个GPU则具有一个大规模并行体系结构,该体系结构包含数千个较小的,更有效的内核,旨在同时处理多个任务。 想象一下,一台机器中是否存在GPU内核和CPU内核的混合,是否在同一块芯片中,这是一台异构的超级计算机。
在计算中,FLOPS或触发器(每秒浮点操作数)是计算机性能的一种度量,在大量使用浮点计算的科学计算领域很有用。 在这种情况下,这是比通用说明更准确的方法。 因此,一台1 FLOP的机器将在一秒钟内执行一次“操作”。 浮点运算涉及浮点数,通常执行时间比简单的二进制整数运算要长。 1 Gigaflops具有10亿FLOPS,1 Teraflops具有1000 Gigaflops。 通常,CPU可以达到100 GFLOPS。 典型的GPU具有32个内核,晶体管的数量是CPU的两倍,并且可以达到3000 GFLOPS。
GPU实现此性能的原因不在于晶体管的数量或内核的数量。存储器带宽是处理器可以从半导体存储器读取数据或将数据存储到半导体存储器中的速率。 内存带宽通常以字节/秒为单位。CPU的内存带宽约为20 GB / s,而GPU的内存带宽为150 GB / s。 CPU支持具有多任务,I / O,虚拟化,深度执行管道和随机访问的通用代码。 相反,GPU设计用于具有可编程和固定功能处理器,浅层执行管线和顺序访问的图形和数据并行代码。
GPU的优点是功耗。 GPU可以达到10 GFLOPS /瓦,CPU可以达到1 GIGAFLOPS /瓦。 机器的电池寿命非常重要,尤其是在手持设备中。 在大多数情况下,用户宁愿不使用消耗电池电量很快的应用程序,而用不消耗电池的类似应用程序代替它们。 如果我们研究从该芯片访问的内存,则CPU会为其访问的数据提供较大的缓存,以便不等待执行从主存储器或辅助存储器读取数据的进程的执行,因为CPU经常使用相同的数据 。 GPU的缓存较小,但是使用大量线程,并且某些线程始终可以正常工作。 GPU可以预取数据以隐藏内存延迟。 与CPU不同,GPU具有较小的缓存,因为多次访问同一数据的可能性很小。
如今,我们可以找到许多CPU编程语言。 C ++是一种流行的CPU编程语言。 在功能和性能方面,它是首选的主要语言。 通用GPU编程(GPGPU)的选择很少。 开发人员需要一种方法来提高其应用程序速度或减少特定计算的功耗。与GPU结合使用GPU的异构计算。 这种选择的一个不利方面是对可以使用这种方法构建的软件的性质的限制。
2.2 gpu架构
GPU具有较浅的执行管道,较小的缓存以及大量执行顺序访问的线程。 线程按组排列。 这些组称为经线。
扭曲一起运行,可以共享内存并进行协作。 GPU的强大功能是可以极其快速地切换这些线程组,因此,如果阻塞了一组线程,则将执行另一组线程。
当相邻线程使用相邻的内存位置时,读取内存的方式可提供良好的速度性能。 不利的一面是,当组中的线程正在访问的内存不接近该组中其他线程正在访问的内存时,性能将受到影响。
只要有诸如编译器之类的工具,高级编程语言的开发人员现在就不需要CPU的体系结构。 如果开发人员想要编写将在GPU上执行的代码,那么他应该了解GPU架构的基础知识。
2.3 通过平行的性能改进
CPU使用带有并行数据的并行代码和顺序代码。GPU在数据并行问题上效果最佳。 有些问题可以轻松地划分为多个子问题,这些子问题可以并行执行并且彼此独立。 但是也存在一些问题,如果以某种方式对待它们,将无法将它们划分为可以并行执行的单元,但是必须以另一种方式对待它们,因此您将能够将它们划分为独立的单元。 可以并行执行的单元。 结论是,当程序员考虑问题的解决方案(源代码)并且执行速度是优先事项时,他们必须考虑并行解决方案。 他们需要以不同的方式设计算法,以创建可拆分成多个独立线程的工作。
例如,具有并行性质的问题是两个矩阵的相加。 如果我们想要一个快速简单的解决方案,则在C ++中如下所示:
int M1[n][n],M2[n][n],M_sum[n][n];
for(int i=0;i<n;i++)
{
for(int j=0;j<n;j++)
M_sum[i][j]=M1[i][j]+M2[i][j]
}
如果我们有两个具有10000个整数的100x100矩阵,则上面的代码将进行10000个加法运算,并并且是一个接一个地执行10000个运算。 如果要快速执行,可以考虑采用并行解决方案,因此可以将操作拆分为10000个线程,所有添加操作都可以立即完成。
另一个示例是在向量(数组,集合)中找到最大值的问题。 快速解决方案是一次遍历数组中的一个元素,并将每个元素与最大值(代表当前的最高值)进行比较,然后将最大值用数组的当前元素更新(如果该值较大)。 如果数组具有100000个元素,则将进行100000个比较。 如果优先考虑算法的快速执行,我们可以考虑采用并行解决方案,那么我们可以选择1000个线程,每个线程可以处理1000个项目。 计算之后,每个线程将选择其数组部分的最大值。 这样,您可以评估仅进行1000次比较所需的每个数字。 之后,第1001个线程可以比较所有线程的结果以找到最高的值。涉及大量数据的问题是并行处理的候选对象。 我们可以找到这类问题的一些领域是:
•实时控制系统
•科学建模和仿真
•游戏
•金融仿真
•图像处理
减少在应用程序的顺序部分中花费的时间量的一种方法是 它的顺序性要差一些-重新设计应用程序以利用CPU并行性和GPU并行性。
3 gpu编程架构
如今,存在一些GPU编程语言,开发人员可以使用它们来构建并行软件。这些并行编程语言有其优点和缺点。 其中一些平台是opencl、cuda和c++amp,下面对其简单介绍。
3.1 opencl
OpenCL是主要的开放式通用GPU计算语言。 英特尔,AMD,Nvidia和ARM平台均支持OpenCL。 它是用于编写跨异构平台执行的程序的框架。
3.2 cdua
CUDA是NVidia创建的并行计算平台和应用程序编程接口(API)模型。CUDA平台可以使用C,C ++和FORTRAN编程语言。
3.3 c++ amp
C ++ AMP(C ++加速大规模并行处理)通过利用通常作为离散图形卡上的图形处理单元(GPU)提供的数据并行硬件,来加速C ++代码的执行。 C ++加速大规模并行计算(C++ Acclerated Massive Parallemism, C++ AMP)是一个本机编程模型,其中包含跨越C ++编程语言及其运行时库的元素。 C ++ AMP是在DirectX 11上实现的库,并且是Microsoft的开放式规范,用于直接在C ++中实现数据并行性。 该语言更易于使用,并且包含许多用于构建数据并行应用程序的库。
4 一个c++ amp解决方案
为了更清楚地说明如何使用C ++ AMP解决数据并行问题,我将提供以下示例。 这个问题很简单,具有并行性质,即矩阵乘法。
让我们以一个数学或金融应用程序为例,其中过程的一部分是矩阵的乘法,但可能的情况是小尺寸矩阵的乘法,并且乘法操作不会执行多次,因此该操作的串行源代码将不需要执行时间长,并且并行源代码会过多。 但是,想象一下一个场景,其中有200个矩阵,每个矩阵有40000个元素。 从这里开始,我们有100个乘法运算,并且串行源代码将花费很长的执行时间,而如果利用GPU提供的功能并行执行100个运算,则执行时间将更短。
下面是C ++中用于两个矩阵相乘的简单非并行函数:
multiplication(vector<vector>& T1,
vector<vector>& T2, vector<vector>& T3,
const int n, const int m, const int k)
{ for(int i=0; i<n;i++)
{
for(int j=0; j<k; j++)
{
int sum = 0;
for(int z=0; z<m; z++)
sum += T1[i][z]*T2[z][j];
T3[i][j] = sum;
}
}
}
如果矩阵T1和T2的大小为200x200,则此功能在中等计算机上的执行时间为3.36秒。 如果采用上述情况,即100次乘法运算,则执行时间将为336秒或5.6分钟。
让我们看一下两个矩阵相乘的C ++ AMP并行函数:
multiplication_parallel(vector<vector<int>>& T1,
vector<vector<int>>& T2, vector<vector<int>>& T3,
const int n, const int m, const int k)
array_view<const int, 2> a(n, m, T1), b(m, k, T2);
array_view<int, 2> c(n, k, T3); c.discard_data();
parallel_for_each(c.extent, [=](index<2> idx) restrict(amp)
{
int row = idx[0];
int col = idx[1];
int sum = 0;
for(int i = 0; i < b.extent[0]; i++)
sum += a(row, i) * b(i, col); c[idx] = sum;
});
c.synchronize();
此版本使用C ++ AMP库的array_view数据结构。 parallel_for_each函数是主要功能,可从计算机的GPU执行所有并行工作。 此功能在一定范围内运行-范围的形状控制着执行工作的线程数。 在中等计算机上,执行时间比无并行的解决方案快20倍。 第一个函数和第二个函数之间执行时间的差异是显而易见的。
5 结论
如果提出的算法的主要条件是执行速度,那么并行编程就变得很重要。GPGPU编程是一种新的具有挑战性的技术,用于解决具有数据并行性的问题。 我们可以发现此类问题的领域包括:实时控制系统,科学建模与仿真,游戏,财务模拟,图像处理等。对于与GPU架构非常匹配的问题,通常很容易实现2 倍与同一个问题的CPU实施,速度提高了甚至更多,经过调整的实现可以比CPU快10到100倍。GPU本身就是一种可编程且功能强大的计算设备。GPU具有大规模并行架构,由数千个更小,更高效的内核组成,旨在同时处理多个任务。
CPU的内存带宽约为20 GB / s,而GPU的内存带宽为150 GB / s。GPU的优点是功耗。GPU可以达到10 GFLOPS /瓦,CPU可以达到1 GIGAFLOPS /瓦。
如果我们要构建将并行执行的代码,则GPGPU是新技术的不错选择,而C ++ AMP是在编程和提供必要的库时提供便利的语言。
参考文献
Manika P, Xhumari E, Fejzaj J. GPU Programming Using C++ AMP[C]//RTA-CSIT. 2016: 140-144.