RMW 指令是否被认为对现代 x86 有害?

Are RMW instructions considered harmful on modern x86?

我记得在优化 x86 速度时通常要避免读-修改-写指令。也就是说,你应该避免像 add [rsi], 10 这样的东西,它会增加存储在 rsi 中的内存位置。建议通常将其拆分为读取修改指令,然后是存储指令,例如:

mov rax, 10
add rax, [rsp]
mov [rsp], rax

或者,您可以使用显式加载和存储以及 reg-reg 添加操作:

mov rax, [esp]
add rax, 10
mov [rsp], rax

对于现代 x86,这仍然是合理的建议(曾经是吗?)?1

当然,在多次使用内存中的值的情况下,RMW 是不合适的,因为您将招致冗余加载和存储。我对一个值只使用一次的情况感兴趣。

基于对 Godbolt 的探索,所有 icc、clang 和 gcc prefer 使用单个 RMW 指令来编译如下内容:

void Foo::f() {
  x += 10;
}

进入:

Foo::f():
    add     QWORD PTR [rdi], 10
    ret

所以至少大多数编译器似乎认为 RMW 很好,当值只使用一次时。

有趣的是,各种编译器 not 同意增量值是全局的,而不是成员的,例如:

int global;

void g() {
  global += 10;
}

在这种情况下,gccclang 仍然是单个 RMW 指令,而 icc prefers 是一个带有显式加载和存储的 reg-reg 添加:

g():
        mov       eax, DWORD PTR global[rip]                    #5.3
        add       eax, 10                                       #5.3
        mov       DWORD PTR global[rip], eax                    #5.3
        ret     

可能与RIP相对寻址和微融合限制有关?然而,icc13 仍然对 -m32 做同样的事情,所以也许它更多地与需要 32 位位移的寻址模式有关。


1我故意使用模糊术语 现代 x86 基本上是指英特尔和 AMD 的最后几代 laptop/desktop/server筹码。

Are RMW instructions considered harmful on modern x86?

没有

在现代 x86/x64 中,输入指令被翻译成 uops。
任何 RMW 指令都将分解为多个微指令;事实上,单独的指令将被分解成相同的微指令。

通过使用 'complex' RMW 指令而不是单独的 'simple' 读取、修改和写入指令,您可以获得以下结果。

  1. 需要解码的指令更少。
  2. 更好地利用指令缓存
  3. 更好地利用可寻址寄存器

您可以在 Agner Fog's instruction tables 中清楚地看到这一点。

ADD [mem],const 有 5 个周期的延迟。

MOV [mem],reg 反之亦然,每个延迟为 2 个周期,ADD reg,const 的延迟为 1,总共为 5。

我检查了 Intel Skylake 的时间,但 AMD K10 是一样的。

您需要考虑到编译器必须迎合许多不同的处理器,有些编译器甚至为不同的处理器系列使用相同的核心逻辑。这可能会导致非常次优的策略。

RIP 相对寻址
在 X64 RIP 上,相对寻址需要一个额外的周期来解析旧处理器上的 RIP。
Skylake 没有这种延迟,我相信其他人也会消除这种延迟。
我相信您知道 x86 不支持 EIP 相对寻址;在 X86 上,您必须以一种迂回的方式执行此操作。