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

Assembly 在x86汇编中将单个内存单元设置为零或常量的最快方法?,assembly,x86,x86-64,zero,micro-optimization,Assembly,X86,X86 64,Zero,Micro Optimization,在x86中,将单个内存单元设置为零的最快方法是什么?我通常的做法是: C745D800000000 MOV [ebp-28], 0 正如您所看到的,这是一个相当粗的编码,因为它使用所有4个字节作为常量。对于普通寄存器,我可以使用更紧凑的MVZE,但是MVZE不适用于内存 我在想可能会清除一个寄存器,然后MOV将寄存器值写入内存。然后,它将是两条指令,但总共只有5个字节,而不是上面的一条7字节指令。遵循“如果它更短,通常更快”的规则,这可能更可取。不幸的是,您在这里写的内容是“直接”将内存单元

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

C745D800000000  MOV [ebp-28], 0
正如您所看到的,这是一个相当粗的编码,因为它使用所有4个字节作为常量。对于普通寄存器,我可以使用更紧凑的
MVZE
,但是
MVZE
不适用于内存


我在想可能会清除一个寄存器,然后
MOV
将寄存器值写入内存。然后,它将是两条指令,但总共只有5个字节,而不是上面的一条7字节指令。遵循“如果它更短,通常更快”的规则,这可能更可取。

不幸的是,您在这里写的内容是“直接”将内存单元归零的唯一方法。当然,对寄存器进行异或运算,然后将其移动到某个内存位置也可以,但我不知道这是否会更快


如果您碰巧有一个值为零的寄存器,并且您确信它,那么请务必使用它。否则,只需使用
mov[ebp-28],0即可。请记住,
mem,imm
操作数是已知速度最慢的操作数之一:如果您分析代码并发现这是一个瓶颈,请尝试在函数(或其他)开头将寄存器初始化为零,然后在整个代码中使用它,作为一种预定义的常量。

如果您希望数据从缓存中出来,并且不希望很快再次访问它,那么这可能是最快的方法。这允许您写入一个或多个字节,而不会影响周围的字节,也不会等待所有权请求将关联的缓存线放入内存


从本质上讲,写操作直接发送到内存,而不是相反的方式。由于CPU在缓存线大小的块中与内存交互,因此在封底下发生的事情是,包含写操作的缓存线被向下发送,同时还有一个掩码,指示哪些字节实际被更新。无论是在内存控制器、三级缓存还是内存本身,要写入的字节都会与应该保留的字节合并。

您可以将其与自身异或,但我认为这不会更快:
XOR[ebp-28],[ebp-28]
。您不能将内存单元与自身异或,因为一条指令不能有两个内存操作数。当然可以,但它们没有明确表示为该指令的操作数并直接用它进行编码。当你说“单个单元”时,是指一个字节吗?或者您是指dword/qword(其中
mov
需要
imm32
)?您标记了这个x86;它们是围绕未对齐的加载/存储和单个字节构建的。显然,许多非x86 CPU实际上在缓存中执行RMW循环,以更新一个字中的一个字节作为字节存储(),但现代ISA都是字节可寻址的,并且都具有体系结构字节存储。(). (除了早期阿尔法,如果你认为它是现代的)你知道这也是最短的方法吗?在x86-64上,0是一条7字节的指令。@Björn在x86-64上,
xor eax,eax
+
mov[r14d],rax
将只有5字节。(您不需要对64位寄存器
rax
进行异或运算,因为32位寄存器上的所有操作都会隐式清除上半部分,并且它们的编码时间较短。)但这并不一定比
mov mem,imm
快。但正如Daniel所说,如果在同一个函数中对值0有任何其他用途,这将是一个明显的、巨大的胜利,特别是因为在x86-64上,实际上总是有寄存器指向空间。这一决定在x86-32上要难一点,因为在x86-32上,您将放弃一个有价值的寄存器作为零寄存器。有趣的事实:英特尔CPU不能将指令与RIP相对寻址模式和立即数进行微融合,因此
mov dword[rel label],0作为2-uop指令进行解码。因此,对于x86-64上的静态数据,如果要针对英特尔CPU进行调优,那么先将寄存器归零是一个纯粹的胜利。