c++ - 为什么 clang 和 gcc 重复代码和分支 vs 无条件跳转到循环内?

标签 c++ c optimization x86

为什么编译器似乎更喜欢将预测试循环优化为检查、条件跳转,然后是 do-while 结构,而不是在 do while 循环内进行无条件跳转?

我写了一个用我描述的第二种风格编写的函数,但是 g++ 和 clang 都撤消了它并将其转换为方法一。 https://godbolt.org/g/2Dvudi

我很困惑,因为编译器似乎为预测试复制了很多指令(对于这个例子来说可能不是那么多)。此外,它无论如何都可能会跳转(尽管可能静态预测不会被采用,并且在一般情况下没什么大不了的),那么为什么不总是进行无条件跳转呢?

这是我的一个想法,但它并不强烈支持任何一种方法:
循环需要对齐,所以可能有空间在不浪费空间的情况下预先复制一些指令,因为它们会被 nops 填充。然而,clang 和 gcc 都为预测试发出了超过 16 字节的代码,并最终在之后插入了一个大的 nop。

编辑:这是来自 godbolt 链接的代码:

typedef unsigned char uchar;

unsigned my_atoi(const uchar *p)//sentinel at end
{
  unsigned acm=0u;
  unsigned d;
  goto LEnter;
  do{
    acm =  acm*10u + d;
  LEnter:
    d = *p++ - '0';
  }while (d<10u);
  return acm;
}

clang 5.0 at -O2 发出:

my_atoi(unsigned char const*):                          # @my_atoi(unsigned char const*)
    movzx   ecx, byte ptr [rdi]
    add     ecx, -48
    xor     eax, eax
    cmp     ecx, 9
    ja      .LBB0_3
    inc     rdi
    xor     eax, eax
.LBB0_2:                                # =>This Inner Loop Header: Depth=1
    lea     eax, [rax + 4*rax]
    lea     eax, [rcx + 2*rax]
    movzx   ecx, byte ptr [rdi]
    add     ecx, -48
    inc     rdi
    cmp     ecx, 10
    jb      .LBB0_2
.LBB0_3:
    ret

最佳答案

引用来自 GCC sources 的一些评论相关的优化过程。

Duplicates headers of loops if they are small enough, so that the statements in the loop body are always executed when the loop is entered. This increases effectiveness of code motion optimizations, and reduces the need for loop preconditioning.

即,如果后面的过程找到一些循环不变的代码,他们将有一个地方可以移动该代码,而无需添加检查,循环是否会迭代。

For all loops, copy the condition at the end of the loop body in front of the loop. This is beneficial since it increases efficiency of code motion optimizations. It also saves one jump on entry to the loop.

关于c++ - 为什么 clang 和 gcc 重复代码和分支 vs 无条件跳转到循环内?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/46737259/

相关文章:

c++ - 如何区分object和const对象

c - GStreamer 1.0 在 C 中将原始编码为 H.264

c - 结构和指针错误 : dereferencing pointer to incomplete type

c - 如何创建动态大小的 NULL 指针数组?

c - 返回 64 位整数中所有设置位的位置的最快方法是什么?

javascript - Google Sheets 脚本 - 排序速度极慢

c++ - 在 constexpr 中连接 string_views

c++ - 使用 select 多路复用未命名管道和其他文件描述符

c++ - 如何在 MacOS 上的 g++ 中使用 gcc 内置 __sync_bool_compare_and_swap?

Linux 内核 'Reserved' 内存