【问题标题】:Is an addition with carry faster with RAX/EAX/AX/AL/AH registers as destination?以 RAX/EAX/AX/AL/AH 寄存器作为目的地的加法是否更快?
【发布时间】:2016-02-12 17:17:05
【问题描述】:

在英特尔文档中,我们对ADC 有下一个定义:

Op/En    Operand 1           Operand 2  .....

RM       ModRM:reg (r, w)    ModRM:r/m (r)
MR       ModRM:r/m (r, w)    ModRM:reg (r)
MI       ModRM:r/m (r, w)    imm8
I        AL/AX/EAX/RAX       imm8

现在是asm 代码的一个小例子:

asm (         
    "adc    -Ox12(%rbp), %rax  \n\t"  //1
    "adc    -Ox12(%rbp), %rdx  \n\t"  //2
    "adc    -Ox12(%rbp), %r8   \n\t"  //3
    "adc    -Ox12(%rbp), %R11  \n\t"  //4

    "adc    %r8 , %rdx  \n\t"  //5
    "adc    %r8 , %rax  \n\t"  //6

    "adc    $3 , %rdx   \n\t"  //7
    "adc    $3 , %rax   \n\t"  //8
);

你能告诉我每组中最快的指令是什么吗?为什么? 我有这个问题是因为在英特尔中他们引用了%RAX 寄存器。另一个慢吗?

【问题讨论】:

  • 我相信这是对 16 位时代的回归,当时英特尔首先设计了指令集,并将寄存器专门用于不同的任务。 ax累加器,因此它的字母为寄存器“A”。如今,唯一真正的优势是带有操作数寄存器 A 的 ADC 具有更短编码的特权,这可能会使代码更快,也可能不会。
  • 我不想局限于加法,更多的寄存器也引用了%RAXEAX(例如)。这是否意味着更多的性能?那么ADC的时钟周期数等于ADDADC 不是比ADD 更有效率,加上进位? pdos.csail.mit.edu/6.828/2008/readings/i386
  • 看起来您提供的链接是 80386 的指令时序。它们不一定与当今的处理器有任何相似之处。我认为寄存器重命名、流水线等功能的存在将使得很难对一个比另一个“更有效”做出任何明确的声明。
  • @IwillnotexistIdonotexist 感谢该链接。非常有用,我不知道。通过本文,寄存器之间没有任何区别。是这样吗?

标签: c++ assembly x86 intel inline-assembly


【解决方案1】:

注意:对于以下所有内容,我假设为现代 80x86(过去 10 年左右的任何内容)。

对于第一组;第一条指令导致缓存未命中或依赖关系停顿的可能性(非常轻微)增加(由 RBP、RAX 或前向指令修改的进位标志引起)。

对于所有其他指令,都依赖于 eflags(它们必须等到前一条指令的进位标志已知),它们都会受到同样的影响。更具体地说,我希望“进位标志依赖性”将执行限制为每条指令 1 个周期(没有指令并行发生)。这是最有可能的瓶颈。

使用的寄存器没有任何区别(除了依赖于之前使用的寄存器)。

【讨论】:

  • 将进位作为输入并不会阻止通常的乱序机制在指令序列的早期或后面运行其他独立的依赖链。不过,它确实将该组变成了一个依赖链,这就是我猜你的意思。 Intel pre-Broadwell 对adc 有两个周期的延迟。
【解决方案2】:

即使 adc $3, %rax 也无法有效地使用特殊的 rax-only 编码
REX.W + 15 id ADC RAX, imm32

  • REX.W + 15 03 00 00 00 是 6 个字节。 (adc rax, imm32)
  • REX.W + 83 mod/rm 03 是 4 个字节。 (adc r/m32, imm8,其中 mod/rm 字节将rax 编码为目标,reg 字段中的/2 是操作码的一部分。immediate-src 操作共享第一个操作码字节。)

The (16bit version of) both encodings were introduced with 8086。请参阅 wiki 中的链接。显然,预计累加器将一直用于所有事情,和/或他们没有考虑未来的指令集扩展,因此他们认为值得在特殊的 alax 版本上花费大量操作码ALU 立即指令。

如果您查看两操作数整数 ALU 指令(andorsubtest 等),每个指令都有一个特殊的短一字节编码 alax/eax/rax 目的地,带有全尺寸立即操作数。 (即 imm32,而不是 imm8 符号扩展为 32 或 64b)。所以每条指令有两个额外的操作码。

这只影响 x86 代码大小。一旦指令被解码,它们的运行方式就没有进一步的区别。请参阅http://agner.org/optimize/ 以了解有关 CPU 内部结构的更多信息。

AMD64 本来可以将这些排除在 64 位模式之外,从而释放更多的编码空间,但他们可能对淘汰 32 位并不乐观。如果您希望指令在 32 位和 64 位模式下工作,则在两种模式下的编码相同的情况下,需要较少的解码器晶体管。不过,他们本可以使用setcc r32 或其他东西的编码空间。不喜欢新的 SIMD 功能,只是取消一些基本指令。在标志设置操作之前,您几乎不能在没有xor 的情况下使用setcc 将完整寄存器归零。无论如何,AMD 错过了从 x86 中删除一些垃圾的黄金机会。


有趣的事实:在 Broadwell / Skylake(以及更高版本?)上,直接编码为 adc 的特殊情况 AL/AX/EAX/RAX 实际上更慢。见Which Intel microarchitecture introduced the ADC reg,0 single-uop special case?

这也可能适用于早期 Sandybridge / Haswell 上的 adc al,0。 (adc eax, 0 不会使用这种编码。)

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2014-10-16
    • 2013-02-17
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多