x86
rep
前缀的初始计数为零会发生什么?
英特尔手册明确表示这是一个
while count != 0
循环,测试位于顶部,这是合理的预期行为。
但是我在其他地方看到的许多模糊报告中的大多数都表明没有对零进行初始测试,因此这就像倒计时,最后进行测试,如果是
repeat
{… count —=1; }
until count == 0;
或谁,那就是灾难知道。
RCX=0 时什么也没有发生;
rep
前缀do首先检查零,就像伪代码所说的那样。 (与 loop
指令不同,它完全相同do{}while(--ecx)
的底部,或dec rcx
/jnz
,但不影响标志。)
我想我很少听说过这个习惯用法,用于条件加载 或 store 与
rep lodsw
或 rep stosw
计数为 0 或 1,尤其是在 cmov 之前的糟糕日子里。 (cmov
是提供 ALU 选择操作的无条件负载,因此它需要一个有效的地址,与计数为零的 rep lods
不同。)这效率不高,特别是对于具有快速字符串微码的现代 x86 上的 rep stos
(P6及以后),尤其是没有像 Fast Short Rep-Movs(Ice Lake IIRC)这样的东西。Golden Cove(Alder Lake / Sapphire Rapids)另外还具有快速的零长度rep movsb
,这使得速度与 1-128 字节相同,使得对于有时执行零长度 memcpy 的用例来说,这并不可怕。
这同样适用于将前缀视为
repz
/ repnz
(cmps/scas) 而不是无条件 rep
(lods/stos/movs) 的指令。进行零迭代意味着它们保留 FLAGS 未修改的状态。
如果您想在
repe/ne cmps/scas
之后检查 FLAGS,则需要确保计数非零,或者 FLAGS 已设置,以便您可以以有用的方式对零长度缓冲区进行分支。 (也许是对您稍后需要的寄存器进行异或归零。)
rep movs
和 rep stos
在 CPU 上具有快速字符串微码,但启动开销使它们几乎不值得,特别是当大小可能很短和/或数据可能未对齐时。它们在无法自由使用 XMM 寄存器的内核代码中更有用。最近的一些 CPU(例如 Ice Lake)具有快速短重复微代码,我认为这应该可以减少小计数的启动开销。
repe/ne scas/cmps
不在大多数CPU上都有快速字符串微码,仅在最近的CPU上,例如Sapphire Rapids,也许还有Alder Lake P核。因此它们非常慢,根据 https://agner.org/optimize/和 https://uops.info/ 的测试,每个时钟周期加载一次(因此
cmpsb/w/d/q
每个计数 2 个周期) .
-O1
过去常常使用 repne scasb
来内联 strlen
。这对于长字符串来说是一场灾难。rep movs
也将使用无 RFO 存储来处理大尺寸,类似于 NT 存储,但不会绕过缓存。关于内存带宽注意事项的良好一般问答。对于条件加载/存储,APX 还将引入一种方法来实现“高效”且无分支,使用标量而不是 AVX2 或 AVX-512 屏蔽:故障抑制(Conditionally-Faulting)cfcmovcc [mem], reg
以及加载形式。请参阅