在前面的绪论中简单介绍过优化,先解释下优化有什么必要性。在前面一部分中间代码生成中,会生成大量的中间变量,缺点是占据大量的存储空间和计算资源。并且编写代码的人有可能在编写代码的过程中不合理的安排结构。在为了在更短的时间内完成编译,需要优化技术的参与。
本文对于优化技术的介绍主要围绕例子来进行,并未每个例子附带文字说明。
在介绍优化方法之前,先介绍和优化相关的一些概念:
优化的分类:
优化的三个原则:
优化常用的技术名称:
下面先介绍一些简单的优化技术:
1.常数合并:主要是针对于表达式中有纯粹的操作数计算,则可将其部分表达式结果直接计算出来。
2.常数传播:主要针对表达式中无用的过渡赋值,越过中间变量,直接赋值
3.代数简化:指调整计算顺序,将常数合并
4.降低运算强度:指使用计算机计算较快的方式代替较慢的,比如用移位操作代替乘法操作,乘法代替除法等:
5.复写传播:指部分中间变量虽然在后面有在使用,但可用初始的变量进行代替,进而达到减少中间变量个数的目的。
以上介绍的五种方法还是比较简单的,而且也没有什么系统性。完全可以在编写代码的人注意下避免,所以一个好的程序员写代码的时候已经完成一次优化啦~
下面介绍两种比较系统的方法:局部优化和循环优化
局部优化
顾名思义,这种优化方式区分了局部。局部的体现就是基本块的划分。那什么是基本块呢?它又是如何进行划分的呢?基本块的划分规则如下:
文字看的眼花缭乱,还是例子深得人心:
基本块划分完之后,也需要一种表达方式,这里选择了一种**流图(控制流程图)**方式:简单来说就是,用一个个矩形块代表基本块。在相互有关联的基本块中使用箭头连接。基本块间连接可构成循环,但一定要有一个出口!举个例子:
一般引出箭头有两种可能性,if和goto。If会有两种可能,会引出两个箭头。Goto则只有一个。
通常情况下,局部优化都是使用基本块DAG图来表示,更方便快捷。那什么是基本块DAG图呢?
这里面的DAG和有向无环图还有点儿不一样,就是通过树状节点之间的关联方式来表示各种四元式,来消除其中一些无关变量达到优化的目的。那如何构造DAG图呢?
哈哈哈,看完了这个是不是日常懵~我们还是通过一个例子来详细说明下:
详解如下:建立节点T0,建立节点T1(常数表达式,直接计算)。建立节点T2,连接T1和T2建立节点A(注意T1在左,T2在右)。语句5,直接变量赋值,在A点旁边写上B。T3相当于常数同T1,在T1旁边写入。语句7,同语句3,在T2旁边标注T4。语句8,T3,T4同等于T1,T2已有乘法,在A旁写入T5。建立T6节点。重新建立B节点,将原来B节点抹去(被替代)。
循环优化
循环优化是十分有必要的,循环体会多次执行。如果循环体内出现无用代码会极大的浪费资源。循环优化主要分为三种:代码外提,强度削弱,删除归纳变量。这三种方法也是首先要进行基本块的划分的。
代码外提:指将对循环无影响的语句提出,放入初始化代码块中(指执行一次)。比如变量的初始化等。但切记代码外提的要求:绝对不能对后续代码产生任何影响
举几个反例说明下:
从以上三个都不能提的反例中可以看到,在代码外提之前一定要注意!主要判断情况是否满足。
强度削弱
强度削弱指将循环中执行长的运算等价的替换成执行时间较短的运算。比较常见的是将乘法变成加法,在循环里面有所体现:
举个例子:
在第一个图(原式)中可以看到,I每次增加1,T2,T6在循环体里每次循环乘以10。相当于每次循环加10,使用强度削弱:在B2中为T2,T6初始赋值,并将乘法修改为加法。重点是一定要放在I自增语句之后。同理T3和T7由于T1和T5的确定,同样随着T2和T6在增长,也可以进行移动。
删除归纳变量
其算法框架为:
拿上面的例子继续做:
经过判断,I是基本归纳变量,T3具有上限值100+T1(同等与I等于10的情况)。本题中T2和T6的删除是认为其在之后的语句中没有了作用。如果后面还有语句则要根据情况进行判断。
优化是编译原理课程的最后一章啦~教材上没有介绍目标代码的生成,我也没学。九章课程转瞬即逝,课程也早已结课,这学期也快要结束了,多灾多难的2020啊,快要完了。2021,来了!