编译优化之 - 通用循环优化
前言
循环是程序中最常见结构,针对循环已有众多的优化技术。循环的优化分为源码上的修改和编译器的优化,编译器能自动执行许多循环优化技术,但对源代码的修改可辅助编译器就行优化处理。
1. 源码上的优化
1. 多重循环的“外小内大”
在多重循环中,采用迭代次数较小的循环驱动内层迭代次数较大的循环能减少内存的消耗,如下示例:
for (int i = 0; i < 10000; i++) {for (int j = 0; j < 200; j++) {}
}
改为:
for (int i = 0; i <200 ; i++) {for (int j = 0; j < 10000; j++) {}
}
2. 循环变量实例化放在循环外
如下示例:
int i,j;
for (i = 0; i <200 ; i++) {for (j = 0; j < 10000; j++) {}
}
3. 循环无关的表达式放到循环外
如下示例:
int i,j;
for (i = 0; i <200 ; i++) {for (j = 0; j < 10000; j++) {j = i*j*x/(y-1);}
}
改为:
int i,j,tmp;
tmp = x/(y-1);
for (i = 0; i <200 ; i++) {for (j = 0; j < 10000; j++) {j = i*j*tmp;}
}
4. 消除循环终止时的方法调用
如下示例:
for (int i = 0; i < vec.size(); i++) {}
改为:
int size = vec.size();
for (int i = 0; i < size; i++) {}
5. 循环外部捕获异常
在循环外部捕获异常能有效减少内层消耗。一般而言,foreach 循环优先于传统的 for 循环。
6. 循环的 i++ 改为 ++i
效率上来说++i 比 i++来的更有效率(后置操作符必须先保存操作数原来的值),但现代编译器上这个影响会很小。
2. 编译器角度的优化
1. 循环展开(loop Unrolling)
重组循环,以便每次迭代多次使用加载的值,让一拍流水线上尽可能满。在流水线中,会因为指令顺序安排不合理而导致CPU等待空转,影响流水线效率。循环展开为编译器进行指令调度带来了更大的空间。
如下示例:
do j = 1,2*ndo i = 1,mA(j) = A(j) + B(i)enddo
enddoUnrolling之后:
do j = 1,2*n by 2do i = 1,mA(j) = A(j) + B(i)A(j+1) = A(j+1) + B(i)enddo
enddo
2. 循环分块(loop tiling)
由于内存空间有限,代码访问的数据量过大时,无法一次性将所需要的数据加载到设备内存,循环分块能有效提高处理器 cache 上的访存效率,改善数据局部性。(分块应用于外部循环,它会增加计算的空间和时间局部性;分块应与缓存块一起使用,因为这样可以提高最内部软件流水线的效率)。示意如图:
如下示例:
do j = 1,2*ndo i = 1,mA(j)= A(j) + B(i)enddo
enddoTiling之后:
do jj = 1,2*n by 2do i = 1,mdo j = jj, jj+2-1A(j)= A(j)+B(i)enddoenddo
enddoUnroll and Jam之后:
do jj = 1,2*n by 2do i = 1,mA(j)= A(j)+B(i)A(j+1)= A(j+1)+B(i)enddo
enddo
更多关于 loop tiling 的优化方法可参见论文:《Augmenting Loop Tiling with data Alignment for Improved Cache Performance》
、《Automatic Tiling of Iterative Stencil Loops》
、《面向局部性和并行优化的循环分块技术》
3. 循环交换(loop interchange)
内外层循环交换,改善空间局部性,并最大限度地利用引入缓存的数据。对循环进行重新排序,以最大程度地减少跨步并将访问模式与内存中的数据存储模式对齐。
如下示例:
do i=1,ndo j=1,nA(i,j)=B(i,j)*C(i,j)enddo
enddointerchange之后:
do j=1,ndo i=1,nA(i,j)=B(i,j)*C(i,j)enddo
enddo
4. 循环融合(loop fusion)
将相邻或紧密间隔的循环融合在一起,减少的循环开销和增加的计算密度可改善软件流水线,数据结构的缓存局部性增加,编译器在某些情况下未执行融合,需要手动完成。
如下示例:
for(i = 0; i < size; ++i){A(i) = a(i) + b(i);c(i) = 2*a(i);
}
for(i = 1; i < size-1; ++i){D(i) = c(i) + a(i);
}fusion之后:
A(0) = a(0) + b(0);
c(0) = 2*a(0);
A(size-1) = a(size-1) + b(size-1);
c(size-1) = 2*a(size-1);
for(i = 1; i < size-1; ++i){A(i) = a(i) + b(i);c(i) = 2*a(i);D(i) = c(i) + a(i);
}
5. 循环分裂(loop fission)
将循环分成多个循环,可以在有条件的循环中使用,分为无条件循环和含条件循环。
如下示例:
for(i = 0; i < size; ++i){A(i) = a(i) + b(i);c(i) = 2*a(i);if(temp[i] > data)d(i) = a(i);
}fission之后:
for(i = 0; i < size; ++i){A(i) = a(i) + b(i);c(i) = 2*a(i);
}
for(i = 0; i < size; ++i){if(temp[i] > data)d(i) = a(i);
}
6. 循环剥离(loop peeling)
剥离 k 次迭代意味着从循环主体中删除这些迭代并将其放置在循环主体之前或之后。
如下示例:
do i=1,nY(i,n) = (1.0 - x(i,1))*y(1,n) + x(i,1)*y(n,n)
enddopeeling之后:
t2 = y(n,n)
t1 = y(1,n)
y(1,n) = (1.0 - x(1,1))*t1 + x(1,1)*t2
do i=2,n-1Y(i,n) = (1.0 - x(i,1))*t1 + x(i,1)*t2
enddo
y(n,n) = (1.0 - x(n,1))*t1 + x(n,1)*t2
7. 循环强度减弱(Strength reduction in loops)
使用简单的表达式来替换复杂的表达式。
例如,除法替换,如下示例:
z(i) = x(i)/y(i)
w(i) = u(i)/v(i)Strength reduction之后:
tmp = 1.0/(y(i)*v(i))
z(i) = x(i)*v(i)*tmp
w(i) = u(i)*y(i)*tmp
References:
- https://blog.csdn.net/qq_37436998/article/details/100055770
- https://www.cs.colostate.edu/~mstrout/CS553Fall07/Slides/lecture27-tiling.pdf
- http://users.cecs.anu.edu.au/~Alistair.Rendell/sc02/module2b.pdf
编译优化之 - 通用循环优化相关推荐
- java for循环优化_Java for循环优化
小编典典 容易被手工制作的微基准所迷惑-您永远不知道它们的 实际 测量值.这就是为什么有诸如JMH之类的特殊工具的原因.但是,让我们分析一下原始的手工基准会发生什么: static class HDo ...
- 【编译原理】中间代码优化(三) 循环优化
文章目录 循环优化概述. 计算必经节点集. 循环查找算法. 1.查找回边. 2.查找循环. 代码外提. 强度削弱. 删除归纳变量. 循环优化概述. 什么叫做循环?循环就是程序中那些可能反复执行的代码序 ...
- AI System 人工智能系统 TVM深度学习编译器 DSL IR优化 计算图 编译 优化 内存内核调度优化 DAG 图优化 DFS TaiChi 函数注册机 Registry
DSL 领域专用语言 TVM深度学习编译器 AI System 人工智能系统 参考项目 TaiChi 三维动画渲染物理仿真引擎DSL TVM 深度学习DSL 密集计算DSL LLVM 模块化编译器 编 ...
- 基于中间代码的优化中,循环的查找算法有哪些?循环优化的方法有哪些?举例说明。
基于中间代码的优化中,循环的查找算法有哪些?循环优化的方法有哪些?举例说明. 基于中间代码的优化中,循环的查找算法有哪些?循环优化的方法有哪些?举例说明. 西北工业大学编译原理课件第八章 代码优化.p ...
- java 优化 寄存器_JVM性能优化系列-(6) 晚期编译优化
6. 晚期编译优化 晚期编译优化主要是在运行时做的一些优化手段. 6.1 JIT编译器 在部分的商用虚拟机中,java程序最初是通过解释器(Interpreter) 进行解释执行的,当虚拟机发现某个方 ...
- QQ音乐客户端Web页面通用性能优化实践
导语 | QQ音乐 Android 客户端的 Web 页面日均 PV 达到千万量级,然而页面的打开耗时与 Native 页面相距甚远,需要系统性优化.本文将介绍 QQ 音乐 Android 客户端在进 ...
- QQ音乐Android客户端Web页面通用性能优化实践
QQ音乐 Android 客户端的 Web 页面日均 PV 达到千万量级,然而页面的打开耗时与 Native 页面相距甚远,需要系统性优化.本文将介绍 QQ 音乐 Android 客户端在进行 Web ...
- webview加载的页面和浏览器渲染的页面不一致_QQ音乐Android客户端Web页面通用性能优化实践...
QQ音乐 Android 客户端的 Web 页面日均 PV 达到千万量级,然而页面的打开耗时与 Native 页面相距甚远,需要系统性优化.本文将介绍 QQ 音乐 Android 客户端在进行 Web ...
- web 折线图大数据量拉取展示方案_【第2010期】QQ音乐Android客户端Web页面通用性能优化实践...
前言 今日早读文章由QQ音乐客户端开发工程师@关岳分享,公号:云加社区(ID:QcloudCommunity,腾讯云官方开发者社区)授权分享. 正文从这开始~~ QQ音乐 Android 客户端的 W ...
最新文章
- 【loj6342】跳一跳 期望dp
- java hibernate sql,Java Hibernate中使用SQL 而不使用HQL
- Android之CSDN 牛人博客索引
- 网络操作系统第242页作业
- 难道计算机专业真的没落
- 一些SAP Partners能够通过二次开发实现打通C/4HANA和S/4HANA的方法介绍
- 华为鸿蒙os正在国外小规模测试,华为鸿蒙OS正小规模测试
- C语言经典迭代算法之求解函数定积分(详解)
- 毕业设计基础测试 定位+后端处理+退出删除
- linux 软硬连接
- 面试官:换人!赶快换人!连CopyOnWriteArrayList都没听过!确实没听过
- 人工智能与大数据就业前景_大数据,数据分析和人工智能方向就业前景
- outlook配置126邮箱
- SpringCloud微服务实战——搭建企业级开发框架(三十一):自定义MybatisPlus代码生成器实现前后端代码自动生成
- Java-dao模式
- 计算机应用杂志论文格式要求,计算机应用专业论文格式说明.doc
- (26)盘古自研框架BackPropagation
- python pymssql连接本地SQL SERVER
- 记一次Oracle 11.2.0.4 RAC异地还原到单实例
- Git冲突和解决冲突
热门文章
- 利用tcp三次握手,使用awl伪装MAC地址进行多线程SYN Flood
- [编程题] 翻转数列--附详细分析思路
- 利用Tensorflow构建RNN并对序列数据进行建模
- 【07】概率图推断之信念传播
- Mathorcup数学建模竞赛第五届-【妈妈杯】A题:城市相邻两交叉口信号配时优化(附一等奖获奖论文和matlab代码实现)
- 程序员眼中的中国传统文化-王阳明《传习录》18
- 代码复杂度分析——时间、空间复杂度
- 【BZOJ 2054】 疯狂的馒头
- 案例解析|自然保护区水资源远程监控方案
- Oracle 、SqlServer 根据日期逐日、逐月递增累加、逐行累加