流水线
在控制指令中我简单提过CPU流水线的概念,但是当时讲的太粗了,现在来细讲一下。
而在CPU中,指令执行单元EU是执行指令的唯一部件,这意味着单核CPU的情况下,每次只有一个指令处于执行中。那么对于一个取指令->译码->执行
这样一个过程来说,目前的想法可能是取指令->译码->执行
、取指令->译码->执行
、取指令->译码->执行
。但是,CPU这颗工业明珠可不这么简单。其每个部分同一时刻只能做一件事,其就像人体器官一样,时刻并行、分工合作。CPU可以一边执行指令,一边取指令,一边译码,其过程如下:
指令 | 周期一 | 周期二 | 周期三 | 周期四 | 周期五 | 周期六 |
---|---|---|---|---|---|---|
第一条指令 | 取指令 | 译码 | 执行 | |||
第二条指令 | 取指令 | 译码 | 执行 | |||
第三条指令 | 取指令 | 译码 | 执行 |
注:jmp会导致流水线性能浪费
CPU是按照程序中指令顺序来填充流水线,即按照程序计数器cs:ip
中的值来装载流水线
上述这个过程也被称之为三级流水线。当然,在我们电脑上运作CPU可远远大于三级,可能是32级也可能更高。按道理来说,一个过程被分的级数越多,其每一级执行的时间都越短。那么,CPU所执行的数量就越多,效率也就越高。
乱序执行
乱序执行,是指CPU中运行的指令并不按照代码中的顺序执行,而是按照一定的策略打断执行。也许是后面的指令先执行呢?只要前后代码无相关性就好了。
如果代码有相关性,那么就无法乱序了,比如以下代码:
mov eax,[0x1234]
add eax,ebx //这个依赖上一步的结果,不能乱序执行
反之,下面这个例子就可以在执行第一步内存访问后的等待间隔中执行第二步:
mov eax,[0x1234]
add ecx,ebx
乱序执行的最大好处就是后面的操作可以放到前面来做,利于装在到流水线上提高效率。
分支预测
分支预测是指当处理器遇到一个分支指令时,是该把分支左边的指令放到流水线上还是把分支的右边指令放到流水线上呢?对于这种分支情况,因为流水线的缘故,就绪亚奥预测那一侧的指令将被执行,然后预测出那一支上的指令放入流水线。
从统计学的角度来看,某些事情一旦 出现,下一次出现几率还是很大。这就造成了分支预测算法:
- 无条件跳转:没有预测方法,直接跳过去
- 有条件跳转:针对有条件跳转,根据以前数据判断下一次是否跳转
注:分支预测法
最简单的便是2位预测法了。用2位的计数器来记录跳转状态,每跳转一次就加1,知道加到最大值3就不再加;如果未跳转就减一,知道减到最小值0就不再减了。那么当遇到跳转指令时,如果计数器的值大于1就跳转,如果小于1就不跳转
在Intel中的分支预测部件中用了分支目标缓冲器BTB。其结构如下面的表一样:
分支指令所在地址 | 预测的分支地址 | 跳转统计 |
---|---|---|
BTB中记录着分支指令地址。当CPU遇到分支指令时,先用分支指令的地址在BTB中查找,若找到相同地址的指令,根据跳转统计信息判断是否把相应的预测分支地址上的指令送上流水线。
如果BTB中没有相同的记录,这个时候就可以使用Static Preditor
,静态预测器。根据其内部固定写死的预测策略进行判断。
程序在实际执行转移分支指令后,再将转移记录录入到BTB。
参考文献
[1] 操作系统真相还原