为什么clang和gcc在循环中重复代码和分支与无条件跳转
Why do clang and gcc duplicate code and branch vs unconditional jump inside loop?
为什么编译器似乎更喜欢将预测试循环优化为检查、条件跳转,然后优化为do-while结构,而不是在do-whil循环内进行无条件跳转?
我写了一个函数,它是用我描述的第二种风格编写的,但g++和clang都撤消了它,并将它转换为方法一。https://godbolt.org/g/2Dvudi
我很困惑,因为编译器似乎为预测试复制了很多指令(在这个例子中可能没有那么多)。此外,它无论如何都可能进行跳跃(尽管可能静态预测不会进行,在一般情况下也没什么大不了的),那么为什么不总是进行无条件跳跃呢?
我对此有一个想法,但它并不强烈支持这两种方法:
循环想要对齐,所以可能有空间在不浪费空间的情况下提前复制一些指令,因为它们会被nop填充。然而,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来源的一些评论。
如果循环的标头足够小,则复制它们,以便语句在进入循环时,总是执行循环主体中的。这提高了代码运动优化的有效性,并减少了需求用于环路预处理。
也就是说,如果后面的过程找到了一些循环不变的代码,他们将有一个地方可以将代码移动到那里,而无需添加检查循环是否会迭代。
对于所有循环,复制前面循环体末尾的条件循环的。这是有益的,因为它提高了代码运动优化。它还可以在进入循环时保存一次跳转。
初始化d
并删除goto
使其不再奇怪。
typedef unsigned char uchar;
unsigned my_atoi(const uchar *p) {//sentinel at end
unsigned acm=0u;
unsigned d=0; // initialized
// goto LEnter;
do{
acm = acm*10u + d;
// LEnter:
d = *p++ - '0';
}while (d<10u);
return acm;
}
xor eax,eax // acm=0
xor ecx,ecx // d=0
data16 data16 nop WORD PTR cs:[rax+rax*1+0x0] // nops, aligns to 16 bytes
lea eax,[rax+rax*4] // *5
lea eax,[rcx+rax*2] // *2+d
movzx ecx,BYTE PTR [rdi] // d=*p
inc rdi // p++
add ecx,0xffffffd0 // d-'0'
cmp ecx,0xa // d<10
jb 4004e0 <my_atoi(unsigned char const*)+0x10>
ret
data16 nop WORD PTR cs:[rax+rax*1+0x0]
main:
xor eax,eax
ret
nop WORD PTR cs:[rax+rax*1+0x0]
nop DWORD PTR [rax]
因此,编译器加倍检查的原因是,您会跳转到一个不对齐的代码中。中央循环看起来足够小,可以放在CPU的循环缓冲区中,但跳到中间可能会污染循环缓冲区。
相关文章:
- C++我的数学有什么问题,为什么我的代码不能正确循环
- 代码在main()中运行,但在函数中出现错误
- 在VS代码中交叉编译Windows与Linux上的MinGW的SDL程序
- 编译包含字符串的代码时遇到问题
- 我在c++代码中生成了一个运行时#3异常
- 如何在linux终端中同时编译和运行c++代码
- 为cl.exe(Visual Studio代码)指定命令行C++版本
- 在Linux for Windows上编译C++代码时出错
- 我的字符计数代码计算错误.为什么
- 孤立代码块在结构中引发异常
- 在编译C++代码(具有dlib和opencv)到WASM时面临问题
- 为什么我的C#代码在调用回C++COM直到Task时会暂停.等待/线程.加入
- 处理小于cpu数据总线的数据类型.(c++转换为机器代码)
- 更改 git 分支名称后,在项目的 Visual Studio 代码中丢失智能感知(建议,转到定义C++
- 代码样式:在 switch/if 语句的分支中重用控件表达式或控制变量
- 为什么clang和gcc在循环中重复代码和分支与无条件跳转
- 使用分支优化 CUDA 代码
- 用无分支代码替换if-else语句
- 为什么在汇编代码中比较一个在范围内的数字时会发生分支
- 帮助编译器优化分支代码序列