[英]How does RIP-relative addressing perform compared to mov reg, imm64?
众所周知,x86-64指令不支持64位立即值(mov除外)。 因此,在将代码从32位迁移到64位时,会执行如下指令:
cmp rax, addr32
不能替换为以下内容:
cmp rax, addr64
在这种情况下,我正在考虑两种选择:(a)使用临时寄存器加载常数或(b)使用rip-relative寻址。 这两种方法看起来像这样:
mov r11, addr64 ; scratch register
cmp rax, r11
ptr64: dq addr64
...
cmp rax, [rel ptr64] ; encoded as cmp rax, [rip+offset]
我写了一个非常简单的循环来比较两种方法的性能(我在下面粘贴)。 虽然(b)使用间接指针,但(a)在指令中具有立即编码(这可能导致i-cache的使用更差)。 令人惊讶的是,我发现(b)比(a)快〜10%。 这个结果在更常见的现实世界代码中应该是预期的吗?
true: dq 0xFFFF0000FFFF0000
false: dq 0xAAAABBBBAAAABBBB
main:
or rax, 1 ; rax is odd and constant "true" is even
mov rcx, 0x1
shl rcx, 30
branch:
mov r11, 0xFFFF0000FFFF0000 ; not present in (b)
cmp rax, r11 ; vs cmp rax, [rel true]
je next
add rax, 2
loop branch
next:
mov rax, 0
ret
令人惊讶的是,我发现(b)比(a)快〜10%
您可能在AMD Bulldozer系列或Ryzen以外的CPU上进行了测试,这些CPU具有快速loop
指令。 在其他CPU上, loop
非常缓慢,主要是出于历史原因 ,因此您会遇到瓶颈 。 例如7个uops,Haswell每5c吞吐量一个。
mov r64, imm64
对uop缓存吞吐量不利,因为在英特尔的uop缓存中立即占用2个插槽。 (参见Agner Fog的microarch pdf中的Sandybridge uop缓存部分), 对于x86-64 , 哪个更快,imm64或m64? 在哪里我列出了细节。
即使除此之外,循环中的1个额外uop使其运行速度变慢并不令人惊讶 。 你可能不是在AMD CPU上(每2个时钟loop
使用单uop / 1),因为在如此微小的循环中的额外mov
会产生超过10%的差异。 或者完全没有区别,因为每2个时钟只有3个对4个uop,如果这是正确的,即使是微小的loop
循环也限制为每2个时钟跳一次。
在Intel上, loop
是7 uops,大多数CPU每5个时钟吞吐量一个,因此每个4个时钟的问题/重命名瓶颈将不会是你所要达到的。 loop
是微编码的,因此前端不能从循环缓冲区运行。 (并且Skylake CPU通过微代码更新禁用了LSD以修复部分寄存器错误。)因此每次循环时都必须从uop缓存中重新读取mov r64,imm64
。
在高速缓存中命中的负载具有非常好的吞吐量 (每个时钟2个负载, 在这种情况下,微融合意味着没有额外的uop来使用内存操作数而不是cmp
的寄存器 )。 因此,从内存中使用常量的主要原因是额外的缓存占用空间和缓存未命中,但是您的微基准测试根本不会显示。 它对装载端口没有其他压力。
如果可能,使用RIP相对的lea
生成64位地址常量。
例如lea rax, [rel addr64]
。 是的,这需要额外的指令来将常量输入寄存器。 (顺便说一下,只需使用default rel
。如果需要,你可以使用[abs fs:0]
。
如果使用默认(小)代码模型构建与位置相关的代码 ,则可以避免额外的指令, 因此静态地址适合虚拟地址空间的低32位并且可以用作中介 。 (实际上低2GiB,因此符号或零扩展两种工作)。 请参阅x86-64 Linux中不再允许的32位绝对地址? 如果gcc抱怨绝对解决; 大多数发行版默认启用-pie
。 这当然不适用于Linux共享库,它只支持64位地址的文本重定位。 但是你应该尽可能地避免重定位,使用lea
来创建位置无关的代码。
大多数整数构建时常量适合32位,因此即使在PIC代码中也可以使用cmp r64, imm32
或cmp r32, imm32
。
如果确实需要64位非地址常量,请尝试将mov r64, imm64
从循环中mov r64, imm64
出来。 如果mov
不在循环内,你的cmp
循环会很好。 x86-64有足够的寄存器,您(或编译器)通常可以避免在整数代码中的最内层循环内重新加载。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.