第11章 代码优化
编译原理第十一章解析
26
此处要用到的有向图,是一种其结点带有下述标记或附 加信息的DAG: ① 图的叶结点,即无后继的结点,以一标识符(变 量名)或常数作为标记,表示这个结点代表该变量或常数 的值。如果叶结点用来代表某变量A的地址,则用addr (A)作为这个结点的标记。通常把叶结点上作为标记的 标识符加上下标0,以表示它是该变量的初值。 ② 图的内部结点,即有后继的结点,以一运算符作为 标记,表示这个结点代表应用该运算符对其后继结点所代 表的值进行运算的结果。 ③ 图中各个结点上可能附加一个或多个标识符,表 示这些变量具有该结点所代表的值。
30
下面是仅含0,1,2型四元式的基本块的DAG构造算法。
首先,DAG为空。 对基本块的每一四元式,依次执行: 1. 如果NODE(B)无定义,则构造一标记为B的叶结点并定 义NODE(B)为这个结点; 如果当前四元式是0型,则记NODE(B)的值为n,转4。 如果当前四元式是1型,则转2.(1)。 如果当前四元式是2型,则:(Ⅰ)如果NODE(C)无定义, 则构造一标记为C的叶结点并定义NODE(C)为这个结点,(Ⅱ) 转2.(2)。 2. (1) 如果NODE(B)是标记为常数的叶结点,则转2.(3), 否则转3.(1)。 (2) 如果NODE(B)和NODE(C)都是标记为常数的叶结 点,则转2.(4),否则转3.(2 (3) 执行op B(即合并已知量),令得到的新常数为P。如 果NODE(B)是处理当前四元式时新构造出来的结点,则删除它。 如果NODE(P)无定义,则构造一用P做标记的叶结点n。置NODE (P)=n,转4.。 (4) 执行B op C(即合并已知量),令得到的新常数为P。如 果NODE(B)或NODE(C)是处理当前四元式时新构造出来的结 点,则删除它。如果NODE(P)无定义,则构造一用P做标记的叶 结点n。置NODE(P)=n,转4.。
第11章 代码优化
对于删除公共子表达式和删除无用代码这两种优化技术, 我们在11.1中已经讨论过,这里简单介绍重新命名临时变量 和交换语句次序是什么含义。 重新命名临时变量:假如有语句t∶=b+c,其中t是临时 变量。如果把这个语句改为u∶=b+c,其中u是新的临时变量, 并且把这个t的所有引用改成u,那么基本块的运算结果不变。 交换语句次序: 如果基本块有两个相邻的语句: t1∶=b+c t2∶=x+y 当且仅当x和y都不是t1,b和c都不是t2时,我们可以交换这 两个语句的次序。
B
1
B
2
图 11.5变换循环控制条件, 合并已知量,复写传播
图11.4中,四元式(3) 可变为T1=4,这种变换称为 合并已知量。 图11.4中,四元式(6)把T1 的值复写到T4中,四元式(8) 要引用T4的值,而从四元式 (6)到四元式(8)之间未改 变T4和T1的值,则将四元式 (8)改为T6∶=T5[T1],这种 变换称为复写传播.复写传播之 后运算结果保持不变。 图11.4经过变换循环控制 条件,合并已知量和复写传播 等变换后,变为图11.5。
另外依据优化所涉及的程序范围,又可分为局部优化、 循环优化和全局优化三个不同的级别。局部优化指的是在 只有一个入口、一个出口的基本程序块上进行的优化。循 环优化是对循环中的代码进行的优化。全局优化是在整个 程序范围内进行的优化。
编译程序的优化工作旨在生成较好性能的目标代码, 为此,编译程序需要对代码(中间代码或目标代码)进行 各种方式的变换。变换的宗旨是: 等价-经优化工作变换后的代码运行结果应与原来程序运行 结果一样。 有效-经优化工作变换后的代码应运行时间较短,占用的存 储空间较小。 合算-获得较好性能的目标代码是优化工作的意图,而优化 本身包括大量的代码分析和变换工作,这里有个权衡: 应尽可能以较低的代价取得较好的优化效果。
编译原理课后第十一章答案
对假设(2) B:=3 D:=A+C E:=A*C F:=D+E K:=B*5 L:=K+F
计算机咨询网()陪着您
10
《编译原理》课后习题答案第十一章
第7题 分别对图 11.25 和 11.26 的流图: (1) 求出流图中各结点 n 的必经结点集 D(n)。 (2) 求出流图中的回边。 (3) 求出流图中的循环。
(1) (2) (3) (4) (5) (6) (7) (8) (9) (10) (11) (12) (13)
i:=m-1 j:=n t1:=4*n v:=a[t1] i:=i+1 t2:=4*i t3:=a[t2] if t3< v goto (5) j:=j-1 t5:=4*j t5:=a[t4] if t5> v goto (9) if i >=编译原理》课后习题答案第十一章
第 5 题: 如下程序流图(图 11.24)中,B3 中的 i∶=2 是循环不变量,可以将其提到前置结点吗? 你还能举出一些例子说明循环不变量外移的条件吗?
图 11.24 答案: 不能。因为 B3 不是循环出口 B4 的必经结点。 循环不变量外移的条件外有: (a)(I)s 所在的结点是 L 的所有出口结点的必经结点 (II)A 在 L 中其他地方未再定值 (III)L 中所有 A 的引用点只有 s 中 A 的定值才能到达 (b)A 在离开 L 之后不再是活跃的,并且条件(a)的(II)和(III)成立。所谓 A 在离开 L 后不再是活跃的是指,A 在 L 的任何出口结点的后继结点的入口处不是活跃的(从此点后 不被引用) (3)按步骤(1)所找出的不变运算的顺序,依次把符合(2)的条件(a)或(b)的 不变运算 s 外提到 L 的前置结点中。如果 s 的运算对象(B 或 C)是在 L 中定值的,则只有 当这些定值四元式都已外提到前置结点中时,才可把 s 也外提到前置结点。
代码优化方案
代码优化方案概述:在软件开发过程中,代码的优化是提高程序性能和可维护性的重要步骤。
通过对代码的优化,可以减少程序运行时间、减小内存占用,提高代码的可读性和可维护性。
本文将介绍一些常见的代码优化方案,帮助开发人员编写高效、可靠且易于维护的代码。
1. 使用合适的数据结构选择合适的数据结构对于代码的性能至关重要。
在某些情况下,使用数组比列表更高效;在某些情况下,使用哈希表比二叉搜索树更适合。
了解不同数据结构的特点和适用场景,可以根据具体需求进行选择,从而优化代码执行效率。
2. 避免重复计算重复计算会浪费系统资源和时间,影响代码的性能。
如果某个计算结果在多个地方都会被使用到,可以将结果缓存起来,避免重复计算。
另外,可以通过合理的算法设计和数据结构选择,避免不必要的重复计算,提高代码的执行效率。
3. 减少内存分配和释放频繁的内存分配和释放操作会导致堆内存碎片化,影响程序性能。
可以通过对象池、缓存等方式来减少内存分配和释放操作的次数,改善程序性能。
4. 优化循环和条件语句循环和条件语句是程序中最常用的语句,也是性能影响较大的语句。
可以通过以下几种方式来优化循环和条件语句:- 减少循环迭代次数:尽量减少循环的迭代次数,通过合理的算法设计和条件判断,避免不必要的循环操作。
- 循环展开:对于循环体比较短小的情况,可以将循环展开,减少循环的次数。
- 使用位运算代替乘除操作:位运算的效率通常比乘除运算高,可以考虑使用位运算进行优化。
- 避免使用递归:递归虽然简洁,但是在某些情况下会导致栈溢出,可以考虑使用循环代替递归,提高代码的性能。
5. 并发优化对于多线程或分布式系统,可以通过合理的并发优化来提高代码的性能。
可以使用线程池、任务队列等方式来提高并发效率,减少线程切换和资源争用。
6. 使用性能分析工具使用性能分析工具可以帮助开发人员找出程序中的性能瓶颈,了解代码的具体问题,并提供相应的优化建议。
常见的性能分析工具有Profiling、Benchmark等,可以通过这些工具来进行代码的性能分析和优化。
第11章 代码优化 new
数等价的集合:如x:=x+0 x:=x*1 x:=y**2(变换成x:=y*y)
例如四元式程序: t1 := 4 – 2 t2 := t1 /2 t3 := a * t2 化简为: t1 := a + a t1 := b + t1 c := t1 * t1
t4 := t3 * t1
t5 := b + t4 c := t5 * t5
这种变换把循环不变运算,提到循环前面。上例中,可
以把(4)和(7)提到循环外,经过删除多余运算和代码外提后, 代码变换成图11.3:
9
3.强度削弱:
把强度大的运算换算成强度小的运算,例如把乘法运 算换成加法运算。在图11.3中,可以把循环中计算T1值的
乘法运算变换成在循环前进行一次乘法运算,而在循环中
因此,图11.4中,四元式(3)可变为T1=4,
12
5.合并已知量不复写传播:
复写传播 复写传播是指尽量不引用那些 在程序中仅仅只传递信息而不改 变其值,也不影响其运行结果的 变量。
图中,T4 就是这样一种变量。四元式 (6)T4=T1,下一个四元式 (8)T6=T5[T4],这之间T4的值没有 变化,因此将(8) 变换为 : T6=T5[T1] 。
21
例 给以下四元式序列划分基本块。
(1)read C 四元式序列的第一个语句 (2)A=0 (3)B=1 由条件转移语句或无条件转移语句 (4)L1 : A=A+b 转移到的语句 (5)if B≥C goto L2 紧跟在条件转移语句后面的语句 (6)B=B+1 (7)goto L1 由条件转移语句或无条件转移语句 (8)L2 : write A 转移到的语句 (9)halt
27
第11章代码优化
(29) t15 := 4n
/* 程序段结束*/
(15) x := a[t6]
(30) a[t15] := x
quicksort(m, j);
quicksort(i+1, n); }
图11.4 快速排序部分程序的三地址代码
图11.3 快速排序的C代码
8
11.2 代码优化的基本技术 i := m -1 j := n
goto B2
t11 := 4i
B6
x := a[t11]
t12 := 4i
t13 := 4n
t14 := a[t12]
a[t12] := t14
t15 := 4n
a[t15] := x
9
11.2 代码优化的基本技术
例如,在图11.5的B5中分别把公
共子表达式4*i和4*j的值赋给t7和 这是仅限于基本块的局部优化,
主要内容
❖ 优化的概念 ❖ 代码优化的基本技术 ❖ 局部优化 ❖ 机器代码优化-窥孔技术
1
11.1 代码优化的概念
代码优化在整个编译过程的位置 ❖ 源程序 编译前端 中间代码 中间代码生成 中间代码 目标代码生成 目标程序
中间代码
中间代码优化
目标代码优化
程序员和编译器可能改上程序的位置
源程序
编译前端
(3)经济原则:代码优化需要占用计算机和编译 程序的资源,代码优化取得的效果应该超出优化 工作所付出的代价。否则,代码优化就失去了意 义。
3
11.1 代码优化的概念
❖ 代码优化依据机器相关性、优化范围和优化语言级别的 分类
按照与机器相关的程度,可以分为与机器相关的代码优化和与 机器无关的代码优化。 ❖ 与机器相关的优化一般有寄存器的优化、多处理器的优化、特 殊指令的优化以及无用指令的消除等技术。显然, 这几类优 化与具体机器的特性密切相关,例如寄存器的总数,寄存器的 具体使用规定,等等。这类优化通常的在目标代码生成之后进 行。 ❖ 与机器无关的优化是在目标代码生成以前进行,主要是根据程 序的控制信息和数据信息,对程序进行优化,与机器无关。
编译第11章
6.删除无用赋值
(1)P:=0 (2)I:=1 (4)T2:=addr(A)-4 (7)T5:=addr(B)-4 (3)T1:=4 (5)T3:=T2[T1] (6)T4:=T1 (8)T6:=T5[T1] (9)T7:=T3*T6 (10)P:=P+T7 (3’)T1:=T1+4 (12)if T1<=80 goto(5) (1)P:=0 (4)T2:=addr(A)-4 (7)T5:=addr(B)-4 (3)T1:=4
3.强度削弱
•基本思想:把强度大的运算换算成强度小的。 例如: a) i*2 = 2*i = i+i = i<<1 b) i/2 = (int)(i*0.5) c) 0-1 = -1 d) f*2 = 2.0 * f = f + f e) f/2.0 = f*0.5
(1)P:=0
(2)I:=1 (4)T2:=addr(A)-4 (7)T5:=addr(B)-4 (3)T1:=4*I (5)T3:=T2[T1] (6)T4:=T1 (8)T6:=T5[T4] (9)T7:=T3*T6 (10)P:=P+T7 (11)I:=I+1 (12)if I<=20 goto(3)
五、常用优化技术简介
1.删除多余运算 2.循环不变代码外提 3.强度削弱 4.变换循环控制条件 5.合并已知量与复写传播 6.删除无用赋值
1.删除多余运算(删除公共子表达式):
目的:提高目标代码速度。 例如: P:=0 for I:=1 to 20 do P:=P+A[I]*B[I]
(1)P:=0 (2)I:=1
n1
D
n2
C
n3
B
4、DAG结点的数据表示法
第11章 代码优化
X+1 X:=X+1 Y-1 Y:=Y-1 A<B
Z:=0
相应四元式代码的分块情况如下:
B1: (=:, 1 ,—, Y ) B2: (label,—,—,100) (>, A, B, T1) (then,T1,—,—) B3: (=:, 0, —, X ) (else,—,—, —) B4: (=:, 0 , —,Y ) Y:=1
C*B D+C*B D:=D+C*B
被省略
D+C*B A:=D+C*B
被省略
D+C*B C:=D+C*B
其中四元式2,5,8的运算符、OPR1、OPR2 均相等 但5、8中的D与2中的不同所以不可以 用T2代替T4、T6。 四元式5和8相同因此四元式8对5是多 余的。
经优化后可得到下列四元式代码: 1.(*, C, B,T1) 2.(+, D, T1,T2) 3.(=:,T2,—,D) 4.( )被省略 5.(+, D, T1,T3) 6.(=, T3,—,A) 7.( )被省略 8.( )被省略 9.( =:,T3,—,C)
其中四元式1,4,7的运算符、OPR1、 OPR2均相等,所以可以用T1代替T3、T5。 1.(*,C, B, 2.(+, D, T1, 3.(=:,T2,—, 4.( 5.(+, D, T1, 6.(=:,T4,—, 7.( 8.(+, D, T1, 9.(=: T6,—, T1) T2) D ) ) T4) A ) ) T6) C )
该四元式是否为赋值语句)
4. 如果QT[i].OPRj(j=1,2)都是常数,则计算结 果,填VVL表,并删除四元式QT[i]: ENTRY(QT[i].RESU, cons) (填写表中值) QT[i]:=($, -, -, -) (四元式节省)
第十一章 代码优化
read X read Y R:=X mod Y if R=0 goto (8) X:=Y Y:=R goto (3) write Y halt
1.求出四元式程序中各个基本块的入口 语句: 1) 程序第一个语句,或 2) 能由条件转移语句或无条件转移语 句转移到的语句,或 3) 紧跟在条件转移语句后面的语句。
(1)P:=0 (2)I:=1 (4)T2:=addr(A)-4 (7)T5:=addr(B)-4
代码 外提 (3)T1:=4*I (5)T3:=T2[T1] (6)T4:=T1 (8)T6:=T5[T4] (9)T7:=T3*T6 (10)P:=P+T7 (11)I:=I+1 (12)if I<=20 goto(3)
删除多 余运算
(1)P:=0 (2)I:=1 (3)T1:=4*I (4)T2:=addr(A)-4 (5)T3:=T2[T1] (6)T4:=T1 (7)T5:=addr(B)-4 (8)T6:=T5[T4] (9)T7:=T3*T6 (10)P:=P+T7 (11)I:=I+1 (12)if I<=20 goto(3)
11.2 局部优化
• 局限于基本块范围内的优化称为基本块内的优化,或 称局部优化。
• 在一个基本块内通常可以实行下面的优化: – 删除公共子表达式 – 删除无用赋值 –合并已知量 – 临时变量改名 – 交换语句的位置 – 代数变换(pow(y,2) 等价于 y*y)
11.2.1 基本块的划分
• 划分四元式程序为基本块的算法: 1.求出四元式程序中各个基本块的入口语句 入口语句: 1) 程序第一个语句,或 2) 能由条件转移语句或无条件转移语句转移到的语 句,或 3) 紧跟在条件转移语句后面的语句。
第十一章代码优化
只计算一次,删除了多余运算
利用DAG进行优化
删除在基本块后不被引用变量的赋值
假如T0,T1,…,T6在基本块后 都不被引用,则这些符号可在
DAG附加标识符中删去,重写四
n6
元式得到进一步的优化:
*
(1) S1:=R+r
(2) A:=6.28*S1
n8 B *
A, T5
n T0
31.14
8* n A, B,T5
6*
T2,T4
n
n T6
5+
-7
n T1,T3 n
n
62.28
3R
4r
➢ni是结点编号
➢结点下面的符号(运算符、标识符或常量) 是各结点的标记
➢结点右边的标识符是结点的附加标识符
2. 四元式及其相应的DAG结点形式
0型: A:=B (:=, B, -,A) n1 A
(6) T3:=2*T0 (7) T4:=R+r (8) T5:=T3*T4 (9) T6:=R-r (10) B:=T5*T6
n1 T0 3.14
2
n8 B
*
n6 A ,B ,T5
*
n5 T2 ,T4 n7 T6
+
-
n2 T1 ,T3 n3
n4
6.28
R
r
2
11.2.3 DAG的应用
在一个基本块被构造成相应的DAG的过程中, 进行了如下基本的优化工作: 1) 合并已知量 在DAG构造算法中,如果运算量都是已知量, 则不生成计算该结点值的内部结点,而执行 该运算,将计算结果生成一个叶结点,实现 了合并已知量优化
代码优化的概念
代码优化的概念代码优化是指通过对程序进行调整和重构来提高代码的效率和性能,以达到更好的运行效果和用户体验。
优化的目标是在不改变程序功能的前提下,尽可能减少时间和空间的占用,使程序更加高效、可维护和可扩展。
优秀的代码优化不仅能提高代码的执行速度、响应速度和稳定性,还能改善代码的可读性和可维护性,有利于程序完整性的保持和可靠性的提高。
代码优化的重要性代码优化是开发过程中必不可少的环节,因为优化后的代码相较于低效的代码,具备以下优势:1. 提高程序运行效率:绝大多数程序的瓶颈在于运行时间和机器资源的占用,而代码优化可以通过降低算法复杂度、减少不必要的循环和分支、避免重复计算等方式,使程序运行速度更快,更加节省计算机资源占用。
2. 改善用户体验:快速响应和流畅的界面对于用户来说是至关重要的,而优化的代码可以使程序更加快速响应,避免界面卡顿和延迟,从而提高用户的使用体验。
3. 提高程序稳定性:优化后的代码往往更加清晰、逻辑更加严谨、异常处理更加完善,从而可以减少程序崩溃和错误的发生,提高程序的健壮性和可靠性。
4. 提高代码可维护性:优化后的代码结构更加清晰,名称规范可读性更高,抽象层次更为明确,从而可以减少后期编码的时间和困难,增强代码的可维护性和可扩展性。
代码优化的方法和技巧对代码进行优化通常需要考虑多种方式和角度,下面列举几种常用的优化方法和技巧:1. 使用高效的算法和数据结构:优化程序的算法和数据结构是优化代码的基础,它可以显著提高程序性能。
对于复杂程序来说,使用适当的数据结构(如散列表、二叉树、图等)可以有效减少程序的运行时间。
此外,应该尝试使用高效的排序算法和查找算法(如归并排序、快速排序、二分查找等)来进一步提高程序的效率。
2. 避免重复计算和内存浪费:在编写代码时应该避免进行重复计算,避免浪费计算资源占用。
同样,应该尽可能避免内存浪费(如重复创建列表对象、使用过多的全局变量等),这不仅会浪费更多的内存,而且会导致程序变慢。
7hh第11章 代码优化
注意: ① 转移语句结点的右边可附加一语句 位置, 用于指示转移目标, 其余结点 的右边只允许附加标识符;
② 数组元素赋值的结点有三个后继 , 其余结点最多只有两个后继。
利用DAG进行优化的基本思想: 首先按基本块内四元式序列的顺 序把所有四元式构造成一个 DAG, 然后按构造结点的次序将 DAG 还 原成四元式序列。 由于在构造 DAG 的同时作了局部 优化{合并已知量、删除公共子表 达式、删除无用赋值}, 因此, 得到 的是优化的四元式序列。
DAG构造算法的基本步骤: (1) 构造叶结点 (2) 合并已知量 (3) 构造op结点 {删除公共子表达式} (4) 添加附加信息 {删除无用赋值}
基本块的DAG构造算法: { 仅含0,1,2型四元式: A=B, A= op B, A=B op C } (1) 构造叶结点: 若 Node(B) 无定义 , 则构造一标记 为B的叶结点, 并做下述处理: ①若当前四元式是0型, 转(4); ②若当前四元式是1型, 转(2)①; ③若当前四元式是2型, 则 若Node(C)无定义, 则构造一标 记为C的叶结点; 转(2)②;
两者对比:
(1) T0 = 3.14 (2) T1 = 2*T0 (3) T2 = R+r (4) A = T1*T2 (5) B = A (6) T3 = 2*T0 (7) T4 = R+r (8) T5 = T3*T4 (9) T6 = R-r (10) B = T5*T6
(1) T0=3.14 (2) T1=6.28 (3) T3=T1 (4) T2=R+r (5) T4=T2 (6)A=6.28*T2 (7) T5=A (8) T6= R−r (9) B=A*T6
11.1.2 基本块的DAG表示
代码优化
主要内容
优化的概念 代码优化的基本技术 局部优化 机器代码优化-窥孔技术
2
11.1 代码优化的概念
代码优化在整个编译过程的位置
源程序 编译前端 中间代码 中间代码生成 中间代码 目标代码生成 目标程序
中间代码
中间代码优化
目标代码优化
程序员和编译器可能改上程序的位置
同样,可以对归纳变量i和t2进行强度消弱。
17
11.2 代码优化的基本技术
对t4 := j*4 和t2 := i*4完成强度消弱以后, 变量i和j除了在B4中语句if i>= j goto B6
之外,不再被引用。因此,可以删除这 些归纳变量i和j,把这个语句变换为if t2 >= t4 goto B6。 经过强度消弱和删除归纳变量,图11.7以及 图11.5最后就变换为图11.8。
11
11.2 代码优化的基本技术
删除公共子表达式后的B5和B6 分别是
B5
B6
t6 := t2 x := a[t6] t7 := t6 t8 := t4 t9 := a[t8] a[t7] := t9 t10 := t8 a[t10] := x goto B2
t11 := t2 x := a[t11] t12 := t11 t13 := t1 t14 := a[t12] a[t12] := t14 t15 := t1 a[t15] := x
这种变换称为代码外提。在给出的快速排序程序中, 没有可以代码外提之处。
16
11.2 代码优化的基本技术
强度消弱和删除归纳变量
考察图11.7的循环B3。j和t4的值保持着线性关系 t4 := j*4,每循环一次,j的值每减1,t4的值就减 去4。这种变量称为归纳变量。
代码优化
代码优化所谓优化是对代码进行等价变换,使得变换后的代码的效率更高(节省运行时间、存储空间)。
代码优化包含很多,下面我从几方面介绍一下。
1.选择合适的算法和数据结构选择一种合适的数据结构很重要,如果在一堆随机存放的数中使用了大量的插入和删除指令,那使用链表要快得多。
下面看一个例子。
例:static void Reverse(int[] array, int begin, int end){...}Reverse方法的作用是将array数组中,从begin下标到end下标之间的元素反序一下。
解决方法有很多:比如说开一个新数组(长度为end - begin + 1),将begin 到end之间的元素放到新数组中去,然后反序,然后再复制回来。
或者是用栈:把begin到end之间的元素给push到栈中,再一个一个pop出来依次赋值给begin到end,这样就反序了……唔!数据结构学的不错!最好的做法是:public static void Reverse(int[] array, int begin, int end){while (end > begin){int temp = array[begin];array[begin] = array[end];array[end] = temp;begin++;end--;}}end和begin两个下标从初始值开始依次向中间逼近,每次都交换一下数组中的元素。
最终,while在判断的时候会发现end == begin(begin和end之间总共奇数个元素),或end < begin(begin和end之间总共偶数个元素)。
无论哪种情况,都表示反序已经完成。
是不是很简单当然,判断传入非法参数需要抛出异常等等在这就不提了。
2.减少运算强度常见方式有:尽量使用增量和减量操作符、复合赋值表达式(如a-=1及a+=1)、C++中的移位操作(a=a*4改为a=a<<2)等等。
代码生成和代码优化
代码生成和代码优化代码生成是指利用其中一种工具或技术自动地生成源代码或二进制代码的过程。
代码优化则是对已有的代码进行分析和改进,以提高其性能和效率。
代码生成有多种方式,最常见的是编译器生成机器码的过程。
编译器通过将源代码转换为中间代码,然后再将中间代码转换为机器码来实现代码生成。
在这个过程中,编译器会根据源代码的语义和目标平台的特性,自动生成相应的机器码。
代码生成也可以通过模板引擎技术实现,通过使用预定义的模板和变量,生成特定的源代码或文本。
代码生成的好处之一是提高开发效率。
通过自动生成的代码,开发者可以避免编写重复的、模板化的代码,减少了开发时间和工作量。
代码生成还可以在一定程度上减少人为错误,提高代码的质量和可维护性。
代码生成还可以用于实现领域特定语言(Domain-specific language, DSL)。
DSL是一种专门用于解决一些特定领域问题的语言。
通过使用代码生成技术,可以将DSL的抽象语法树转换为可执行的代码,从而实现领域内问题的自动化解决方案。
代码生成的挑战之一是生成的代码质量的可控性。
自动生成的代码可能不符合编码规范和最佳实践,也可能出现逻辑错误。
为了解决这个问题,可以通过在代码生成过程中引入模板和规则,来指导生成的代码的生成和质量控制。
代码优化是对已有代码进行改进,以提高其性能和效率。
在软件开发中,优化是非常重要的一环,它可以减少资源消耗,提高系统响应速度和用户体验。
代码优化可以从多个层面进行,如算法优化、数据结构优化、编译优化等。
算法和数据结构优化是最基本的优化方法,通过改进算法和数据结构的设计,可以提高程序的执行效率。
编译优化是指通过对已有的源代码进行分析和变换,优化生成的机器码的执行效率。
代码优化的过程中需要注意的是平衡优化的成本和收益。
优化过程可能会导致代码增加复杂性和可读性下降,增加了维护的难度。
因此,在进行代码优化时,需要综合考虑优化的效果和代价,选择合适的优化策略。
代码优化概述
代码优化概述1.1.代码优化简介代码优化是指对程序进行各种等价变换,使得从变换后的程序出发,能生成更高效的目标代码。
目标代码的质量,通常有两个衡量的标准:空间效率和时间效率。
有时空间优化也会导致时间优化(如减少指令条数),但通常它们是一对矛盾,不能兼顾。
代码优化的目的是产生更高效的代码,使程序以更快的速度、占用更少的空间运行。
对于编译器,代码优化分为三个阶段:图1-1 代码优化流程图为了获得更优化的程序,可以从各个环节着手。
首先,在源代码这一级,程序员可以通过选择适当的算法和安排适当的实现语句来提高程序的效率。
其次,再设计语义动作时,要尽可能产生高效的中间代码,同时还可以安排专门的编译优化阶段对中间代码进行各种等价变换,改进代码的效率。
最后,在目标代码这一级上,应该考虑如何有效地利用寄存器,如何选择指令,以及进行窥孔优化等。
对于编译优化,最主要的时机是在语法、语义分析生成中间代码之后,在中间代码上进行。
这一类优化不依赖于具体的计算机,而取决于语言的结构。
另一类优化则是在生成目标程序时进行的,它在很大程度上与具体的计算机有关。
由优化编译程序提供的对代码的各种变换必须遵循如下原则[1]:1)等价:经过优化后不改变程序运行的结果;2)有效:优化后产生的目标代码运行时间较短,占用的存储空间较小;3)合算:应尽可能以较低的代价取得较好的优化效果。
如果为实现一种优化变换所花时间和精力,以及编译器编译源程序时的额外开销,不能从目标程序的运行中得到补偿,那么是没有意义的。
在设计一个编译程序时,究竟应考虑哪些优化项目以及各种优化项目进行到何种程度,应权衡利弊,根据具体情况而定。
其中,控制流分析主要目的是分析出程序的循环结构.循环结构中的代码的效率是整个程序的效率的关键。
数据流分析进行数据流信息的收集,主要是变量的值的定义和使用情况的数据流信息.包括到达-定值分析;可用表达式;活跃变量。
最后,根据上面的分析,对中间代码进行等价变换。
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
(1)Read X (2)Read Y (3) T1:=X mod Y (4) If T1<>0 goto (6)
(5)goto (10) (6)T:=X mod Y (7)X:=Y (8)Y:=T (9)goto (3) (10)write Y (11)halt
3、块内优化
主要是进行已知量合并、删除公共子表达式、 删除无用赋值。
第十一章
代码优化
• 11.1 优化技术简介 • 11.2 局部优化 • 11.3 控制流分析和循环优化
11.1 优化技术简介
一、优化的原则 1、等价原则 优化后不改变原程序运行的功能。 2、有效原则 优化后产生的目标代码运行时间较短,占用空 间较小。
二、优化阶段
源代码 中间代码 目标代码
中间代码优化 目标代码优化 •与机器无关的优化---中间代码优化。 •依赖于机器的优化---目标代码优化。
(1)P:=0 (2)I:=0 (4)T2:=addr(A) (7)T5:=addr(B)
(3)T1:=0
(5)T3:=T2[T1] (6)T4:=T1
(8)T6:=T5[T1]
(9)T7:=T3*T6 (10)P:=P+T7 (3’)T1:=T1+4 (12)if T1<=80 goto(5)
6.删除无用赋值
注:无用赋值有以下情形:
(a)对某变量A赋值后,在程序中没有引用; (b)对某变量A赋值后,在引用前又重新赋值; (c)对某变量A进行自增赋值,且它仅仅被用在自 增运算中。
对上面第一和第三种情况,应进行全局分析。
二、基本块的DAG表示
1、DAG(有向无环图)定义
a)父子结点
若在一有向图中,结点ni有弧指向nj,则称ni是nj 的父结点,nj是ni的子结点。
(1)Read X (2)Read Y (3) T1:=X mod Y (4) If T1<>0 goto (6) (5)goto (10) (6)T:=X mod Y (7)X:=Y (8)Y:=T (9)goto (3) (10)write Y (11)halt
基本块的划分:
(1)Read X (2)Read Y (3) T1:=X mod Y (4) If T1<>0 goto (6) (5)goto (10) (6)T:=X mod Y (7)X:=Y (8)Y:=T (9)goto (3) (10)write Y (11)halt
C)删除不会被执行的语句
凡是没有纳入到任何一个基本块中的语句, 都是程序控制流程所无法到达的语句,即不会 被执行到的语句,可将其删除。
例:求最大公因子算法 begin read X; read Y; while (X mod Y<>0) do begin T:=X mod Y; X:=Y; Y:=T end; write Y end
a)初始化
初始DAG为空,无任何结点。 b)对基本块内每个四元式(op,A,B,C)进行如下操作: 1)如果node(A)无定义,则建立叶结点,标记为A,让 node(A)等于这个结点。如果对于2型四元式node(B)没有定义 ,则建立叶结点,这时标记为B,且让node(B)等于新建结点, 如果node(A)和node(B)都是已知常量,则执行 A op B(合并 常量),得到新常量P,为P建立一个叶结点,标记为P,如果 node(A)或node(B)是建立当前四元式的新结点,则删除它。
(3)T1:=4*I (5)T3:=T2[T1] (6)T4:=T1 (8)T6:=T5[T4] (9)T7:=T3*T6 (10)P:=P+T7 (11)I:=I+1 (12)if I<=20 goto(3)
4、变换循环控制条件
• 经过变换后,有些变量不(4)T2:=addr(A) (7)T5:=addr(B) (3)T1:=4*I (5)T3:=T2[T1] (6)T4:=T1 (8)T6:=T5[T4] (9)T7:=T3*T6 (10)P:=P+T7 (11)I:=I+1 (3’)T1:=T1+4 (12)if I<=20 goto(5)
5、合并已知量与复写传播
• 编码时的已知量—常数,可在编译时计算 出它的值,这种变换称为合并已知量或常 数合并。 • 通过复制后没有再改变的值可以互相替换, 不会改变程序的结果,这种变换称为复写 传播。
(1)P:=0 (2)I:=0 (4)T2:=addr(A) (7)T5:=addr(B) (3) T1:=4*I (5)T3:=T2[T1] (6)T4:=T1 (8)T6:=T5[T4] (9)T7:=T3*T6 (10)P:=P+T7 (3’)T1:=T1+4 (12)if T1<=80 goto(5)
A:=B[C],即四元式(=[],B[C],_,A),其DAG 结点为2型; 变址取数 if B rop C goto (s)即四元式(jrop,B,C,(s))
其DAG结点为2型 n3 A op n1 B n2 C n1 B n3 A =[] n2 C n1 B n3 (s) rop n2 C
D)3型
(1)P:=0 (2)I:=1 (4)T2:=addr(A) (7)T5:=addr(B) (3)T1:=4*I
(5)T3:=T2[T1] (6)T4:=T1 (8)T6:=T5[T4 ] (9)T7:=T3*T6 (10)P:=P+T7 (3’)T1:=T1+4 (12)if T1 <=80 goto(5)
(1)P:=0 (2)I:=0 (3)T1:=4*I (4)T2:=addr(A) (5)T3:=T2[T1] (6)T4:=T1 (7)T5:=addr(B) (8)T6:=T5[T4] (9)T7:=T3*T6 (10)P:=P+T7 (11)I:=I+1 (12)if I<=20 goto(3)
(5)T3:=T2[T1] (8)T6:=T5[T1] (9)T7:=T3*T6 (10)P:=P+T7 (3’)T1:=T1+4 (12)if T1<=80 goto(5)
11.2局部优化
一、基本块 1、定义 程序中一个只有一个入口和一个出口的一段顺 序执行的语句序列,称为程序的一个基本块。 注:1)一个给定的程序,可以划分为一系列的基本 块。优化在各基本块中分别进行(局部优化)。 2)在运行基本块时,只能从其入口进入,从出 口退出。
例如:A:=B op C
即四元式(op,B,C,A)的DAG图为:
n3 A
op
n1 n2
B
C
注:1)图中的有向弧都省去箭头。 2)结点ni是构造DAG过程中给予各结点的编号; 3)各结点下面的符号(运算符、变量、常数) 是各结点的标记; 4)各结点右边的标识符是结点的附标。
3、四元式的DAG结点类型
根据其对应结点的子结点个数,可分为0型、1 型、2型和3型四种类型。 A)0型 A:=B,即四元式(:=,B,_,A),其DAG结点为0型:
n1
B
A
B)1型
A:=op B,即四元式(op,B,_,A),其DAG结点为1型: n2 A
op
n1 B
C)2型 A:=B op C,即四元式(op,B,C,A),其DAG结 点为2型;
3.强度削弱
•基本思想:把强度大的运算换算成强度小的。 例如: a) i*2 = 2*i = i+i b) 0-1 = -1 c) f/2.0 = f*0.5
(1)P:=0 (2)I:=0 (4)T2:=addr(A) (7)T5:=addr(B)
(1)P:=0 (2)I:=0 (4)T2:=addr(A) (7)T5:=addr(B) (3)T1:=4*I (5)T3:=T2[T1] (6)T4:=T1 (8)T6:=T5[T4] (9)T7:=T3*T6 (10)P:=P+T7 (11)I:=I+1 (3‘)T1:=T1+4 (12)if I<=20 goto(5)
(1)P:=0 (2)I:=0 (4)T2:=addr(A) (7)T5:=addr(B) (3)T1:=0 (1)P:=0 (4)T2:=addr(A) (7)T5:=addr(B) (3)T1:=0
(5)T3:=T2[T1] (6)T4:=T1 (8)T6:=T5[T1] (9)T7:=T3*T6 (10)P:=P+T7 (3’)T1:=T1+4 (12)if T1<=80 goto(5)
(3)T1:=4*I (4)T2:=addr(A) (5)T3:=T2[T1] (6)T4:=T1 (7)T5:=addr(B) (8)T6:=T5[T4] (9)T7:=T3*T6 (10)P:=P+T7 (11)I:=I+1 (12)if I<=20 goto(3)
2、代码外提
目的:减少循环中代码总数。 方法:把循环不变运算,即其结果独立于循环 执行次数的表达式提到循环的前面,使之只 在循环外计算一次。
b)路径与环路
若在一有向图中,结点n1 n2 …… nk间存在有向 弧n1n2 …… nk,则称n1 到nk之间存在一条通路 ,若n1=nk则该路径称为环路; c)有向无环图
若有向图中任一路径都不是环路,则称该图为无 环路有向图。
2、DAG结点标记或附加信息
a)叶结点 没有子结点的结点称为叶结点,以一标识符(变量名)或常 数作为标记。即该结点代表该变量或常数的值。 注:1)若叶结点代表变量a的地址,则标记为addr(a) 2)通常把叶结点上作为标记的标识符加上下标0,以表示 它是该变量的初值。 b)内部结点 以一运算符作为标记。即该结点代表应用该运算符对其后 继结点所代表的值进行运算的结果。 c)各结点上可能附加一个或多个标识符,表示这些标识符具有 该结点所代表的值,简称附标。