在x86汇编中,将单个内存单元设置为零或常量的最快方法是什么?

4

在x86中,将单个内存单元设置为零的最快方法是什么?通常我这样做:

C745D800000000  MOV [ebp-28], 0

你可以看到,这种编码方式相当复杂,因为它使用了全部4个字节的常量。如果使用普通的寄存器,我可以使用更紧凑的MVZE指令,但是MVZE指令不适用于内存。

我在考虑先清空一个寄存器,然后将寄存器的值MOV到内存中, 这样虽然需要两条指令,但总长度只有5个字节,比上面那个7个字节的指令更短。遵循"越短就越快"的原则,这可能更可取。


2
由于指令不能有两个内存操作数,因此您无法将内存单元与其自身进行异或运算。 - Daniel Kamil Kozar
2
当然可以,但它们并没有明确地作为该指令的操作数进行说明,并且直接与其编码在一起。 - Daniel Kamil Kozar
2
当你说“单个单元格”时,你是指一个字节吗?还是指一个双字/四字(其中mov需要一个imm32)? - Peter Cordes
1
你给这个贴打了x86标签;x86架构围绕着未对齐的加载/存储和单字节进行设计。显然,许多非x86 CPU实际上在缓存中执行RMW循环以更新字中的一个字节进行字节存储(是否有任何现代/古老的CPU/微控制器,其中缓存的字节存储实际上比字存储慢?),但现代指令集架构都是按字节寻址的,并且都有体系结构级别的字节存储指令。 (除早期的Alpha之外,如果您认为它现代的话)。 - Peter Cordes
显示剩余9条评论
2个回答

5
很遗憾,您在此处编写的是“直接”将内存单元清零的唯一方法。当然,通过对寄存器进行异或操作,然后将其移动到某个内存位置也可以实现,但我不知道这是否会更快。
如果您恰好拥有一个值为零并且确定的寄存器,请尽管使用它。否则,只需坚持使用mov [ebp-28], 0。请记住,mem, imm操作数被认为是最慢的之一:如果您分析了代码并发现这是瓶颈,那么请在函数(或其他内容)开头初始化一个寄存器为零,然后在整个代码中使用它作为预定义常量。

你知道这是否也是最短的方式吗?在x86 64位上,mov [r14], 0是一个7字节的指令。 - Björn Lindqvist
4
在x86-64架构中,“xor eax, eax” + “mov [r14d], rax”只需5个字节。因为对32位寄存器进行的所有操作都会隐式清除其上半部分,并且编码时更短,所以您不需要对64位寄存器“rax”进行异或操作。尽管如此,这种方法可能并不一定比“mov mem,imm”更快。但是像Daniel所说的那样,在同一个函数中如果您有任何其他用途可以使用值0,那么这将是一个显而易见的、巨大的优势,特别是在x86-64中,您几乎总是会有足够的寄存器。在x86-32上做决定会更加困难,因为您将失去一个宝贵的寄存器作为空寄存器。 - Cody Gray
1
有趣的事实:英特尔CPU无法使用RIP相对寻址模式和立即数微融合一条指令,因此mov dword [rel label], 0解码为2个uop指令。因此,在x86-64上的静态数据中,如果您正在针对英特尔CPU进行调整,则首先使用xor将寄存器清零是纯粹的优势。 - Peter Cordes

2
如果您希望数据不在缓存中,并且不指望很快再次访问,MASKMOVDQU 可能是最快的方式。这使您可以写入一个或多个字节,而不影响周围的字节,并且无需等待请求所有权请求将关联的高速缓存行带入内存。
本质上,写操作直接发送到内存,而不是相反。由于 CPU 以高速缓存线大小的块与内存交互,因此在底层发生的事情是,包含写操作的高速缓存线被发送下来,同时还有一个掩码表示实际更新的字节。然后在存储控制器、L3高速缓存或存储器本身中,要写入的字节与应保留的字节合并。
原始答案:最初的回答

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接