Repz ret:为什么这么麻烦



repz ret的问题已经在这里[1]以及其他来源[2,3]中得到了相当令人满意的讨论。然而,阅读这两个来源,我找到了以下答案:

  1. 在与retnop; ret的定量比较中实际惩罚是多少?尤其是在后一种情况下——当大多数函数要么有100多条指令,要么被内联时,解码一条额外的指令(而且是空指令!)真的相关吗?

  2. 为什么这个问题从来没有在AMD K8中得到解决,甚至在K10中得到解决?当每个细节的原因都是已知的时候,基于一个行为和保持未记录的方式来记录一个丑陋的解决方案,比实际解决问题更可取吗?

分支预测错误
所有这些喧嚣的原因都是分支错误预测的代价。当一个分支出现时,CPU预测该分支被占用,并在管道中预加载这些指令。
如果预测错误,则需要清除管道并加载新指令。
这可能需要number_of_stages_in_pipeline周期加上从缓存加载数据所需的任何周期。典型的错误预测是14到25个周期。

原因:处理器设计
K8和K10之所以会出现这种情况,是因为AMD进行了一项巧妙的优化。AMD K8和K10将在缓存中预解码指令,并在CPU L1指令缓存中跟踪它们的长度。为了做到这一点,它有额外的位。

对于每128位(16字节)的指令,有76位额外的数据存储。

下表详细说明:

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没有英特尔那么大的晶体管预算,它需要更聪明地工作。

然而,如果代码特别紧凑,跳转和ret可能占用相同的两个字节槽,这意味着在那里RET被预测为未被占用(因为它后面的跳转是)。
通过使RET占用两个字节REP RET,这种情况永远不会发生,并且RET将始终被预测为OK。

英特尔没有这个问题,但(过去)受到有限数量的预测插槽的困扰,而AMD没有。

nop ret
没有理由去做nop ret。这两条指令浪费了一个额外的周期来执行nop,而ret可能仍然会与跳转"配对"。如果你想要对齐,使用REP MOVmultibyte nop

闭幕词
只有本地分支预测与指令一起存储在缓存中。
还有一个单独的Global分支预测表。

相关内容

  • 没有找到相关文章

最新更新