《OpenACC并行程序设计:性能优化实践指南》一 2.4 优化循环

2.4 优化循环

此刻,测试代码可以比原始代码加速2倍了。但这是能够获得的性能最好的代码吗?目前添加的导语可以在任何加速器上实现代码的提速,但为了在特定的测试机器上取得最佳性能,需要使用特定目标设备优化技术。万幸的是,OpenACC提供了一种指定devcie_type的优化手段,因此特定的子句仅在编译为指定设备的代码时才会生效。首先从分析目前代码的编译器反馈信息着手,针对matvec子程序,因为它是最为耗时的代码段(见图2-22)。

编译器提供了它如何并行化两个矩阵循环(第30和24行)的信息。但为了理解这些信息,需要理解OpenACC的三层并行层级:工作组(gang),工作项(worker)和向量(vector)。从最底层开始研究,向量化并行层级是针对细粒度并行的,对多个数据元素实施同样的操作。例如,有两个数组想要进行相加操作,有些硬件支持特殊的指令集,可以在成组的数据中同时进行加法操作。同时相加操作的数据个数称为向量长度,这一值随着不同硬件有所不同。顶层并行层级是工作组。工作组运行完全独立的操作,操作相互间不可能进行同步,且无法保证什么时候每个工作组会与其他工作组发生通信。由于工作组并行层级是粗粒度的,故具有很好的可扩展性。工作项并行层级介于工作组和向量之间。工作组由一个或多个工作项组成,每个工作项操控一个向量。同一个向量内的工作项可进行同步操作,且可以共享同一个高速缓存。每个由OpenACC编译器并行化的循环将被映射到以上三层并行层级中的至少一层,或串行执行(如果由seq修饰)。图2-23展示了OpenACC的三层并行层级。
基于以上背景知识,能够研究OpenACC编译器如何将二重循环映射到三层并行层级中的。它将第30行的循环映射到gang和vector并行层级,隐式地为每个gang建立了一个worker,且将向量长度设置为128。第34行的循环是串行执行的。有了这些信息,现在可以告知编译器额外的信息,引导获知编译器如何将这些循环映射到不同的并行层级。

2.4.1 缩短向量长度

使用NVIDIA GPU运行测试程序,作者会利用对NVIDIA GPU的理解和应用经验来更好地决策如何进行循环并行化。例如,知道NVIDIA GPU在向量循环在stride-1范围内进行数据访问时效率最高,即每个连续的循环体访问的是连续的数组元素。外层循环以跨度1访问row_offsets数组,内层循环也以跨度1访问Acols,Acoefs和xcoefs数组,这样使得代码更适合于向量化目标处理器。为内层循环添加loop导语允许进一步标明内层循环属于哪一个并行度层级。此外,可以说明向量长度是多少。编译器更倾向于使用128作为向量长度,但内层循环永远处理27个非零元素,这样会在每次运行循环时损失101个向量通道的性能。这里缩减向量长度是更好的选择,当然前提是提前知道循环次数。理想情况下,将向量长度缩减为27,因为已知循环迭代次数就是27,但NVIDIA GPU具有硬件向量长度,NVIDIA称为warp宽度,即32。因此,将向量宽度缩减为32,仅浪费掉5个向量通道而不是101个。图2-24展示了内层循环映射为vector并行层级且向量长度为32的matvec循环。注意:device_type子句需要添加到循环导语中,该子句表明列举的多条子句仅适用于nvidia类型的设备。这表明在其他设备上,编译器不进行这些特殊处理。

如图2-25所示,这一改动实际上降低了程序整体性能。可以尝试性地将代码改回原样,因为这一改动没有提升性能。但作者认为向量化最内层循环是一个正确的决定,故使用性能调试器来运行程序,以便能更好地理解为什么没有获得理想中的加速效果。

2.4.2 增加并行度

