正如我们前面所说的,处理器的核心是处理器中负责执行指令的部分。早期的处理器每个时钟周期执行单条指令,因此运行频率为4 MHz的处理器每秒钟可执行400万条指令。执行单条指令的逻辑可能相当复杂,所以执行最长指令所用的时间决定了一个时钟周期的长度,并因此确定了处理器的最高时钟速度。 为了改进这种情况,出现了“流水线”处理器设计。完成单条指令所需的操作被划分为多个较小的步骤。下面是最简单的流水线。 提取 从内存中提取下一条指令。 解码 确定该指令的类型。 执行 完成相应工作。 完成 使指令状态的变化对系统其余部分可见。 假设完成指令所需的总时间保持不变,则4个步骤中任何一个步骤都占用原时间的1/4。然而,一旦一条指令完成“提取”步骤,下一条指令就可以进入这一阶段。这意味着可同时执行4条指令。决定指令完成一个流水线阶段的时间的时钟速率现在可高达以前的4倍。现在一条指令执行完要用去4个时钟周期,这意味着每条指令完成执行所用的实际时间相同,但现在有4条指令同时在处理器流水线上进行处理,因而流水线作业后处理器执行指令的速度是此前的4倍。 例如,图1-9显示了UltraSPARC T2处理器的整数和浮点数流水线。整数流水线有8个阶段,浮点数流水线有12个阶段。 各阶段的名称并不重要,但流水线有几个方面值得探究。无论指令是浮点还是整数类型,4个流水线阶段(提取、缓存、选取及解码)必须完成,只有在流水线的“执行”阶段两种指令类型的执行路径才开始不同。 对于所有指令来说,其操作结果都在“旁路”阶段提供给后续指令,后续指令则需要当前指令“执行”阶段的数据,所以如果第一条指令在周期0开始执行,则依赖指令就可以在周期3开始执行,并可预期此时它所需要的数据已经就绪。例如,如图1-10所示的整数指令中,一条在周期0提取数据的指令将在7个周期之后的“旁路”阶段产生可旁路给下一条指令的结果。依赖指令在到达“执行”阶段时需要此结果作为输入。如果每一周期都提取一条指令,那么第一条指令达到“旁路”阶段时,第四条指令应已达到“执行”阶段。 长流水线的缺点在于出错时的纠正执行,最常见的例子就是错误预测分支。 为了连续不断地提取指令,处理器需要猜测将要执行的下一条指令,下一条指令多为内存中下一个地址内的指令。分支指令可能会改变提取指令的地址,但只有当决定分支的所有条件都已明确、实际的分支指令已执行时,处理器才能确定提取指令的地址。 通常用来处理这个问题的方法是预测是否会产生分支,然后从预测的地址提取指令。如果处理器的预测正确,那么指令流不会中断,也不会产生分支成本。但如果处理器预测错误,那么所有在分支后执行的指令都必须刷新,而且还要重新从内存提取正确的指令流。这就称为分支误预测,其成本与流水线的长度成正比。流水线越长,误预测分支时通过流水线获得正确指令的时间就越长。 流水线作业使处理器的时钟速度更高,但处理器每个周期仍然只能执行一条指令。接下来的一个改进就是 “超标量执行”,即每个时钟周期执行多条指令。英特尔的奔腾处理器是第一个在同一时钟周期中可执行多条指令的x86处理器,有两条流水线,每条流水线每个周期执行一条指令。双流水线可使处理器的性能比上一代产品提升一倍。 最新处理器有4条或更多流水线,每条流水线处理一种特定类型的指令。通常有一条用于加载和存储的内存流水线、一条处理整数计算的整数流水线(整数加法、移位、比较等)、一条浮点数流水线(处理浮点计算)和一条分支流水线(用于分支或调用指令)。如图1-11所示。 之前讨论过的UltraSPARC T2每个核心有4条流水线:两条用于整数运算,一条用于内存操作,一条用于浮点运算。这4条流水线由两组四线程共享,每个周期每组都有一个线程可以发出指令。