这里[1]以及其他来源[2,3]已经涵盖repz ret
的问题,非常令人满意。但是,在阅读这两个来源时,我找到了以下答案:
与ret
或nop; ret
的定量比较中,实际惩罚是什么?特别是在后一种情况下 - 解码一个额外的指令(并且那个空的指令!)真正相关,当大多数函数有100多个或者内联时?
为什么AMD K8永远无法修复,甚至进入K10?从什么时候开始记录一个丑陋的变通方法,基于保持未记录的行为,更倾向于实际修复问题,何时知道原因的每个细节?
答案 0 :(得分:7)
分支错误预测
所有喧闹的原因是分支机构误预测的代价
当分支出现时,CPU会预测分支,并在管道中预加载这些指令
如果预测错误,则需要清除管道并加载新指令
这可能需要最多number_of_stages_in_pipeline
个周期以及从缓存加载数据所需的任何周期。每次误预测通常会有14到25个周期。
原因:处理器设计
K8和K10遭受这种情况的原因是由于AMD的优化
AMD K8和K10将对高速缓存中的指令进行预解码,并在CPU L1指令高速缓存中跟踪它们的长度
为了做到这一点,它有额外的位。
For every 128 bits (16 bytes) of instructions there are 76 bits of additional data stored。
下表详细说明了这一点:
Data Size Notes
-------------------------------------------------------------------------
Instructions 128 bits The data as read from memory
Parity bits 8 bits One parity bit for every 16 bits
Pre-decode 56 bits 3 bits per byte (start, end, function)
+ 4 bit per 16 byte line
Branch selectors 16 bits 2 bits for each 2 bytes of instruction code
Total 204 bits 128 instructions, 76 metadata
因为所有这些数据都存储在L1指令缓存中,所以K8 / 10 cpu在解码和分支预测上的工作量要少得多。这节省了硅。
而且由于AMD没有像英特尔那样大的晶体管预算,因此需要更智能地工作。
但是,如果代码是esp。一个跳跃和一个ret可能会占用相同的两个字节的插槽,这意味着RET
被预测为未被捕获(因为跟随它的跳跃)。
通过使RET占用两个字节REP RET
,这永远不会发生,并且始终可以预测RET。
英特尔没有这个问题,但(曾经)从有限数量的预测时段中受到影响,AMD没有。
<强> nop ret
强>
没有理由做nop ret
。这是两条指令浪费一个额外的周期来执行nop
而ret
可能仍然与跳跃“配对”。
如果您想使用REP MOV
代替multibyte nop
或使用range(-128, 128)
。
结束语
只有本地分支预测与缓存中的指令一起存储
还有一个单独的全局分支预测表。