★继续学习体系结构的知识。
指令之间的相关性
1.数据相关性
具体分为三类:
(1) Output Dependence (WAW):当两条指令尝试向同一个寄存器写入数据时发生。后面的写操作必须等待前面的写操作完成,因为它们争夺同一资源。
(2) Anti-Dependence (WAR):发生在一条指令试图向一个寄存器写入数据,而该寄存器的内容被前面某条指令作为输入使用的情况。写的指令必须等待前读的指令完成。
(3) True Dependence (RAW):一条指令读取的数据依赖于前一条指令写入的数据。真正的“依赖”,限制指令的执行顺序,必须先读后才能写。
数据相关性是并行处理中关键的问题。RAW相关性直接限制了指令的并行执行。
通过硬件设计(如乱序执行、寄存器重命名)和软件优化(编译器重排指令)可以减轻相关性的影响,提高处理器的效率和吞吐量。
2. 存储器数据相关性
这类相关性涉及对内存(通过load和store指令)的访问。与数据相关性相似,也分为WAW、WAR和RAW,但关注点在于内存地址而非寄存器。
3. 控制相关性
由条件分支指令引起,它决定了程序的执行路径。这种依赖关系基于分支条件的结果,影响后续指令的执行。通过分支预测技术可以尝试提前确定分支方向,减少控制相关性带来的延迟。
4. 结构相关性
指的是处理器内部资源限制导致的依赖,比如发射队列、重排序缓存或功能单元的可用性。当所有必要的处理器资源都被占用时,新指令必须等待,直到有资源可用才能继续执行。
数据相关性中WAW和WAR可以通过更换寄存器名来解决
我们可以看到,WAW和WAR相关性被称为“假相关性”。 理论上可以通过寄存器重命名(register renaming)来解决。
寄存器重命名的引入:
寄存器数量限制:现代处理器拥有较多的通用寄存器(如32个),而较老的CISC架构如早期的x86,通用寄存器较少(如8个)。寄存器很少的话编译器和处理器不得不复用寄存器,从而引入了WAW和WAR相关性。寄存器重命名是一种有效的解决方案,它通过映射逻辑寄存器到物理寄存器,允许逻辑上相同的寄存器在不同时间点映射到不同的物理寄存器,以此消除数据冲突。
下面介绍可能存在WAW相关性两个例子:循环和函数调用
(1)循环是编程中常见的结构,循环内的寄存器使用很容易产生大量WAW相关性,即使通过循环展开减少迭代次数,最终也会受限于物理寄存器的数量。循环展开还可能增加代码大小,影响指令缓存(I-Cache)的命中率,降低性能。
(2)频繁调用的小函数也会造成类似的问题。当函数修改相同的寄存器时,会产生大量的WAW相关性。如果使用内联函数可以减少函数调用开销,但也可能导致寄存器耗尽和代码膨胀,影响缓存性能。
所以增加寄存器数量能缓解上述问题,但这涉及到兼容性问题,新增寄存器意味着现有软件需要重新编译,且不增加寄存器并不能根本解决由循环和函数调用等代码结构特性引起的资源竞争问题。所以最好的解决办法是引入寄存器重命名。
寄存器重命名的工作原理
重名名映射表:处理器内部维护一个映射表,记录逻辑寄存器到物理寄存器的当前映射关系。每当指令调度执行时,硬件会根据映射表将逻辑寄存器名转换为实际的物理寄存器。
空闲寄存器列表:记录那些寄存器是空闲的。
分配与回收:当一条指令准备执行,且需要写入寄存器时,寄存器重命名逻辑会检查映射表,为该逻辑寄存器分配一个新的物理寄存器。完成执行的指令所占用的物理寄存器会被释放,以便于后续指令的使用。
解决WAW和WAR:通过为每个写操作分配一个新的物理寄存器,WAW相关性自然消失,因为每个写入都不会覆盖另一个未来可能需要的值。对于WAR相关性,后面的写操作的寄存器和读操作的寄存器不同,两个不会影响。
寄存器重命名显著提高了处理器的指令级并行(ILP),使得更多的指令能够无冲突地同时执行。