为了确定为何修改向量宽度后并未获得期望的性能提升效果,将使用PGProf指导分析模式来辅助监测性能瓶颈点。指导性分析是默认开启的,因此在当前可执行文件中开启一个新代码段时,需要选择“分析”选项卡并遵循文档建议的操作。分析过程最终将集中于GPU占用率,这是一个瓶颈,如图2-26所示。占用率是NVIDIA提出的一个术语,标志GPU工作强度的饱满程度,即GPU当前工作强度与其理论最大工作能力之间的比较。NVIDIA GPU由1个或多个流多处理器组成,通常称为SM。每个SM可以操控多达2048个并行线程,但这些线程不一定能够同时处于激活和运行状态。性能调试器输出结果显示对于可能激活的2048条线程,SM仅运行了512条线程,导致占用率为25%。图2-26中的红线说明了原因:SM可以最多调度16个线程块,对应于OpenACC的gang,但根据gang的尺度大小,需要有64个线程块才能达到最大占用率,但从“Threads/Block”行中发现仅有32个线程。以上情况说明通过缩减向量宽度至32,每个线程块的总线程数也被缩减到32,因此需要引入更大的并行度来更好地“喂饱”GPU。
可以通过两种途径增加gang的并行度:增加向量宽度和增加工作项。从前述章节可知,增加向量宽度是无效的,因为没有足够的计算量适应更长的向量宽度。因此将尝试增加工作项的数目。GPU线程块中的总任务可以认为是工作项数×向量宽度。因此,最多可以利用32个工作项,因为32×32=1024个GPU线程。图2-27展示了最终的matvec循环的代码。图2-28展示了通过改变每个工作组中的工作项数获得的加速效果。

在本书使用的测试系统中,8个worker和32个线程对应于最优gang。因为使用了device_type子句,这些优化仅作用于NVIDIA GPU,不会影响其他加速器设备对应的代码。

时间: 2024-11-01 14:06:24

《OpenACC并行程序设计:性能优化实践指南》一 2.4 优化循环的相关文章

《OpenACC并行程序设计:性能优化实践指南》一 1.1 简单的数据并行循环

1.1 简单的数据并行循环 在顺序处理器程序设计中,需要编写计算某个最终结果所需要的任务和数据操作的程序.通过创建OpenACC,编程人员可以插入编译指令给编译器提供信息,而这些编译指令是关于并行机会和数据在加速器与主机间来回传输的信息.结合编译器,程序员使用注记来创建.调试和优化并行代码,使得程序达到高性能. OpenACC帮助程序员编写高效的数据和任务并行软件. 数据并行关注跨多个并发执行线程的分布式数据操作.在计算机科学中,线程是串行执行一段代码的线程的缩写.通过使用多个线程,应用程序可以

《OpenACC并行程序设计:性能优化实践指南》一 导读

前言 欢迎阅读本书,这是一本由浅入深的书籍,从初学者到高级开发人员,都可以通过本书了解OpenACC的相关知识.本书由世界各地的24位作者共同编著而成,他们在高度并行编程的教学和实践方面分享了自己的专业知识.书中的例子既有时效性又不会过时.每个章节都是自包含的,可用于自学,也可以作为课堂教学的一部分. 这是一本关于并行编程的书,不仅仅介绍OpenACC语法或从文档中收集的信息,更介绍了如何编写实际的.高性能的以及可移植的程序,这些程序可以运行在从CPU到GPU的大量设备上.具体而言,书中演示了使

《OpenACC并行程序设计:性能优化实践指南》一 1.3 Amdahl定律及其扩展

1.3 Amdahl定律及其扩展 绘制任务运行时间,可以看到并行增加应用程序的扩展行为.并行计算的理论性能:运行在有N个处理单元的并行计算机上,理论上可以获得N倍加速.换一句话说,一个程序运行在10核处理器上可能获得10倍加速(对于固定大小的问题),在支持1000个并发执行线程的GPU上获得1000倍加速.开发人员依据Amdahl定律来讨论并行与串行间的加速比. 用计算机架构师Gene Amdahl来命名Amdahl定律.它不是实际上的定律,但是修改串行程序使其并行执行时,它相当接近模型理论加速

《OpenACC并行程序设计:性能优化实践指南》一 1.4 并行执行和竞争条件

