函数仅在标题中定义时才会内联。我错过了什么吗?

时间:2013-10-22 12:11:58

标签: c++ c g++ inline compiler-optimization

使用gcc v4.8.1

如果我这样做:

//func.hpp

#ifndef FUNC_HPP
#define FUNC_HPP

int func(int);

#endif

//func.cpp

#include "func.hpp"

int func(int x){
    return 5*x+7;
}

//main.cpp

#include <iostream>

#include "func.hpp"

using std::cout;
using std::endl;

int main(){
    cout<<func(5)<<endl;
    return 0;
}

即使是简单的函数func也不会被内联。原型和/或定义上没有inlineexternstatic__attribute__((always_inline))的组合会改变这一点(显然这些说明符的某些组合会导致它甚至无法编译和/或产生警告,而不是谈论那些)。我使用g++ *.cpp -O3 -o rung++ *.cpp -O3 -S进行程序集输出。当我查看汇编输出时,我仍然看到call func。它似乎只有我能够正确内联函数的方法是拥有原型(可能没有必要)和头文件中函数的定义。如果标题仅包含在整个程序中的一个文件中(例如仅由main.cpp包含),则它将进行编译,并且该函数将被正确内联,甚至不需要inline说明符。如果要将标头包含在多个文件中,则似乎需要inline说明符来解决多个定义错误,这似乎是其唯一目的。该功能当然可以正确内联。

所以我的问题是:我做错了吗?我错过了什么吗?无论发生什么:

&#34;编译器比你聪明。它知道什么时候应该比你更好地内联函数。永远不要使用C数组。始终使用std :: vector!&#34;

- 每个其他StackOverflow用户

真的?所以调用func(5)并打印结果比打印32更快?我会盲目地跟着你离开悬崖的边缘,所有人都知道并且都是明智的gcc。

对于记录,上面的代码只是一个例子。我正在写一个光线跟踪器,当我将我的数学和其他实用程序类的所有代码移动到它们的头文件并使用inline说明符时,我看到了大量的性能提升。对于某些场景,字面上要快10倍。

3 个答案:

答案 0 :(得分:6)

最近的GCC能够通过link-time optimizations(LTO)内联编译单元。您需要编译 - 并链接 - 与-flto;请参阅Link-time optimization and inlineGCC optimize options

(实际上,LTO由链接时编译器的特殊变体lto1完成; LTO通过在目标文件中序列化GCC的一些内部表示来工作,{{1}也使用它们所以lto1会发生什么,当用它编译-flto时,生成的src1.c除了对象二进制文件外还包含GIMPLE表示;当链接 src1.o gcc -flto src*.o“前端”从lto1内部提取GIMPLE表示,几乎全部重新编译......)

您需要在编译时和链接时明确传递src*.o(请参阅this)。如果使用-flto,您可以尝试Makefile;否则,用例如编译每个翻译单元。 make CC='gcc -flto'(同样适用于gcc -Wall -flto -O2 -c src1.c等...)并将您的所有程序(或库)与src2.c

相关联

请注意,gcc -Wall -flto -O2 src1.o src2.o -o prog -lsomelib会显着降低您的构建速度(-flto不会传递它,因此您需要明确使用它,并且您还需要与它链接)。通常,您可以将构建程序的性能提高5%或10%,但代价是构建时间几乎翻倍。有时您可以获得更多改进。

答案 1 :(得分:3)

编译器无法内联它没有的内容。它需要函数的完整主体来内联其代码。

您必须记住,编译器一次只能处理一个源文件(更确切地说,一次只能处理一个翻译单元),并且不知道其他源文件及其中的内容

链接器可能会这样做,因为它看到所有代码,并且一些链接器具有允许一些链接时优化的标志。

答案 2 :(得分:1)

inline关键字只不过是对编译器的建议,“我想要内联这个函数”。它可以忽略这个关键字,甚至没有警告。

为了使您的函数func(...)内联,您的编译器/链接器必须支持某种形式的链接时代码生成(和优化)。因为func()和main()位于不同的代码单元中,所以C ++编译器不能同时看到它们,因此不能在另一个内部内联一个函数。它需要链接器支持这样做。

如果支持链接时间代码gen功能,请参阅构建工具手册。