函数调用在C / C ++中使用常量优化

时间:2012-09-07 11:26:50

标签: c++ c optimization compiler-construction

如果你有使用常量的函数调用,它没有副作用,它不依赖于任何东西,如下所示:

int foo(int a,int b)
{         
  return a+b;   
}

该函数是否内联?或者,也许是在编译时评估的函数,插入此评估的结果代替函数调用?

7 个答案:

答案 0 :(得分:7)

我尝试使用相当旧的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。

显然,结果取决于您的代码和编译器,但这表明它可以并且在编译时内联和计算添加,如果可以的话。

答案 1 :(得分:4)

不在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_-_Generalized_constant_expressions

答案 2 :(得分:2)

如果您在头文件中定义它,那么它很有可能被内联。如果你使用整数编译时常量作为参数,那么编译器应该能够在编译时执行该函数。

即使没有这样的保证,您也应该信任您的编译器。他们非常善于优化您的代码。如果要确保在编译时执行该函数,可以添加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

因此,您可以看到它实际上是在编译时执行的。

答案 3 :(得分:1)

是否执行此类优化不是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

答案 4 :(得分:0)

您可以检查程序集列表以查看它是否被内联,但如前所述,它是特定于编译器的。

答案 5 :(得分:0)

这取决于编译器和优化设置,但一般来说,您可以假设任何足够先进的编译器在您至少开启一些优化时都会内联这样一个简单的函数。

如果要确保函数是内联的,可以随时使用inline关键字声明它:

inline int foo(int a,int b){
    return a+b;
} 

但通常应避免使用这些良好的提示,因为大多数编译器在决定内联函数方面比大多数程序员更好。

答案 6 :(得分:0)

可能的情况如何在编译时计算此函数:
1)在内联优化阶段中编译内联foo函数 2)在constant propagation优化阶段,编译器可以“传播”编译时已知的变量值,即常量和常量表达式。

注意:在看到程序的汇编程序代码之前,您永远不会确切地知道函数是否内联。即使您使用inline说明符。如果没有此说明符,编译器可能会忽略此说明符或内联函数。