《CSAPP》优化程序性能——帮助编译器让程序更快

前言:

     最近事情太多了,十一月到现在只写了一篇博客,得赶紧把最近看的《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. 重新结合变换:

对于一个计算表达式中,两个连乘,我们可以使用括号,让后一次乘法先进行,然后再进行前一次乘法。这样做的能提升程序速度的原理在于,如果使用顺序乘法,第一次乘法结果与第二次乘法结果都会保存在同一个寄存器中,无形中增长了关键路径。通过该优化方法,能使得关键路径变短。虽然看起来什么也没做。

总结:

虽然针对编译器优化有这么多种的做法,但是实际上最重要的还是第一步,选择合适的数据结构与算法。毕竟只有选择了合适方法,才能真正的在次方级别减少计算,而不是优化带来的几倍的性能提升了。再完成了第一步之后,再来考虑更细致的优化才有意义。




  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值