C/C++中使用常量优化的函数调用
Function calls with constants optimization in C/C++
如果你有一个带有常量的函数调用,它没有副作用,也不依赖于任何东西,比如:
int foo(int a,int b){返回a+b;}
函数是否内联?或者,也许是在编译时对函数求值,并将求值结果插入函数调用的位置?
我尝试使用一个相当旧的gcc-编译这个
#include <iostream>
int foo(int a,int b)
{
return a+b;
}
int main()
{
std::cout << foo(100, 123) ;
}
并且主要编译到这个-
LFB1439:
subq $8, %rsp
.LCFI1:
movl $223, %esi
movl $_ZSt4cout, %edi
call _ZNSolsEi
xorl %eax, %eax
addq $8, %rsp
ret
因此,它在编译时编译了加法,得到223。
显然,结果取决于您的代码和编译器,但这表明它可以并且可以在编译时内联和计算加法(如果可以的话)。
不在C++中。它们不会像那样在编译时执行——除非编译器神奇地做到了。然而,这是不能强迫的。
然而,对于C++11,您可以使用constexpr
来确保在编译时对其进行评估,例如:
constexpr int get_five() {return 5;}
因此,您可以将函数重写为:
constexpr int foo(int a,int b)
{
return a+b;
}
请注意,如果此函数的参数不总是常量,则不必担心。
来自维基百科:
如果使用以下参数调用constexpr函数或构造函数不是常量表达式,调用的行为就像函数不是constexpr,并且得到的值不是常量表达式。同样,如果constexpr的return语句中的表达式函数的计算结果不是特定的常量表达式调用时,结果不是常量表达式。
这意味着foo(1,1)
将是常数,但是:
int i,j;
cin >> i >> j;
foo(i,j) // this is not constant
参考:http://en.wikipedia.org/wiki/C%2B%2B11#constexpr_-_通用常量表达式
如果在头文件中定义了它,那么它很有可能被内联。如果使用积分编译时常数作为其参数,那么编译器应该能够在编译时执行该函数。
即使没有这样的保证,你也应该相信你的编译器。他们非常擅长优化您的代码。如果要确保函数在编译时执行,可以添加constexpr
重载(仅限C++11):
constexpr int foo(int a,int b){
return a+b;
}
我尝试了以下片段:
int add(int a, int b) {
return a + b;
}
int main() {
return add(5, 2);
}
当使用GCC和-O3标志编译时,它被编译为:
0x08048300 <+0>: mov $0x7,%eax
0x08048305 <+5>: ret
因此,您可以看到它实际上是在编译时执行的。
是否执行此类优化不是C和C++语言的定义部分。从本质上讲,只要生成的代码根据源代码有效,编译器就可以自由地进行优化。在一般情况下,在更高的优化级别上,此调用可以是内联的,或者如果调用站点总是传入常量(编译时已知的值),则可以在编译时计算结果,并且完全避免任何运行时开销。
优化编译器选择不内联函数的一般情况是:
- 递归函数
- 当它倾向于大小而非速度时,或者如果内联会大大扭曲最终二进制文件的大小
需要注意的另一个问题是,内联将更改函数的链接。
使用-O3
:在GCC和G++上编译以下C代码
int foo(int a, int b) {
return a+b;
}
int main(void)
{
return foo(1, 2);
}
产生以下汇编代码:
00000000004004e0 <main>:
main():
4004e0: b8 03 00 00 00 mov $0x3,%eax
4004e5: c3 retq
您可以检查程序集列表,看看它是否内联,但如前所述,它是特定于编译器的。
这取决于编译器和优化设置,但通常情况下,当您打开至少一点优化时,您可以假设任何足够高级的编译器都会内联这样一个微不足道的函数。
当您希望确保函数是内联的时,您可以始终使用内联关键字声明它:
inline int foo(int a,int b){
return a+b;
}
但通常应该避免使用这种善意的提示,因为大多数编译器比大多数程序员更善于决定内联哪些函数。
编译时如何计算此函数的可能场景:
1) 编译器在一个内联优化阶段内联foo
函数
2) 在常量传播优化阶段,编译器可以"传播"编译时已知的变量值,即常量和常量表达式。
注意:在看到程序的汇编代码之前,您永远不会确切知道函数是否内联。即使使用inline
说明符。编译器可以忽略此说明符或没有此说明符的内联函数。
- 为什么乘以常量有符号整数分数没有优化?
- 如何防止优化静态常量
- C++全局常量数组:是否保证合并(优化)到一个副本中
- 常量右值引用是否允许对编译器进行额外优化?
- 优化开关(x),x是作为参数传递的常量数
- GCC可以优化具有编译时常量变量的类的方法吗
- C++.优化使用大量常量字符串的程序
- C/C++中使用常量优化的函数调用
- 如何通过常量优化数组乘法
- 当函数参数是常量引用临时或按值复制的临时时,为什么 MSVC 优化会破坏 SSE 代码
- 优化器是否会根据编译时常量推导出数学表达式
- 可变成员是否禁用不可变成员的常量优化
- 循环中的常量条件:编译器优化
- 常量指针C++和优化,它们更快吗
- 带有两个常量的C++:pow()优化
- 编译器是否会将这个表达式优化为一个临时常量,而不是每次迭代都解析它
- C++常量引用参数优化
- C/ c++编译器会对交换运算符(例如:+,*)重新排序以优化常量吗?
- c++ -常量和优化
- 在循环中有常量函数返回值的优化吗?