请参阅
http://agner.org/optimize/和
x86维基中的其他链接,了解有关如何优化汇编代码的文档。
这样:
mov byte [edi],0
mov byte [edi+1],1
mov byte [edi+2],2
mov byte [edi+3],3
...
将会更快。据我所知,在任何当前的微架构中使用位移都没有额外的成本,除了指令大小增加的额外一个或四个字节。在英特尔SnB系列CPU上,双寄存器寻址模式可能会更慢, 但固定的位移是可以的。
像gcc和clang这样的实际编译器在展开循环时始终使用第一种方法(有效地址中的位移)。
顺便说一句,存储0x03020100
的4字节比四个单独的1字节存储快近4倍。大多数现代CPU都有128位数据路径,因此任何单个存储器高达128位需要与8位存储相同的执行资源。在Intel SnB / IvB上,AVX 256b存储仍然比两个128b存储要便宜得多(如果对齐),而Intel Haswell及更高版本可以在单个操作中执行256b存储。然而,mov-immediate到内存只适用于8、16和32位操作数。mov r64,imm64
(仅限寄存器)在64位模式下可用,但没有128或256 mov-immediate指令。
在32位模式下,可以使用一个字节的编码来实现
inc reg
,因此
inc edi
/
mov byte [edi],1
的代码大小相等,但在最近的英特尔和AMD微架构上,仍会解码为两倍的uops。如果代码仍然受存储吞吐量或其他问题的限制,则这可能不是一个问题,但无论如何都不会更好。 CPU非常复杂,通过计算uops的简单分析并不总是与实际结果匹配,但我认为在每个存储之间运行
inc
会更快的可能性非常小。你能说的最好的就是它可能不会运行得更慢。它可能会使用更多的电力/热量,并且对于超线程来说不太友好。
在64位模式下,
inc rdx
需要3个字节进行编码:1个REX指定64位操作数大小(而不是默认的32位),1个操作码字节指定
inc r/m
,以及1个mod/rm字节指定
rdx
作为操作数。
在64位模式下,有一个代码大小的不利影响。 在两种情况下,“inc”解决方案将使用高价值uop-cache(在Intel SnB家族CPU上保持融合域uops)中的两倍条目,这是一种代码储存器。
更多的uops也意味着ROB中有更多的空间,因此乱序执行不能查看太远的前面。
此外,“inc”指令链将延迟存储地址uops从先前计算多个存储地址(并将它们写入存储缓冲区)。 Intel Ice Lake有两个端口可以运行存储地址uops(从Haswell的3个端口降至)。 如果存储地址尽早准备好,则对后面的负载更好,因此CPU可以确定它们是独立的,或者它们是否重叠。 它还让它们更早地离开调度程序(RS),从而释放出乱序执行结构中的空间。
第二部分:
mov byte [edi+5],0xFF
mov byte [edi+5],0xFF
mov byte [edi+5],0xFF
mov byte [edi+5],0xFF
对抗。
add edi,5 ; 3 bytes to encode.
mov byte [edi],0xFF ; saving one byte in each instruction
mov byte [edi],0xFF
mov byte [edi],0xFF
mov byte [edi],0xFF
除非代码大小非常重要(不太可能),或者存在更多的存储器,否则请使用第一种形式。第二种形式比第一种多一个字节,但少了一个融合域uop。在具有uop缓存的CPU上,它将使用更少的空间。在较旧的CPU上(没有uop缓存),指令解码是瓶颈,因此有些情况下指令更好地对齐为4个一组会成为瓶颈。如果您已经受到存储端口的限制,则不会出现这种情况。