GPU 并行优化的几种典型策略

如何对现有的程序进行并行优化,是 GPU 并行编程技术最为关注的实际问题。本文将提供几种优化的思路,为程序并行优化指明道路方向。

优化前准备

首先,要明确优化的目标 - 是要将程序提速 2 倍?还是 10 倍?100倍?也许你会不假思索的说当然是提升越高越好。

但这里存在一个优化成本的问题。在同样的技术水平硬件水平下,提升 2 倍也许只要一个下午的工作量,但提高 10 倍可能要考虑到更多的东西,也许是一周的工作量。提高 100 倍, 1000 倍需要的成本,时间就更多了。

然后,需要将这个问题进行分解。通常来说先对数据集进行分解,然后将任务进行分解。这里要从数据集这样的矩阵角度来分析数据,将输入集和输出集中各个格点的对应关系找出来,然后分派给各个块,各个线程。

策略一:识别代码中的瓶颈所在

分析程序效率的瓶颈所在一方面靠的是分析。这种方式对于代码结构比较简单的程序非常有用,但对于实际应用中的复杂项目,人脑分析往往会导致错误的结论 - 也许你费尽心思想出来了瓶颈,然后对它做了优化,之后却发现效率仅仅提升了 1%。

因此更有效的方法是使用分析工具来找出瓶颈。

还有一点要特别说明的是,在 GPU 进行数据处理的时候,CPU 可以考虑做点别的事情,比如去服务器取数之类的,这样就将 CPU 并行和 GPU 并行结合起来了,程序效率自然会大大提高。

策略二:合理的利用内存

首先,要灵活的使用显卡中的各类内存结构,如共享内存,常量内存等。特别要注意共享内存的使用,它的速度可是接近一级缓存的。

此外,必要时对多个内核函数进行融合。因为这样可以避免启动新的内核函数时需要进行的数据传递问题,还可以重用前面的任务遗留下的一些有用的数据。不过,如果是对别人写的多个内核函数进行融合的话,一定要注意其中隐含的同步问题 - 上个内核函数的代码彻底执行完毕之后,下个内核函数才会开始执行。

然后,对于数据的访问应该采取合并访问的方式。一次访问的数据应当大于 128 字节,这样才能充分地利用显卡的带宽。

策略三:传输过程的优化

数据在内存和显存之间进行交换是非常费时的。

对于这样的问题,首先我们可以以锁页内存的方式使用主机端内存。所谓锁页内存,是指该区域内存和显卡的传递不需要 CPU 来干预,如果某区域不声明为锁页内存,那么在内存往显存中或者显存往内存中传递数据前,会发生一些开销不小的锁定操作(表示该区域内存正在和显存发生数据传递,CPU勿扰)。

然后,还需要重点推荐的是零复制内存。它是一种特殊的锁页内存,一种特殊的内存映射。它允许你将主机内存映射到 GPU 的内存空间。如果你的程序是计算密集型的,那么这个机制就会非常有用,它会自动将数据传输和计算重叠。

策略四:线程结构布局的优化

建立科学的计算网格,通过设定合适的维数,块数,以及块内线程数来尽量实现合并的内存访问,保证最大的内存带宽。

要学会灵活使用多维度的计算网格,而不是仅仅局限于一维。多维计算网格的使用请参考我的这篇文章。

尤其在单维度的块数受到限制的时候,多维网格就必须被考虑进来了。

策略五:从算法本身进行任务级的分解

将算法的步骤分解各个不相关的部分,步骤内采用GPU并行,这几个步骤则采用CPU并行。

小结

本文仅仅是提供优化的总体策略和思路,至于具体的实现方法,请参考相关资料实现之。

摘自: 穆晨-博客园

最新文章