内存目标 BTS 如何比 load / BTS reg,reg / store 慢得多?

Noa*_*oah 4 performance assembly x86-64 cpu-architecture microcoding

在一般情况下,可以使用内存或寄存器操作数的指令如何使用内存操作数变慢然后 mov + mov -> 指令 -> mov + mov

根据Agner Fog 指令表中的吞吐量和延迟(在我的案例中查看 Skylake,p238),我看到以下btr/bts指令数字:

instruction, operands, uops fused domain, uops unfused domain, latency, throughput
mov          r,r       1                  1                    0-1      .25
mov          m,r       1                  2                    2        1
mov          r,m       1                  1                    2        .5
... 
bts/btr      r,r       1                  1                    N/A      .5
bts/btr      m,r       10                 10                   N/A      5
Run Code Online (Sandbox Code Playgroud)

我不明白这些数字怎么可能是正确的。即使在没有可用寄存器的最坏情况下,并且您将一个寄存器存储在临时内存位置,这样做也会更快:

## hypothetical worst-case microcode that saves/restores a scratch register
mov m,r  // + 1  throughput , save a register
mov r,m  // + .5 throughput , load BTS destination operand
bts r,r  // + 1  throughput , do bts (or btr)
mov m,r  // + 1  throughput , store result
mov r,m  // + .5 throughput , restore register
Run Code Online (Sandbox Code Playgroud)

作为最坏的情况,这具有比bts m,r(4 < 5)更好的吞吐量。(编者注:当它们有不同的瓶颈时,将吞吐量相加不起作用。您需要考虑 uops 和端口;这个序列应该是 2c 吞吐量,瓶颈在 1/clock 存储吞吐量上。)

并且微码指令有自己的一组寄存器,因此实际上不太可能需要这样做。任何人都可以解释为什么bts(或通常任何指令)可以比使用最坏情况的移动策略具有更高的内存、寄存器操作数吞吐量。

(编者注:是的,微码可以使用一些隐藏的临时寄存器。类似的东西add [mem], reg至少在逻辑上只是加载到其中之一然后存储结果。)

Ros*_*dge 6

您缺少的是 BT、BTC、BTS 和 BTR 不像您在使用内存操作数时所描述的那样工作。您假设内存版本与寄存器版本的工作方式相同,但事实并非如此。对于寄存器版本,使用的第二个操作数的值取模 64(或 16 或 32)。对于内存版本,第二个操作数的值按原样使用。这意味着指令访问的实际内存位置可能不是内存操作数给出的地址,而是超过它的某个位置。

例如,忽略保存寄存器和原子性的需要,要获得与BTS [rsi + rdi], rax使用 BTS 的寄存器版本相同的操作,您需要执行以下操作:

LEA rbx, [rsi + rdi]
MOV rcx, rax
SHR rcx, 8
MOV rdx, [rbx + rcx]
BTS rdx, rax
MOV [rbx + rcx], rdx
Run Code Online (Sandbox Code Playgroud)

如果您知道 RAX 的值小于 64,或者它是一个更简单的内存操作数,则可以简化此操作。事实上,正如您所注意到的,在这种情况下,使用较慢的寄存器版本比使用较慢的内存版本可能是一种优势,即使这意味着更多的指令。