繁体   English   中英

与mov reg,imm64相比,RIP相对寻址的执行情况如何?

[英]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, imm32cmp r32, imm32

如果确实需要64位非地址常量,请尝试将mov r64, imm64从循环中mov r64, imm64出来。 如果mov不在循环内,你的cmp循环会很好。 x86-64有足够的寄存器,您(或编译器)通常可以避免在整数代码中的最内层循环内重新加载。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM