for循环次数太多的时间优化_第五章 优化程序性能(二)优化背后的原理

a06acabaae7164bfe8bde1ccbe0b0fea.png

上一篇讲到,现在我们还有两个问题,第一,为什么上一步减少get_vec_element的优化,看起来没有作用。第二,combine4是否还有进一步优化的空间。

要回答这两个问题,我们需要先稍微了解一下现代处理器的一些内部操作流程。

5.7 理解现代处理器

前面都是些基本的优化操作:

  • 降低循环的低效率
  • 减少函数调用
  • 消除存储器引用,使用临时变量

这几个方法都是处理器无关的,不依赖任何机器特性。接下来,就要利用处理器的一些处理方式来有正对性的优化了。

从汇编级来看,指令都是一条条执行,每条指令都包括从寄存器或者存储器取值,执行一个操作,并把结果放回寄存器或者存储器。但在实际的处理器中,是同时对多条指令求值的,这个现象称为指令级并行,在某些设计中,可以有100或更多的指令在处理中。当然,处理器会有很多精细的机制来保证,指令看起来是简单的顺序执行的。

似乎进入了魔法领域

5.7.1 整体操作

下图是基于近期Intel处理器的一个近似结构,它的一个特性叫做超标量,意思是它可以在每个时钟周期执行多个操作,而且是乱序的,即可能跟他们在机器级代码中的循序不一致。整个设计有两个主要部分ICU(Instuction Control Unit 指令控制单元)EU(Execution Unit 执行单元)

听到ICU是不是觉得虎躯一震。。。

3e107fbb3e58064754d2fd795a368759.png

处理器在遇到分支跳转的时候,会采用一种分支预测的技术,用来投机提前执行一些操作。预测就有可能错误,当预测错误的时候,提前执行的东西就浪费了,需要重新取出另一个方向上的指令。

指令解码将汇编指令转换成一组基本操作。例如addl %eax,%edx 产生一个加法操作,而addl %eax,8(%edx)则会产生三个操作——加载内存,加法操作,存回内存。

如图5.11中,有一个退役单元, 这个部分记录正在进行的操作。一旦一条指令操作完成了,而且所有引起这条指令的分支点也都被确认为预测正确,那么这条指令就可以退役了,所有对程序寄存器的更新都可以被实际执行了。反之,如果预测错误,则指令被清空,计算出的结果被抛弃。

任何对程序寄存器的更新都只会在指令退役的时候才会发生。

寄存器重命名

所谓寄存器重命名就是一种减少寄存器读写等待的技术,一个要写寄存器的操作,可能因为是在预测分支中,无法直接写入,需要等待后续确认之后才能写入,那么该分支上后续指令需要使用这个寄存器,那么就在一张特定的表中取查询该值,从而跳过等待。即使没有分支预测,也能加快指令的执行,无需等待写入。

这个设计可以考虑怎么利用到项目中,对于那些更新数据会引起一连串变化的操作,可以进行数据缓存,把实际的更新延后。而取数据的时候需要先检测缓存。

5.7.2 功能单元的性能

下标是Intel Core i7参考机的一些算术性能。这里的容量是表示该运算的功能单元的数量。这个数量也是处理器能同时处理多个相同操作的基础。

3a5b16d780992e6118a571cb4f7d7ccd.png

加法和乘法的发射时间都是1,这是通过流水线来实现的。当个操作没有结束就可以开始新的阶段操作。

除法运算时没有流水线实现的,它的发射时间等于延迟,说明新的运算需要等上一个运算结束后才开始。所以除法很慢。

功能单元的最大吞吐量为发射时间的倒数。容量可以增加吞吐量,例如容量为C,发射时间为I ,则吞吐量为 C / I 。而下文提到的吞吐量界限则是吞吐量的CPE表示,即吞吐量的倒数。例如吞吐量为2,表示每个时钟周期能执行两个操作,即相应的CPE为0.5 。

5.7.3 处理器操作的抽象模型

前面combine4函数目前为止是最快的,这里对比下该函数与理论值的比较:可以看到距离理论值还有一定的距离,我们能达到理论值吗

6c071007e92293533e243c0fcd1cc70d.png
  • 3
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值