如果x86 rep
前缀的初始计数为零,会发生什么情况?
Intel's manual明确表示这是一个while count != 0
循环,测试位于顶部,这是正常的预期行为。
但是我在其他地方看到的许多模糊的报告中,大多数都表明没有对零进行初始测试,所以它就像是一个倒计时,在最后进行测试,所以如果它是repeat
{… count —=1; }
until count == 0;
,或者谁知道呢,那就是灾难。
如果x86 rep
前缀的初始计数为零,会发生什么情况?
Intel's manual明确表示这是一个while count != 0
循环,测试位于顶部,这是正常的预期行为。
但是我在其他地方看到的许多模糊的报告中,大多数都表明没有对零进行初始测试,所以它就像是一个倒计时,在最后进行测试,所以如果它是repeat
{… count —=1; }
until count == 0;
,或者谁知道呢,那就是灾难。
1条答案
按热度按时间wwwo4jvm1#
当RCX=0时,什么也不会发生;
rep
前缀 do 首先检查零,就像伪代码所说的那样。(与loop
指令不同,它完全类似于do{}while(--ecx)
或dec rcx
/jnz
的底部,但不影响FLAGS。我想我很少听说过这被用作条件加载or store和
rep lodsw
或rep stosw
的习惯用法,计数为0或1,特别是在cmov之前的糟糕日子里。(cmov
是一个无条件的加载,它提供ALU选择操作,所以它需要一个有效的地址,不像rep lods
的计数为零。)这是没有效率的,特别是对于现代x86上的rep stos
,使用快速字符串微代码(P6和更高版本),特别是没有像快速短重复移动(Ice Lake IIRC)这样的东西。这同样适用于将前缀视为
repz
/repnz
(cmps/scas)而不是无条件rep
(lods/stos/movs)的指令。进行零次迭代意味着它们不修改FLAGS。如果你想在
repe/ne cmps/scas
之后检查FLAGS,你需要确保计数值不为零,或者FLAGS已经被设置,这样你就可以以一种有用的方式分支零长度的缓冲区。(也许是通过对稍后需要的寄存器进行异或置零。)rep movs
和rep stos
从P6开始就在CPU上有快速字符串微码,但启动开销使它们不值得,特别是当大小可能很短和/或数据可能不对齐时。它们在不能自由使用XMM寄存器的内核代码中更有用。最近的一些CPU,如Ice Lake,具有快速短重复的微码,我认为这应该减少小计数的启动开销。repe/ne scas/cmps
在大多数CPU上 * 没有 * 快速字符串微代码,只有在最近的CPU上,如Sapphire急流和桤木Lake P-cores。所以它们非常慢,根据https://agner.org/optimize/和https://uops.info/的测试,每个时钟周期加载一次(因此cmpsb/w/d/q
每个计数2个周期)。-O1
用于使用repne scasb
内联strlen
。这对长弦来说是一场灾难。rep movs
也将使用no-RFO存储来处理大容量数据,类似于NT存储,但不会绕过该高速缓存。关于内存带宽注意事项的一般问答。