1、编译技术被分为“与机器无关”和“与机器相关”两类。“与机器无关”,使用这些技术时可以不考虑将执行代码的计算机的特性;而“与机器有关”,指这些技术依赖于许多机器的低级细节。
2、最小二乘法拟合
3、优化之一:消除循环的低效率
将循环中需要每次计算,但是计算结果不会改变的语句移出去,称为代码移动(code motion)。
4、减少不必要的过程调用。如可以确保边界安全的情况下,就不需要每次都进行边界安全检查。
5、消除不必要的存储器引用
示例代码
void Test1(int *pToInt) { *pToInt = 0; for (int i = 0; i < 10; i++) { *pToInt += i; } } //上述写法的机器效率,在数据量大的时候,相对下述写法 //低很多,因为*pToInt涉及到存储器的引用。 void Test2(int *pToInt2) { int iTemp = 0; for (int i = 0; i < 10; i++) { iTemp += i; } *pToInt = iTemp; }
引入临时变量来保存中间结果。只有在最后的值计算出来时,才将结果存放到数组或全局变量中。而通过优化,编译器用寄存器eax(通常)来存储中间变量的结果。(通过察看汇编代码来看得到)
6、现代处理器结构
超标量(superscalar):可以在每个时钟周期执行多个操作,而且是乱序的(out of order)。乱序,即指令执行的顺序不一定要与它们在汇编程序中的顺序一致。
整个设计有两个主要部分:ICU(Instruction Control Unit,指令控制单元)和EU(Execution Unit,执行单元)。前者负责从存储器中读出指令序列,并根据这些指令序列生成一组针对程序数据的基本操作;后者执行这些操作。
退役单元(retirement unit)记录正在进行的处理,并确保它遵守机器级程序的顺序语义。
7、大多数单元能够每个时钟周期开始一个新的操作。仅有的例外是浮点乘法器和两个除法器。除法器没有流水线化。
Latency(执行时间) represents the total number of cycles for a single operation.
Issue time(发射时间) denotes the number of cycles between successive(连续的),
independent(独立的) operations. (Obtained from Intel literature).
8、降低循环开销
我们可以通过在每次迭代中执行更多的数据操作来减小循开销的影响,使用的是称为循环展开(loop unrolling)的技术。其思想是在一次迭代中访问数组元素并做乘法。
9、在一个IA32处理器上,所有的浮点操作都是以扩展的80位精度执行的,而浮点寄存器也是按照这个格式存储的。只有当寄存器的值写入存储器时,才把它转换成32位(浮点数)或64位(双精度格式)。
10、性能提高
1)为问题选择适当的数据结构和算法。
2)编码中:
(1)前面列出的几种可能引起低性能的地方都要注意。
11、程序剖析
程序剖析(profiling)包括运行程序的这样一个版本,其中插入了工具代码,以确定程序的各个部分需要多少时间。
Unix系统提供了一个剖析程序GPROF。更详尽的介绍,可以GOOGLE,或参见本书第5章。
12、Amdahl定律
其主要思想是:当我们回快系统一个部分的速度时,对系统整体性能的影响依赖于这个部分有多重要(占全体的百分比)和速度提高了多少(相比原来,提高了几倍)。
<Computer Systems:A Programmer's Perspective>