Exploiting ILP Using Dynamic Scheduling, Multiple Issue, and Speculation
到目前为止,我们已经看到了动态调度,多个问题和推测的各个机制是如何工作的。 在本节中,我们将所有这三个部分放在一起,得出的微体系结构与现代微处理器中的微体系结构非常相似。 为简单起见,我们仅考虑每个时钟发出两个指令的速率,但是这些概念与每个时钟发出三个或更多指令的现代处理器没有什么不同。
假设我们想扩展Tomasulo的算法以支持具有独立整数,加载/存储和浮点单元的多发射超标量流水线(FP乘法和FP加法),它们每个都可以在每个时钟上启动一个操作。 我们不想不按顺序向保留站发出指令,因为这可能导致违反程序语义。 为了获得动态调度的全部优势,我们将允许流水线在一个时钟中发布两个指令的任何组合,使用调度硬件将操作实际分配给整数和浮点单元。 由于整数和浮点指令的交互至关重要,因此,我们还扩展了Tomasulo的方案,以处理整数和浮点功能单元和寄存器,并结合了推测性执行。 如Figure 3.21所示,基本组织类似于一个推测每个时钟有一个发射的处理器架构,不同之处在于必须增强发射并完善逻辑以允许每个时钟处理多个指令。
由于多个指令可能相互依赖,因此在动态调度的处理器中每个时钟发出多个指令(有或没有推测)非常复杂。 因此,必须并行更新指令表。 否则,表格将不正确或依赖性可能会丢失。
在动态调度的处理器中,已使用两种不同的方法在每个时钟上发出多个指令,并且两种方法都依赖于知道处理的关键是分配预留站并更新流水线控制表。一种方法是在半个时钟周期内运行此步骤,以便可以在一个时钟周期内处理两条指令。 然而,这种方法不能轻易扩展为每个时钟处理四个指令。
第二种选择是构建一次处理两个或更多指令所必需的逻辑,包括指令之间的任何可能依赖关系。每个时钟发出四个或更多指令的现代超标量处理器可能包括两种方法:它们都流水线化并扩大了发行逻辑。 一个关键的观察是,我们不能简单地通过流水线解决问题。 由于每个时钟周期都会发布新指令,因此使指令发布占用多个时钟,我们必须能够分配保留站并更新流水线表,以便在下一个时钟发布的从属指令可以使用更新的信息。
这个发布步骤是动态调度的超标量中最基本的瓶颈之一。 为了说明此过程的复杂性,Figure3.22显示了一种情况的发布逻辑:发布负载,然后执行从属的FP操作。 该逻辑基于第197页Figure3.18中的逻辑,但仅代表一种情况。 在现代超标量中,必须考虑允许在同一时钟周期内发出的相关指令的所有可能组合。 由于可能性的数量随时钟中可以发出的指令数量的平方而上升,因此发出步骤可能是每个时钟尝试超过四个指令的瓶颈。
我们可以概括Figure 3.22的细节,以描述在动态调度的超标量中更新问题逻辑和预留表的基本策略,每个时钟最多n个issue,如下所示:
- 为下一个发行捆绑包中可能发布的每条指令分配一个预留站和一个重排序缓冲区。 通过使用n个可用的重新排序缓冲区条目,可以在知道指令类型之前完成此分配,将包含重新排序缓冲区条目按顺序预先分配给数据包中的指令,并确保有足够的预留站可用于发布整个捆绑包,而与捆绑包中的具体指令无关。 通过限制给定类的指令数量(例如,一个FP,一个整数,一个ld,一个st),可以预分配必要的保留站。 如果没有足够的预留站(例如程序中的下几条指令全部属于一种指令类型),则捆绑包将被破坏,并且仅以原始程序顺序发出指令的子集。 捆绑包中的其余指令可以放在下一个捆绑包中,以防可能出现问题。
- 分析发出捆绑包中各指令之间的所有依存关系。
- 如果捆绑包中的指令取决于同一捆绑包中的较早指令,将使用分配的重排序缓冲区号来更新从属指令的保留表。 否则,将使用现有的保留表和重新排序缓冲区信息来更新发布指令的保留表条目。
当然,使上述步骤变得非常复杂的是,所有这些操作都是在一个时钟周期内并行完成的!
从性能方面来看,我们可以展示这个概念如何与一个例子合理。
在流水线的后端,我们必须能够每个时钟完成并提交多个指令。 这些步骤比发出问题要容易一些,因为实际上可以在同一时钟周期内提交的多个指令必须已经处理并解决了所有依赖性。 正如我们将看到的,设计人员已经弄清楚了如何处理这种复杂性:我们在3.12节中研究的Intel i7本质上使用了我们描述的用于推测性多个问题的方案,包括大量的预留站,重新排序 缓冲区,以及还用于处理非阻塞高速缓存未命中的加载和存储缓冲区。
这个例子清楚地表明了当存在依赖于数据的分支时,推测是如何有利的,否则分支将限制性能。 但是,这种优势取决于准确的分支预测。 错误的推测并不能提高性能。 实际上,它通常会损害性能,并且正如我们将要看到的那样,它大大降低了能效。