前言:
最近事情太多了,十一月到现在只写了一篇博客,得赶紧把最近看的《CSAPP》的内容写一写,不然持之以恒的标志要没了。这里跳过了第四章,因为讲解处理器的技术,暂时对我用处不大,以后有需要再回来看。
我的github:
我实现的代码全部贴在我的github中,欢迎大家去参观。
https://github.com/YinWenAtBIT
第五章:
准备工作:
一、高效程序的前提:
1. 合适的算法与数据结构:这个其实是最重要的,有了一个良好的数据结构与算法,能把程序的时间空间复杂度降低很多。
2. 编写编译器能够优化的高效源代码:这里实际上也就是不要给编译器帮倒忙了,大部分编译器已经能对源代码进行非常好的优化了
3.使用多线程技术:这个就是在大量的计算的时候会用到。
二、编译器的能力与局限性:
1.编译器只会很小心的对程序使用安全的优化:
这就意味着,许多在我们看来很明显可以优化的行为,编译器是不一定敢于优化的。这里通常存在的不安全的原因是,如果出现了对同一个存储器的别名使用,就会有许多想不到的行为。所以在这里,编译器是没法进行优化的。
2.选择优化的级别:
在编译时,可以通过-O1 -O2 -O3来选择优化的级别,优化的级别依次递增。
三、表示程序性能:
使用每元素周期数来表示程序的性能:这里每个元素所用周期越长,那么证明程序的速度越慢。
编写优化的程序:
一、消除循环的低效率:
1.代码移动:
把要在循环中计算多次,但是结果又不会改变的计算移动到循环之外。(对于编译器来说,这是不安全的优化,因为不确定该计算代码是否还有别的作用)
2. 减少调用过程:
在循环中调用函数,将会带来压栈与出栈的开销,所以,对于简单的返回值的函数,尽量使得其不需要使用函数来实现。
3.消除不必要的存储器引用:
遇上累加等过程的时候,如果把每次操作的值都赋给存储器,那么必然效率不如使用一个局部变量。因此,应该让局部变量在循环中进行累加,最后再赋值给存储器中的变量。
二、理解现代处理器:
1.指令并行与延迟界限:
在现代CPU中,如果几个计算不会相互影响,那么是可以同时在CPU中进行运算的。
延迟界限指一条指令,从开始到结束必须要经过的时钟周期。该周期为最短周期,无法优化。
2.吞吐量界限:
由于处理器的并行,那么存在有些运算是可以在处理器中同时进行的。比如说加法与乘法运算,那么可以使用吞吐量界限来描述最多同时进行的运算操作所用时间。
3.关键数据流:
循环运算中,有些数据是不能同时并行运算的,他们必须一个接一个的运算,因为后一次运算依赖于前一次计算的结果。所以该计算流程就是该循环中的关节数据流。该数据流处理的必须用时,就成为了优化的界限。最好的优化也只能在这里停止。
三、循环展开:
1. 循环展开是一种程序变换,通过增加每次迭代计算的元素数量,减少循环的次数。
该原理可以用来减少关键数据流的长度。
2. 提高并行性:
由于CPU是可以并行计算的,那么对于累加和累乘,我们可以把结果保存在多个变量中,在最后在合并结果。
3. 重新结合变换:
对于一个计算表达式中,两个连乘,我们可以使用括号,让后一次乘法先进行,然后再进行前一次乘法。这样做的能提升程序速度的原理在于,如果使用顺序乘法,第一次乘法结果与第二次乘法结果都会保存在同一个寄存器中,无形中增长了关键路径。通过该优化方法,能使得关键路径变短。虽然看起来什么也没做。
总结: