eXX*_*XX2 6 c++ optimization assembly gcc
我试图用三元运算符的语义引入泛型函数:E1 ? E2 : E3.我看到编译器能够消除三元运算符之一E2或E3取决于E1条件的计算.然而,GCC在ternary函数调用的情况下错过了这种优化(即使E2/ E3没有副作用).
在下面的清单中ternary,写入函数的行为类似于三元运算符.然而,GCC发出可能对函数的大量调用,f这似乎可以消除某些输入值(确切地说它是如何为三元运算符完成的)因为f使用纯属性声明 - 请查看GCC生成的汇编代码的godbolt链接.
它是否可以在GCC(优化空间)中得到改进,或者C++标准是否明确禁止这种优化?
// Very heavy function
int f() __attribute__ ((pure));
inline int ternary(bool cond, int n1, int n2) {
return cond ? n1 : n2;
}
int foo1(int i) {
return i == 0 ? f() : 0;
}
int foo2(int i) {
return ternary(i == 0, f(), 0);
}
Run Code Online (Sandbox Code Playgroud)
装配清单-O3 -std=c++11:
foo1(int):
test edi, edi
jne .L2
jmp f()
.L2:
xor eax, eax
ret
foo2(int):
push rbx
mov ebx, edi
call f()
test ebx, ebx
mov edx, 0
pop rbx
cmovne eax, edx
ret
Run Code Online (Sandbox Code Playgroud)
我看到,对于三元运算符,编译器能够根据E1条件(只要E2/E3没有副作用)消除E2或E3中的一个的计算.
编译器不会消除它; 它从来没有在一开始就优化它cmov. C++抽象机器不评估三元运算符的未使用侧.
int a, b;
void foo(int sel) {
sel ? a++ : b++;
}
Run Code Online (Sandbox Code Playgroud)
像这样编译(Godbolt):
foo(int):
test edi, edi
je .L2 # if(sel==0) goto
add DWORD PTR a[rip], 1 # ++a
ret
.L2:
add DWORD PTR b[rip], 1 # ++b
ret
Run Code Online (Sandbox Code Playgroud)
cmov如果两个输入都没有任何副作用,则三元运算符只能优化为asm .否则他们就不完全等同.
在C++抽象机器(即gcc优化器的输入)中,你foo2总是会调用f(),而你的调用foo1则不然. foo1编译它的方式就不足为奇了.
为了让foo2以这种方式进行编译,它必须优化掉调用f(). 总是调用它来创建一个arg ternary().
这里有一个错过优化,您应该报告GCC的bugzilla(使用missed-optimization关键字作为标记). https://gcc.gnu.org/bugzilla/enter_bug.cgi?product=gcc
呼叫int f() __attribute__ ((pure)); 应该能够被优化掉.它可以读取全局变量,但不能有任何副作用. (https://gcc.gnu.org/onlinedocs/gcc/Common-Function-Attributes.html)
正如@melpomene在评论中发现的那样,int f() __attribute__ ((const));确实为您提供了所需的优化.一个__attribute__((const))功能甚至无法读取全局变量,只有它ARGS.(因此没有args,它必须始终返回一个常量.)
HVD指出gcc没有任何费用信息f().即使它可能已经优化了调用((pure)) f()以及((const)) f,也许它没有,因为它不知道它比条件分支更昂贵?可能使用配置文件引导优化进行编译会说服gcc做些什么?
但鉴于它调用了((const)) f条件foo2,gcc可能只是不知道它可以优化对((pure))函数的调用?也许它只能CSE它们(如果没有写过全局变量),但是不能完全从基本块中进行优化?或许当前的优化器无法利用.就像我说的,看起来像一个错过选择的错误.
| 归档时间: |
|
| 查看次数: |
302 次 |
| 最近记录: |