完善资料让更多小伙伴认识你,还能领取20积分哦, 立即完善>
1. Loop Interchange 相关基本概念1.1 访问局部性访问局部性指的是在计算机科学领域中应用程序在访问内存的时候,倾向于访问内存中较为靠近的值。这种局部性是出现在计算机系统中的一种可预测行为,我们可以利用系统的这种强访问局部性来进行性能优化。访问局部性分为三种基本形式,分别为时间局部性、空间局部性、循序局部性。 而本文主要讲述的Loop Interchange主要是利用了空间局部性。空间局部性指的是,最近引用过的内存位置以及其周边的内存位置容易再次被使用。比较常见于循环中,比如在一个数组中,如果第3个元素在上一个循环中被使用,那么本次循环中极有可能会使用第4个元素;如果本次循环确实使用第4个元素,就是命中上一次迭代所prefetch到的cache数据。 所以对于数组循环运算,可以利用空间局部性这一特征,保证两次相邻循环中对数组元素的访问在内存上是更加靠近的,即循环访问数组中的元素时stride越小,相应的性能可能会有所优化。 那么,数组在内存上是如何存储的呢? 1.2 Row-major 和 Column-majorRow-major 和 Column-major 是两种将多维数组存储在线性存储中的方式。数组的元素在内存中是连续的;Row-major ordering代表行的连续元素在内存中彼此相邻,而Column-major ordering则是代表列的连续元素彼此相邻,如下图所示。 虽然Row和Column的名称看起来像是特指二维数组,但是Row-major和Column-major也可以推广适用于任何维度的数组。 那么在C/C++中,数组是以以上哪种方式存储的呢? 举一个小例子,用cachegrind工具来展示C使用两种不同的访问形式的CPU的cache丢失率对比。 按行访问:
按列访问:
根据上述C代码中对相同数组的两种不同访问方式时cache丢失率的对比,可以说明在C/C++代码中,数组是以Row-major形式储存的。也就是说,如果前一步访问了 至于其他常用的编程语言,Fortran、MATLAB等则是默认Column-major形式。 1.3 Loop InterchangeLoop Interchange利用系统倾向于访问内存中较为靠近的值的特征以及C/C++ Row-major的特点,通过改变循环嵌套中两个循环之间的执行顺序,增加整体代码空间局部性。此外,它还可以启用其他重要的代码转换,例如,Loop Reordering就是Loop Interchange扩展到两个以上循环被重新排序时的优化。在LLVM中,Loop Interchange需要通过使能 2. 优化示例2.1 基础场景简单看下面一个矩阵运算的示例: 原始代码:
试着把
可以发现,在原始代码中,最内层的 那么cache命中率是否真的增加,以及两者的性能又如何呢? 原始代码:
Loop Interchange后的结果如下:
两者相比: L1-dcache-loads的数目差不多,因为要访问的数据总量差不多; L1-dcache-load-misses所占L1-dcache-loads的比例在进行Loop Interchange代码修改后降低了将近10倍。 同时,性能数据上也能带来接近9.5%的性能提升。 2.2 特殊场景当然,在实际使用时,并不是所有的场景都是如2.1中所示的那种工整的可Loop Interchange场景。
如上述场景,如果N是超大数组,那么Loop Interchange理论上可以带来较大收益;但由于两层循环中间增加一个分支判断,导致原本可以Loop Interchange的场景无法实现。 针对这种场景,可以考虑将中间的分支判断逻辑剥离,可以先保证Loop Interchange使得数组
当然,这样的源码修改也需要考虑cost是否值得,如果该if分支进入频率非常高,那么之后回退带来的cost也会较大,可能就需要重新考虑Loop Interchange是否值得;反之,如果分支进入频率非常低,那么Loop Interchange的实现还是可以带来可观的收益的。 3. 毕昇编译器在LLVM社区的贡献毕昇编译器团队在LLVM社区中对Loop Interchange pass也做出了不小的贡献。团队从legality、profitability等方面对Loop Interchange pass做了全方位的增强,同时也对该pass所支持的场景做了大量的扩展。在Loop Interchange方面,近两年来团队小伙伴为社区提供了二十余个主要的patch,包含Loop Interchange,以及相关的dependence analysis、loop cache analysis、delinearization等分析和优化的增强。简单举几个例子:
这两个patch将Loop Interchange的应用场景扩展到内层或者外层循环中包含不止一个induction variable的情况:
这两个patch将Loop Interchange的应用场景扩展到支持浮点类型的reduction计算的场景:
D120386 [LoopInterchange] Try to achieve the most optimal access pattern after interchange (https://reviews.llvm.org/D120386) 这个patch增强了Interchange的能力使编译器能够将循环体permute成为全局最优的循环顺序:
=>
D124926 [LoopInterchange] New cost model for loop interchange (https://reviews.llvm.org/D124926) 这个patch为loop interchange提供了一个全新的,功能更强的cost model,可以更准确地对loop interchange的profitability做出判断。 此外,我们还为社区提供了大量的bugfix的patch:
以及其他功能的增强:
结语如果想要尽可能的利用Loop Interchange优化,那在书写C/C++代码时,请尽可能保证每个迭代之间对数组或数列的访问stride越小越好;stride越接近1,空间局部性就越高,自然cache命中率也会更高,在性能数据上也可以拿到更理想的收益。另外,由于C/C++的存储方式为Row-major ordering,所以在访问多维数组时,请注意内层循环要为Column才能拿到更小的stride。 |
|
相关推荐 |
|
只有小组成员才能发言,加入小组>>
6个成员聚集在这个小组
加入小组Native Memory Tracking 详解(2):追踪区域分析(一)
16909 浏览 0 评论
15030 浏览 0 评论
16907 浏览 0 评论
15850 浏览 0 评论
openEuler 资源利用率提升之道 04:CPU 抢占和 SMT 隔离控制
22850 浏览 0 评论
Native Memory Tracking 详解(4):使用 NMT 协助排查内存问题案例
929浏览 0评论
openEuler社区开源项目:CPDS(容器故障检测系统)介绍
662浏览 0评论
1034浏览 0评论
1027浏览 0评论
1077浏览 0评论
小黑屋| 手机版| Archiver| 电子发烧友 ( 湘ICP备2023018690号 )
GMT+8, 2024-11-8 07:24 , Processed in 0.687140 second(s), Total 45, Slave 37 queries .
Powered by 电子发烧友网
© 2015 bbs.elecfans.com
关注我们的微信
下载发烧友APP
电子发烧友观察
版权所有 © 湖南华秋数字科技有限公司
电子发烧友 (电路图) 湘公网安备 43011202000918 号 电信与信息服务业务经营许可证:合字B2-20210191 工商网监 湘ICP备2023018690号