Performance 与mov reg、imm64相比,RIP相对寻址的性能如何?
众所周知,x86-64指令不支持64位立即数(mov除外)。因此,当将代码从32位迁移到64位时,如下所示的指令:Performance 与mov reg、imm64相比,RIP相对寻址的性能如何?,performance,assembly,x86-64,Performance,Assembly,X86 64,众所周知,x86-64指令不支持64位立即数(mov除外)。因此,当将代码从32位迁移到64位时,如下所示的指令: cmp rax, addr32 mov r11, addr64 ; scratch register cmp rax, r11 不能替换为以下内容: cmp rax, addr64 在这种情况下,我考虑两种选择:(a)使用暂存寄存器加载常量或(b)使用rip相对寻址。这两种方法如下所示: cmp rax, addr32 mov
cmp rax, addr32
mov r11, addr64 ; scratch register
cmp rax, r11
不能替换为以下内容:
cmp rax, addr64
在这种情况下,我考虑两种选择:(a)使用暂存寄存器加载常量或(b)使用rip相对寻址。这两种方法如下所示:
cmp rax, addr32
mov r11, addr64 ; scratch register
cmp rax, r11
我写了一个非常简单的循环来比较两种方法的性能(我在下面粘贴)。虽然(b)使用间接指针,(a)在指令中编码了立即数(这可能会导致i-cache的更糟糕使用)。令人惊讶的是,我发现(b)跑得比(a)快约10%。在更常见的实际代码中,这是预期的结果吗
令人惊讶的是,我发现(b)跑得比(a)快约10% 您可能在AMD推土机系列或Ryzen以外的CPU上进行了测试,后者具有快速
循环
指令。在其他CPU上,,因此您在它上遇到了瓶颈。e、 g.7个UOP,Haswell上每5c吞吐量一个
mov r64、imm64
对uop缓存吞吐量不利,因为在Intel的uop缓存中,大的立即数占用了2个插槽。(请参阅中的Sandybridge uop缓存部分),我在其中列出了详细信息
除此之外,循环中的1个额外uop使其运行速度变慢也就不足为奇了。您可能不在AMD CPU上(每2个时钟只有一个uop/1loop
),因为在这样一个微小的循环中,额外的mov
将产生超过10%的差异。或者根本没有区别,因为每2个时钟只有3个或4个uops,如果这是正确的话,即使很小的循环循环也被限制为每2个时钟跳一次
在Intel上,loop
是7个uops,在大多数CPU上是每5个时钟一个吞吐量,因此每4个时钟的问题/重命名瓶颈不会是您遇到的问题<代码>循环
是微编码的,因此前端不能从循环缓冲区运行。(Skylake CPU的LSD被微码更新禁用,以修复部分寄存器勘误表。)因此每次通过循环时都必须从uop缓存中重新读取mov r64,imm64
uop
命中缓存的加载具有很好的吞吐量(每个时钟2次加载,在这种情况下,微融合意味着不需要额外的UOP来使用内存操作数而不是cmp的寄存器)。因此,从内存中使用常量的主要缺点是额外的缓存占用空间和缓存未命中,但您的微基准不会显示这一点。它在负载端口上也没有其他压力
在一般情况下:
如果可能,使用RIP相对lea
生成64位地址常量。
e、 g.lea-rax,[rel addr64]
是的,这需要额外的指令才能将常数放入寄存器。(顺便说一句,只需使用默认rel
。如果需要,您可以使用[abs fs:0]
如果使用默认(小)代码模型构建位置相关的代码,则可以避免额外的指令,使静态地址适合低32位的虚拟地址空间,并可用作即时地址(实际上是低2GiB,因此符号或零扩展这两个工作)。查看gcc是否抱怨绝对寻址;-pie
在大多数发行版上默认为启用。这在Linux共享库中当然不起作用,因为Linux共享库只支持64位地址的文本重定位。但您应该尽可能避免重定位,方法是使用lea
生成位置无关的代码
大多数整数构建时间常数适合32位,因此您甚至可以在PIC代码中使用CMPR64、imm32
或CMPR32、imm32
如果确实需要64位非地址常量,请尝试将mov r64,imm64
从循环中提升出来。如果mov
不在循环中,则cmp
循环就可以了。x86-64有足够的寄存器供您(或编译器)使用通常可以避免在整数代码的最内部循环中重新加载。mov rcx,0x1
/shl rcx,30
与mov ecx相比没有任何优势,1是的,我知道,我只是很懒,不想写一个非常大的常数,并且计数为零:)。至于rax
,我对它的价值不感兴趣,但对je next
不感兴趣。有一个不可预测的初始值在我看来似乎是一个很好的属性,但我想这并不重要,只是让人困惑。这就是为什么你让汇编程序为你设定常数,但实际上写的是mov ecx,1相关:/