览敌?
这样看来,既然流水线这么好,为什么不制造一个 500-段流水线的处理器呢?不幸的是,有很多问题令我们无法这么做。最大的问题是成本和复杂性
—— 以当今的技术,一条 500-段的流水线还是有点过分。就象你需要一间能够容纳 500 个而不是 4 个汽车生产工人的厂房,相对我们的无流水线处理器,你也需要一个
500 倍大的管芯,用我们目前的技术,这样的处理器太昂贵了,产量也将低的可怜。当然,还有其它一些问题。
经常会出现这样的情况,一条指令依赖前一条指令的结果。比如说,视第一条指令的执行结果而定,第二条指令可能不一样。看看下面这个例子:
[1] 指令1 —— 判断 c 的值,给定的 c = a + b
[2] 指令2 —— 如果 c 大于 4,c 的值乘以 2,d = 2c
[3] 指令2 —— 如果 c 小于 4,c 的值乘以 -2,d = -2c
据估计,x86 代码集中大约有 10-20% 包含了上述分支。在这种情况下,不知道第一条指令的结果,我们无法执行第二条指令。因此,提取单元将一直死等到第一条指令执行完毕之后才能开始处理第二条指令。这就浪费了时钟周期,将大大降低效率,这种情况是无法接受的。那么,怎么解决这个问题呢?
有一种可行的解决方案被称为分支预测。本质上说,就是处理器根据经验猜测一下结果可能是什么,并且按照这种假设进行处理。就拿上面那个例子,它可能预测,第一条指令的执行结果可能是
3,于是就按照小于 4 的情况继续执行。因为大多数代码使用了类似的重复循环,所以分支预测也并不是一件极其困难的事。实际上,目前的分支预测单元预测正确率超过
90%。
但是,这 10% 的错误预测时间里会发生些什么呢?(还有其它方法也可以处理指令的依赖性,比如说乱序执行,但这我们留到以后再说)
分支预测错误
让我们再来看看前面那个例子:
提取单元 译码单元 执行单元 存储单元
时钟周期一 指令1
时钟周期二 指令2 指令1
时钟周期三 指令3 指令2 指令1
时钟周期四 指令4 指令3 指令2 指令1
时钟周期五 指令5 指令4 指令3 指令2
假设指令2,3,4等等都依赖于指令1的执行结果,并且都已经按照一个预测的结果执行了。假设这个预测时错误的。因此,指令2基于一个错误的数据,必须按照正确的数据重新处理,指令3,4等等同样如此。这时就要清洗? 线。既然流水线上的所有数据都是根据一个错误的假
设计算出来的,结果,处理器必须放弃所有流水线上的数据。用在这些指令上的时钟周期都浪费了。