ADD 1真的比INC快吗?86

Tyl*_*den 17 optimization performance x86 assembly

我已经阅读了各种优化指南,声称ADD 1比在x86中使用INC更快.这是真的吗?

Ste*_*non 29

在某些微架构上,使用一些指令流INC会产生"部分标志更新停顿"(因为它会在保留其他标志的同时更新一些标志). ADD设置所有标志的值,因此不会冒这样的停顿.

ADD并不总是比INC它快,但它几乎总是至少一样快(在某些较旧的微架构上有一些极端情况,但它们非常罕见),有时速度要快得多.

有关更多详细信息,请参阅英特尔优化参考手册Agner Fog的微架构说明.

  • +1,低级优化的真正高手. (2认同)
  • 无论如何,今天与我开始编程时有很大不同。那时 INC 速度更快。:-) (2认同)
  • 当P4是最新的时,首选`add`.现在P4已经或多或少地被埋没了,在大多数情况下,`inc`是首选,因为它更短,并且以与"add"相同的速度运行.如果你想避免修改进位标志,使用`lea reg,[reg + 1]`不修改*any*标志,避免可怕的部分标志停顿.或者,如果可能,避免在标志生成器和标志使用者之间进行增量.AMD K8到Steamroller和Intel P6/Sandybridge系列都针对不同的标志位分别跟踪标志依赖性.例如CF被自己跟踪,以避免像'inc`这样的虚假代表 (2认同)

Bjö*_*ist 6

虽然这不是一个确定的答案。编写这个 C 文件:

=== inc.c ===
#include <stdio.h>
int main(int argc, char *argv[])
{
    for (int n = 0; n < 1000; n++) {
        printf("%d\n", n);
    }
    return 0;
}
Run Code Online (Sandbox Code Playgroud)

然后运行:

clang -march=native -masm=intel -O3 -S -o inc.clang.s inc.c
gcc -march=native -masm=intel -O3 -S -o inc.gcc.s inc.c
Run Code Online (Sandbox Code Playgroud)

注意生成的汇编代码。相关的clang输出:

mov     esi, ebx
call    printf
inc     ebx
cmp     ebx, 1000
jne     .LBB0_1
Run Code Online (Sandbox Code Playgroud)

相关的 gcc 输出:

mov     edi, 1
inc     ebx
call    __printf_chk
cmp     ebx, 1000
jne     .L2
Run Code Online (Sandbox Code Playgroud)

这证明了 clang 和 gcc 的作者都认为INCADD reg, 1现代架构上更好的选择。

这对你的问题意味着什么?好吧,我相信他们对您阅读的指南的判断,并得出结论,这与由于较短的寄存器编码而节省的一个字节INC一样快ADD,因此更可取。编译器作者只是普通人,所以他们可能会出错,但这不太可能。:)

更多的实验告诉我,如果您不使用该-march=native选项,那么 gcc 将add ebx, 1改为使用。Clang otoh,总是最喜欢公司。我的结论是,当您在 2012ADD年问这个问题时有时更可取,但现在在 2016 年您应该始终使用INC.

  • 您必须添加 `-march=native` 才能让 gcc 使用 `inc` 而不是 `add`。 (3认同)
  • 是的,看看编译器选择什么通常是一个很好的策略。(即使在 2012 年,`inc` 也完全没问题。那时 P4 已经无关紧要了。)我注意到 gcc 的指令成本估计似乎更关注延迟而不是吞吐量。总的来说,也许这是一个很好的策略。例如,即使针对 Haswell 进行调整,它也会使用两条“lea”指令来替换乘以常数。clang 确实更喜欢通过使用 `imul r32, r32, imm` 来乘以小常量来实现代码大小/insn 计数/吞吐量,除非它可以使用单个 LEA 来完成(例如 `lea eax, [rcx+rcx*4]` )。 (2认同)