2012年11月13日 由 大卫·坎特
哈斯威尔乱序调度
Haswell 的乱序执行是微架构变得相当有趣的地方,许多变化是显而易见的。Haswell 的宽度明显大于 Sandy Bridge,具有更多用于动态调度的资源,尽管整体设计相对相似,如图 2 所示。
乱序执行的第一部分是重命名。重命名器将架构源和目标 x86 寄存器映射到底层物理寄存器文件 (PRFs),并分配其他资源,例如加载、存储和分支缓冲区条目以及调度器条目。最后,微操作被绑定到特定端口以进行下游执行。
重命名器可以从 uop 队列中为单个线程提取 4 个融合的 uop,分配适当的资源并重命名寄存器以消除虚假依赖关系。至关重要的是,这 4 个融合的 uop 可以映射到不仅仅是 4 个执行管道。例如,4 个融合的加载+执行 uop 可能映射到 8 个实际的 uop,4 个加载和 4 个依赖的 ALU 操作。
图 2. Haswell 和 Sandy Bridge 的乱序调度
与Sandy Bridge不同,Haswell和Ivy Bridge中的重命名器不必处理所有寄存器到寄存器的移动uops。前端经过增强,以处理某些寄存器移动uops,这通过完全移除这些uops节省了实际乱序执行中的资源。
Haswell 和 Sandy Bridge 核心具有统一的整数和向量重命名、调度和执行资源。大多数乱序资源在两个活动线程之间进行分配,因此如果一个线程停滞,另一个线程可以继续取得实质性进展。相比之下,AMD 的 Bulldozer 划分了向量和整数管道。每个 Bulldozer 模块包括两个完整的整数核心,但在两个核心之间共享一个大型向量核心。从概念上讲,Bulldozer 动态共享浮点和向量单元,同时拥有专用的整数核心。
Haswell中最关键的性能资源都得到了扩展。ROB包含关于uops的状态信息,从168个uops增长到192个,增加了大约15%的乱序窗口。每个融合的uop占用一个ROB条目,因此考虑到融合的加载和存储,Haswell的调度窗口实际上超过了300个操作。ROB在两个线程之间静态分割,而其他结构则是动态共享的。
物理寄存器文件保存了 uops 的实际输入和输出操作数。整数 PRF 增加了 8 个寄存器,使总数达到 168。考虑到 AVX2 是对 Haswell 的重大改动,256 位 AVX 寄存器的数量大幅增加以适应新的整数 SIMD 指令也就不足为奇了。Haswell 具有 24 个额外的物理寄存器,用于重命名 YMM 和 XMM 架构寄存器。分支顺序缓冲区在预测错误的情况下用于回滚到已知良好的架构状态,仍然保持 48 个条目,与 Sandy Bridge 一样。加载和存储缓冲区是任何内存访问所必需的,分别增加了 8 和 6 个条目,使在飞行中的加载总数达到 72,存储总数达到 42。
与AMD的Bulldozer不同,Haswell继续使用一个统一的调度器,该调度器包含所有不同类型的uops。Haswell中的调度器现在有60个条目,较Sandy Bridge的54个有所增加,这些条目在活动线程之间动态共享。调度器保存由于资源或操作数限制而等待执行的uops。一旦准备就绪,uops通过调度端口发给执行单元。虽然融合的uops在ROB中占用一个条目,但执行端口可以处理单个未融合的uop。因此,一个融合的load+ALU uop将占用两个端口进行执行。
Haswell 和 Sandy Bridge 都可以在所有组成的 uops 成功执行后,每个周期退役最多 4 个融合的 uops。退役是按顺序进行的,并清除诸如 ROB、物理寄存器文件和分支顺序缓冲区等资源。
新的两种指令集扩展给乱序机器带来了新的负担。TSX 创建了一类潜在的流水线清空。当事务中止时,流水线被清空,架构状态被回滚。正如我们在早期关于 Haswell 的 TM 的文章中预测的,这看起来与分支误预测非常相似。
TSX 还支持多个嵌套事务,这需要硬件资源来跟踪和区分不同级别的嵌套。具体来说,Haswell 可以同时处理 7 个嵌套事务。任何后续事务将在开始时立即中止。需要注意的是,这个限制是微架构级别的,未来几代可能会增加。可以推测这与某些具有 7 个猜测条目的硬件结构有关,最后一个条目用于回滚状态。
收集指令是微编码的,并给微架构引入了额外的复杂性。正如预期的那样,英特尔的收集方法强调简单性和正确性。这使得相对于更复杂的实现,性能有所妥协,但由于风险设计选择的减少,更不容易导致项目延迟。
gather 指令执行的 uops 数量取决于元素的数量。每个元素由一个加载 uop 获取,该 uop 消耗一个加载缓冲区条目,而 ALU uops 计算向量寻址并将收集的数据合并到一个寄存器中。执行 gather 的 uops 完全访问硬件,其语义与传统的 x86 加载和 ALU 指令有很大不同,因此 gather 的实现比程序员能够创建的更高效。