8 代码优化
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-pje0kXbt-1642042188871)(…/picture/41.png)]
- 代码优化是对被优化的程序进行的一种语义保持的变换
- 语义保持:
-
- 程序的可观察行为不能改变
- 变换的目的是让程序能够比变换前(更小、快、节能等)
很困难!!
循环、支配性
在入口节点为 b 0 b_0 b0的流图中,当且仅当 b i b_i bi位于从 b 0 b_0 b0到 b j b_j bj的每条路径上时,结点 b i b_i bi支配结点 b j b_j bj, b i D O M b j b_i \ DOM \ b_j bi DOM bj
集合 D O M ( b j ) DOM(b_j) DOM(bj)包含了支配 b j b_j bj的所有节点的名字,包括 b j b_j bj,称为结点 b j b_j bj的支配结点集
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-yH6a5lp8-1642042188873)(…/picture/42.png)]
循环
对于大多数程序,绝大多数运行时间都花费在循环部分
利用控制流图 C F G CFG CFG来识别程序中的循环,这是开展循环优化的必要条件
基本思路
根据流图计算所有结点的支配结点集,然后得到流图中的回边,根据回边就可以确定该流图中的循环。
回边
b → a b \to a b→a是流图中的一条有向边,如果 a D O M b a \ DOM \ b a DOM b,则称 b → a b \to a b→a是流图中的一条回边。
循环
假设有向边 b → a b \to a b→a是回边,则它组成的循环是由结点 a a a,结点 b b b及满足以下条件的结点组成:即从该结点出发有通路到达 b b b而该通路不经过 a a a,其中 a a a是循环中唯一的入口结点。
循环节点序列的性质
- 是强连通的,即任意两节点间,必有一条通路,且该通路上各节点都属于该结点序列
- 如果循环 L L L的回边是 b → a b \to a b→a,则对所有 n i ∈ L n_i \in L ni∈L,有 a D O M n i a \ DOM \ n_i a DOM ni,并且 a a a是他们中间仅有的唯一入口节点。
-
- 入口节点:是指一个节点,满足从序列外某节点有一条有向边指向它,或者它就是流图的首结点
数据流分析
数据流分析和优化紧密结合,为了优化需要做数据流分析。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-t28GSqZB-1642042188873)(…/picture/43.png)]
静态保守
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-qPk75SgV-1642042188874)(…/picture/44.png)]
- 如上图所示 A = { 3 } A = \{3\} A={3}
- 如果 L 2 L_2 L2中 y = 2 y=2 y=2,那么 A = { 2 , 3 } A = \{2,3\} A={2,3}
- 如果 L 1 L_1 L1和 L 2 L_2 L2中 y = 2 y=2 y=2,那么 A = { 2 } A = \{2\} A={2}
数据流分析通过对程序代码进行静态分析,得到关于程序数据相关的保守信息。
- 保守:因为必须保证程序分析的结果是安全的
根据优化的目标不同,需要进行的数据流分析也不同
- 到达定义分析
- 活性分析
到达定义分析
定义、使用
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-cKAQ9ZMy-1642042188874)(…/picture/45.png)]
到达定义分析:对每个变量的使用点,有哪些定义可以到达?
(即:该变量的值是在哪儿赋值的)
对任何一条定义:
[ d : x = … ] [d:x= \dots] [d:x=…]
给出两个集合:
g e n [ d : x = … ] = { d } gen[d:x=\dots] = \{d\} gen[d:x=…]={d} 产生集:自身编号
k i l l [ d : x = … ] = d e f s [ x ] − { d } kill[d:x=\dots]=defs[x]-\{d\} kill[d:x=…]=defs[x]−{d} 杀死集, d e f s [ x ] defs[x] defs[x]表示 x x x的所有定义点
到达定义分析:通过数据流方程来进行数据流分析。
数据流方程:
i n [ s i ] = o u t [ s i − 1 ] in[s_i]=out[s_{i-1}] in[si]=out[si−1]
o u t [ s i ] = g e n [ s i ] ∪ ( i n [ s i ] − k i l l [ s i ] ) out[s_i]=gen[s_i] ∪ (in[s_i]-kill[s_i]) out[si]=gen[si]∪(in[si]−kill[si])
i n : in: in: 在语句 s i s_i si之前哪些语句可以见到、可以到达
o u t : out: out: 在语句 s i s_i si之后哪些语句可以出去到达下一条
计算in/out集合
-
采用不动点算法,到最后一次迭代时,in或out集合已经不发生变化。
-
以基本块为单位计算 g e n / k i l l / i n / o u t gen/kill/in/out gen/kill/in/out
对于基本块 B B B:
- g e n [ B ] gen[B] gen[B]: B中定值的并能够到达B出口处所有定值集合,即B所“产生”的定值点集合,基本块中没有被块中各语句“杀死”的定值的集合
- k i l l [ B ] kill[B] kill[B]: B外的定值点集:这些定值点到达B的入口处,但所定值的变量在B中已被重新定值,即B所杀死的定值集合
- i n [ B ] in[B] in[B]: 到达流图中基本块B的入口处的定值的集合
- o u t [ B ] out[B] out[B]: 到达流图中基本块B的出口处的定值的集合
数据流方程:
i n [ B ] = ∪ ( o u t [ P ] ) in[B]=∪(out[P]) in[B]=∪(out[P]) P是B的一个前驱
o u t [ B ] = g e n [ B ] ∪ ( i n [ B ] − k i l l [ B ] ) out[B]=gen[B]∪(in[B]-kill[B]) out[B]=gen[B]∪(in[B]−kill[B])
UD链
引用-定值链
- UD链是一个列表,对于变量的每一次引用,到达该引用的所有定值都在该列表中
-
- 如果块B中变量a的引用之前有a的定值,那么只有a的最后一次定值会在该引用的UD链中。
- 如果块B中变量a的引用之前没有a的定值,那么a的这次引用的UD链就是 i n [ B ] in[B] in[B]中a的定值的集合。
活性分析
在代码生成时假设目标机器有无限多个(虚拟)寄存器可用
- 简化了代码生成的算法
- 对物理机器是个坏消息
-
- 机器只有有限多个寄存器
-
- 必须把无限多个虚拟寄存器分配到有限个寄存器中
- 这是寄存器分配优化的任务
-
- 需要进行活性分析
能否把a、b、c 3个变量同时放到寄存器 r r r中?
只要活跃区间不同,就可以进行代码重写。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-q0Uoymcb-1642042188875)(…/picture/46.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-7jvpNxZx-1642042188876)(…/picture/47.png)]
对任何一条语句: [ d : s ] [d:s] [d:s]
给出两个集合:
u s e [ d : s ] = { x ∣ 变 量 x 在 语 句 s 中 被 使 用 } use[d:s] = \{x|变量x在语句s中被使用\} use[d:s]={x∣变量x在语句s中被使用}
d e f [ d : s ] = { x ∣ 变 量 x 在 语 句 s 中 被 定 义 } def[d:s]=\{x|变量x在语句s中被定义\} def[d:s]={x∣变量x在语句s中被定义}
例如: 1 : x = y + z 1:x = y + z 1:x=y+z
u s e [ 1 : ] = { y , z } use[1:]=\{y,z\} use[1:]={y,z}
d e f [ 1 : ] = { x } def[1:]=\{x\} def[1:]={x}
数据流方程
o u t [ s i ] = i n [ s i + 1 ] out[s_i]=in[s_{i+1}] out[si]=in[si+1]
i n [ s i ] = u s e [ s i ] ∪ ( o u t [ s i ] − d e f [ s i ] ) in[s_i]=use[s_i] ∪ (out[s_i]-def[s_i]) in[si]=use[si]∪(out[si]−def[si])
需要从下向上计算,先计算out再计算in
一般的数据流方程:
o u t [ s ] = ∪ p ∈ s u c c [ s ] i n [ p ] out[s]=∪_{p \in succ[s]}in[p] out[s]=∪p∈succ[s]in[p] p是s的后继
i n [ s ] = u s e [ s ] ∪ ( o u t [ s ] − d e f [ s ] ) in[s]=use[s] ∪ (out[s]-def[s]) in[s]=use[s]∪(out[s]−def[s])
从初始的空集出发,循环到没有集合变化为止
对于基本块B:
- u s e [ B ] use[B] use[B]: B中被定值之前要引用的变量集合
- d e f [ B ] def[B] def[B]: B中定值且定值前未曾在 B B B中引用过的变量集合
- i n [ B ] in[B] in[B]: 在基本块B的入口处的活跃变量集合
- o u t [ B ] out[B] out[B]: 在基本块B的出口处的活跃变量集合
o u t [ B ] = ∪ i n [ S ] out[B]=∪ in[S] out[B]=∪in[S] S是B的一个后继
i n [ B ] = u s e [ B ] ∪ ( o u t [ B ] − d e f [ B ] ) in[B]=use[B] ∪ (out[B]-def[B]) in[B]=use[B]∪(out[B]−def[B])
例子:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ZrqcznQO-1642042188876)(…/picture/48.png)]
基本块 | in/out | in/out | in/out | in/out |
---|---|---|---|---|
L1 | {} {} | {} {} | {c} {ac} | {ac} {ac} |
L2 | {} {} | {ac} {} | {ac} {ac} | {ac} {ac} |
L3 | {} {} | {c} {} | {c} {} | {c} {} |
DU链
定值-引用链
设变量x有一个定值d,该定值所有能够到达的引用u的集合称为x在d处的定值-引用链(DU链)
如果在求解活跃变量数据流方程中的 o u t [ B ] out[B] out[B]时,修改 o u t [ B ] out[B] out[B],将 o u t [ B ] out[B] out[B]表示成从B的末尾处能够到达的引用的集合,那么,可以直接利用这些信息计算基本块B中每个变量x在其定值处的DU链
- 如果B中x的定值d之后有x的第一个定值 d ′ d^{'} d′,则 d d d和 d ′ d' d′之间x的所有引用构成 d d d的DU链
- 如果B中x的定值d之后没有x的新的定值,则B中d之后x的所有引用以及out[B]中x的所有引用构成d的DU链
优化
前端优化
常量折叠:
基本思想:在编译期计算表达式的值
语法树节点个数减少
机器码指令数目减少
- 在编译期计算表达式的值
- 例如 a = 3 + 5 → a = 8 a=3+5 \to a=8 a=3+5→a=8
- i f ( t r u e a n d f a l s e ) → i f ( f a l s e ) if(true \ and \ false) \to if(false) if(true and false)→if(false)
可以在整型、布尔型、浮点型等数据类型上进行。
容易实现,可以在语法树或者中间表示上进行
通常被实现成公共子函数被其他优化调用
代数化简:
基本思想:利用代数系统的性质对程序进行化简
实例: a = 0 + b → a = b a=0+b \to a=b a=0+b→a=b
- a = 1 ∗ b → a = b a=1*b \to a=b a=1∗b→a=b
- 2 ∗ a → a + a 2*a \to a + a 2∗a→a+a(强度消弱)
- 2 ∗ a → a < < 1 2*a \to a << 1 2∗a→a<<1(强度消弱)
不可达代码的删除
基本思想:静态移除程序中不可执行的代码
实例:
if(false) s1;
else s2; s2为不可达
中期优化
依赖于具体所使用的中间表示:
- 控制流图CFG,控制依赖图CDG,静态单赋值形式SSA。
共同的特点是需要进行程序分析
- 优化是全局进行的,而不是局部
- 通用的模式:程序分析->程序重写
常量传播
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-RxIz34Zn-1642042188877)(…/picture/49.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-mUgHtmXt-1642042188878)(…/picture/50.png)]
拷贝传播
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-dywsslEC-1642042188878)(…/picture/51.png)]
死代码删除
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Rj90p6Rm-1642042188879)(…/picture/52.png)]
后端优化
不同角度看优化
窥孔优化
目标代码级别,采用滑动窗口
是指在语句/指令序列上滑动一个包含几条语句/指令的窗口(称为窥孔),发现其中不够优化的语句/指令序列,用一段更有效地序列来替代它。
如前面介绍的:常量折叠、常量传播、代数化简、死代码删除
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-RjXXJCNY-1642042188879)(…/picture/53.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ZwuGZBjm-1642042188880)(…/picture/54.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-bNrcuuAW-1642042188880)(…/picture/55.png)]
局部优化
中间代码级别,以基本块为单位
指的是一个基本块范围内进行的优化。
常量传播、常量合并、删除公共子表达式、复写传播、删除无用赋值、代数化简等
基本块内的许多优化也可以看成将基本块作为窗口的窥孔优化。
循环优化
中间代码级别,针对循环
对于大多数应用程序,循环部分的执行时间在整个程序执行时间中所占的比重非常大,因此针对循环的优化值得关注
基本两类的循环优化:
把所谓的循环不变量(即产生的效果独立于循环执行次数的表达式计算)放到循环的前面,这里的循环只存在一个入口
可以借助UD链可以查找循环不变量,例如:
对于循环内部的语句x:=y+z,若y和z的定值点都在循环外,则x:=y+z为循环不变量。
在循环的入口节点前面建立一个新节点(基本块),称之为循环的前置结点,外提代码都放到前置结点中
前置结点以循环的入口节点为唯一后继,原来流图中从循环外引到循环入口结点的有向边,改成引到循环前置结点。
看个例子:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-HVlkrLz2-1642042188881)(…/picture/57.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-v76QngEk-1642042188881)(…/picture/56.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-66r0bFWb-1642042188882)(…/picture/58.png)]
代码外提的充分条件:(以x:=y+z)为例
(1)所在节点是循环的所有出口节点的支配节点
或者
(5)x在离开循环之后不再是活跃的
(2)循环中其他地方不再有x的定值点
(3)循环中x的所有引用点都是且仅是这个定值能达到的
(4)若y或z是在循环中定值的,只有当这些定值点的语句(一定也是循环不变量)已经在之前被执行过代码外提了。
基本归纳变量
如果循环中对变量I只有唯一的形如 I : = I + C I:=I+C I:=I+C的赋值,且C为循环不变量,则称I为循环中的基本归纳变量。
归纳变量
如果I是循环中的基本归纳变量
J是循环中的定值,总是可以划归为I的同一线性函数,即 J : = C 1 ∗ I + C 2 J:=C_1*I+C_2 J:=C1∗I+C2
其中 C 1 C_1 C1和 C 2 C_2 C2都是循环不变量,则称 J J J是归纳变量,和I同族
往往只在循环中用来计算其他归纳变量以及用来控制循环的进行
可以用与循环控制条件中的基本归纳变量同族的某一个归纳变量来替换它
进行变换后,常伴随可将基本归纳变量的递归定值作为无用赋值而删除
也会带来运算强度的消弱,如将乘法转换为加法,循环内部的强度消弱通常是非常有价值的优化
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-TKRjgjot-1642042188883)(…/picture/59.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-UQqHlafg-1642042188883)(…/picture/60.png)]
若x不活跃,则可以将x删除。
全局优化
中间代码级别,过程内
过程内全局优化是在一个程序过程范围内进行的优化
前面常数传播,常数合并,删除公共子表达式,复写传播,控制流优化,删除无用赋值等都是可用于不同范围的优化方法,也可以用到跨越多个基本块的全局优化中。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-AHPA8wUk-1642042188884)(…/picture/61.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-zuud89BR-1642042188884)(…/picture/62.png)]
量,和I同族
往往只在循环中用来计算其他归纳变量以及用来控制循环的进行
可以用与循环控制条件中的基本归纳变量同族的某一个归纳变量来替换它
进行变换后,常伴随可将基本归纳变量的递归定值作为无用赋值而删除
也会带来运算强度的消弱,如将乘法转换为加法,循环内部的强度消弱通常是非常有价值的优化
[外链图片转存中…(img-TKRjgjot-1642042188883)]
[外链图片转存中…(img-UQqHlafg-1642042188883)]
若x不活跃,则可以将x删除。
全局优化
中间代码级别,过程内
过程内全局优化是在一个程序过程范围内进行的优化
前面常数传播,常数合并,删除公共子表达式,复写传播,控制流优化,删除无用赋值等都是可用于不同范围的优化方法,也可以用到跨越多个基本块的全局优化中。
[外链图片转存中…(img-AHPA8wUk-1642042188884)]
[外链图片转存中…(img-zuud89BR-1642042188884)]