1.4 并行执行和竞争条件 OpenACC并行化for循环(Fortran中是do循环),因此循环内的代码使用并发硬件执行线程并行执行. 循环内的变量i似乎是顺序递增的,但实际上在这个for循环中使用多个i变量的线程可能同时并行执行,这可能有点令人困惑.OpenACC不保证线程执行的顺序,注意这点非常重要.实际上,甚至不可能假设单调性.例如,很有可能第nCount―1次迭代实际上先于第0次迭代执行完. OpenACC不保证线程执行的顺序,注意这点非常重要. 总之,OpenACC编程人员不能也不应

《OpenACC并行程序设计:性能优化实践指南》一 2.6 小结

2.6 小结 OpenACC是一种描述型并行编程模型.在本章中,通过一个测试函数的应用,使用了OpenACC的多种特性来描述并行度和数据操控,并针对特定平台对代码进行了优化.尽管使用的是PGI编译器和PGProf性能调试器,但类似的优化流程也是适用于任何支持OpenACC工具包的应用的. 1.获得应用程序的性能分析结果,辨识和挖掘代码中的可并行之处. 2.逐步向编译器描述代码中可挖掘出的并行性.如果主机端和设备端使用各自的存储器,这一步骤后获得的代码很可能会减速. 3.描述应用程序的数据移动.编

《OpenACC并行程序设计:性能优化实践指南》一 1.6 控制并行资源

1.6 控制并行资源 accParaCounter.cpp中的嵌套循环结构可以用来控制循环并行性,因此可以控制并行资源消耗. 大多数并行编程人员遵循在系统中利用最大并行性来实现最高性能的方法. 大量线程背后的思路是给予并行调度器尽可能多的线程以供调度,从而最大化地利用所有计算资源.GPU编程人员喜欢用占有率(occupancy)作为线程并行度的度量.高占有率意味着调度器有更多的激活线程以供调用,因此有机会实现更高性能. 高占有率并不一定转为最快的应用性能.指令级并行(Instruction le

《OpenACC并行程序设计:性能优化实践指南》一 2.1 测试代码:共轭梯度法

2.1 测试代码:共轭梯度法 本章中使用共轭梯度法作为标准测试代码.共轭梯度法是一种迭代算法,常用来逼近一组线性方程组成的大型稀疏系统.由于这种系统通常规模庞大,因此难于使用直接法进行求解.阅读本章不需要理解共轭梯度法的数学含义.提供了共轭梯度法的C和Fortran编码实现供读者参考.简洁起见,本章仅展示了C代码.不过不必担心,C代码的应用方式可以扩展到Fortran代码,两者区别不大.本章使用的代码遵循Apache许可,版本2.0.详情请阅读许可文件. 示例代码包含两种数据结构.第一个是向量结

《OpenACC并行程序设计:性能优化实践指南》一 1.5 无锁编程

1.5 无锁编程 互斥锁是用于同步进程或线程的常用机制,这些进程或线程需要访问并行程序中的一些共享资源.互斥锁就像它们名字所说的:如果一个线程锁住了资源,另一个线程希望访问它需要等待第一个线程解锁这个资源.一旦资源被解锁,第二个线程在处理这个资源时会一直锁住它.程序的线程必须遵守:一旦使用完共享资源尽快解锁,以保持程序执行流程. 由于OpenACC中没有锁,编程人员需要熟悉无锁编程和数据结构的概念.无锁方法保证至少一个执行该方法的线程的进展.可能存在某些线程可以被延迟的情况,但是保证至少一个线程

《OpenACC并行程序设计:性能优化实践指南》一 第3章 使用Score-P和Vampir分析混合应用性能

第3章 使用Score-P和Vampir分析混合应用性能 Guido Juckeland 德国亥姆霍兹联合会(HZDR)信息服务和计算机系 Robert Dietrich 德国德累斯顿工业大学 本章的目的是让读者熟悉逐步性能提升的概念,以及在向OpenACC应用程序添加其他并行模式时所涉及的工具.混合应用程序可能会遭受许多性能瓶颈,应用程序运行期间所有活动的整体图可以揭示如何提高整体性能. 阅读本章后,读者将会理解以下内容: 混合应用程序(例如,MPI+OpenACC)性能分析的术语和方法. 如