【问题标题】:while(i--) optimization by gcc and clang: why don't they use sub / jnc?gcc 和 clang 的 while(i--) 优化:他们为什么不使用 sub / jnc?
【发布时间】:2019-06-14 03:22:45
【问题描述】:

有些人在需要不带计数器或带n-1, ..., 0 计数器的循环时编写此类代码:

while (i--) { ... }

一个具体的例子:

volatile int sink;
void countdown_i_used() {
    unsigned i = 1000;
    while (i--) {
         sink = i;  // if i is unused, gcc optimizes it away and uses dec/jnz
    }
}

在 GCC 8.2 (on the Godbolt compiler explorer) 上,它被编译成

# gcc8.2 -O3 -march=haswell
.L2:
    mov     DWORD PTR sink[rip], eax
    dec     eax                      # with tune=generic,  sub eax, 1
    cmp     eax, -1
    jne     .L2

在 clang (https://godbolt.org/z/YxYZ95) 上,如果不使用计数器,它会变成

if(i) do {...} while(--i);

但如果使用的话,像 GCC 一样会变成

add esi, -1
cmp esi, -1
jnz lp

不过,这似乎是一个更好的主意:

sub esi, 1
jnc lp

这两个编译器为什么不用这种方式?

因为cmp 方式更好?还是因为它们不会以这种方式节省空间而且它们的速度几乎相同?

或者他们只是不考虑这个选项?


更新:即使我写代码使用进位方式(这里我使用add/jc但它是一样的)

bool addcy(unsigned& a, unsigned b) {
    unsigned last_a = a;
    a+=b;
    return last_a+b<last_a;
}
volatile unsigned sink;
void f() {

    for (unsigned i=100; addcy(i, -1); ) {
        sink = i;
    }
}

compiler still compile it as checking equality to -1。但是,如果将 100 替换为输入,the JC code remain

【问题讨论】:

  • 发布minimal reproducible example。还要确保您启用了优化。
  • gcc 在将循环优化到一个宏融合的 uop 开销方面出奇地糟糕。使用for(int i=0 ; i &lt; size ; i++) 循环,即使i 在循环内未使用,您也倾向于得到add / cmp/jcc 而不是dec size/jnz。但是是的 sub / jnc 可能是最佳的;可以在 Sandybridge-family 上进行宏融合。
  • 看起来你在循环中使用i,否则 gcc 会更好。

标签: c performance gcc assembly x86


【解决方案1】:

是的,这是一个错过的优化。英特尔 Sandybridge 系列可以将 sub/jcc 宏融合到单个 uop 中,因此 sub/jnc 可以节省这些 CPU 上的代码大小、x86 指令和 uops。

在其他 CPU 上(例如 AMD,它只能将 test/cmp 与 jcc 融合),这仍然可以节省代码大小,因此至少稍微好一些。没有什么比这更糟了。

报告https://bugs.llvm.orghttps://gcc.gnu.org/bugzilla/ 上的优化错误是个好主意。

【讨论】:

  • 发现 gcc 甚至将 jc 代码编译回 jnz 代码,这是怎么回事?
  • @l4m2:您的更新中没有文字jc,您只有一个编译器可以识别为执行检查的习语。但是使用恒定的行程计数,编译器可以看到循环何时结束并以它显然更喜欢的次优方式编译它。如果您希望编译器开发人员更改 gcc 和 LLVM 以免搞砸,请报告错过的优化错误。
猜你喜欢
  • 2013-06-16
  • 1970-01-01
  • 2013-05-11
  • 1970-01-01
  • 1970-01-01
  • 2018-11-01
  • 1970-01-01
  • 2010-09-12
  • 2021-11-22
相关资源
最近更新 更多