推测执行技术

基本块

2c0071fd-4ac8-400d-af04-b5feedb36357

程序可划分成数个基本块,基本块包含多条顺序执行的指令,且只有一个入口一个出口。一个入口意味着整个程序中不能有跳转类指令进入基本块第一条指令后的指令,一个出口意味着基本块最后一条指令才能是跳转指令。上图给出了一段高级语言程序,其包含多个基本块。

前面在讨论乱序执行时,使用到的汇编程序完全由算术指令构成,不包含跳转指令。所以说前面讨论的乱序执行仅适用于基本块内部,这有较大的局限性,因为实际程序中的基本块通常都很短,且基本块间不一定能并行。所以乱序执行只有跨越基本块边界才能有更大效果。而跨越基本块则意味着执行可能不应该执行的指令,这种不知道是否需要执行就提前执行的技术统称为推测执行技术。对于很多硬件来说,其无法支持跨越基本块的乱序执行,所以这部分工作也会交给编译器完成,称为指令重排

 

推测执行

前面讨论分支预测技术时,提到会根据分支预测结果预先执行指令,这就是一种推测执行的场景。前面提到过,推测执行的一个大问题是如何“回滚”计算机状态,即在发现不需要执行这些指令时,如何撤销这些指令对内存、寄存器的修改。比如上述程序中,在k、evensum、oddsum可用时就可以跳过if,推测执行后续的加法指令,但是如果把推测执行后的evensum、oddsum存入内存就不行了。

一种“回滚”计算机状态的方法是拓展前面使用的寄存器重命名方案,在进行推测执行时,先让指令结果存入某些物理寄存器,当确定这些指令是需要实际执行的指令时,再将某些物理寄存器的值作为执行结果。实现这种方案要拓展记分牌、增加额外硬件,实现相关控制逻辑等,虽然复杂但可行。

推测执行还有个问题是可能带来“负优化”,比如在推测执行LOAD指令(从内存读数据装入寄存器)时,如果发生高速缓存缺失,那么会流水线会去读内存和等待内存。如果之后发现这部分推测执行的指令并不需要,那么推测执行这样代价很高的LOAD指令反而是负优化的行为,违背了推测执行优化的初衷。为了解决这个问题,某些现代计算机在指令层中加入了SPECULATIVE-LOAD指令,如果对应的内存数据在高速缓存中,那么其作用和LOAD一样,但如果不在缓存中则直接放弃执行。

推测执行还有个难以解决的麻烦是处理指令产生的异常,比如执行“if(x>y) z=x/y”这样的高级语言程序,假设“if”后面的“x/y”会在“if”之前推测执行,并且因为y=0产生了除0异常,但这又不是真实执行产生的异常。一种解决思路和上面引入“SPECULATIVE-LOAD”指令的思路类似,在指令系统中,为每个可能产生异常的指令都增加一个用于推测执行的版本,并相应的在每个寄存器增设1个毒性位,当推测执行版本的指令产生异常时,不进入通常的异常处理,而是直接标记毒性位为1。当确定这些推测执行的指令应该被执行时,直接根据毒性位处理异常。当这些推测执行的指令不应该被执行时,则清除毒性位。

 

发表评论

您的电子邮箱地址不会被公开。 必填项已用 * 标注

滚动至顶部