函数仅在标题中定义时才会内联。 我错过了什么吗?
使用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 #include "func.hpp" using std::cout; using std::endl; int main(){ cout<<func(5)<<endl; return 0; }
即使是简单的函数func
也不会内联。 原型和/或定义上没有inline
, extern
, static
和__attribute__((always_inline))
的组合改变了这一点(显然这些说明符的某些组合导致它甚至不编译和/或产生警告,而不是讨论那些) 。 我正在使用g++ *.cpp -O3 -o run
和g++ *.cpp -O3 -S
来进行汇编输出。 当我查看程序集输出时,我仍然看到call func
。 它似乎只有我能够正确内联函数的方法是拥有原型(可能没有必要)和头文件中函数的定义。 如果标题仅包含在整个程序中的一个文件中(例如仅由main.cpp
包含),则它将进行编译,并且函数将被正确内联,甚至不需要inline
说明符。 如果要将标头包含在多个文件中,则似乎需要inline
说明符来解决多个定义错误,这似乎是其唯一目的。 该function当然可以正确内联。
所以我的问题是:我做错了吗? 我错过了什么吗? 无论发生什么:
“编译器比你聪明。它知道什么时候应该比你更好地内联函数。永远不要使用C数组。总是使用std :: vector!”
– 每个其他StackOverflow用户
真? 所以调用func(5)并打印结果比打印32更快? 我会盲目地跟着你离开悬崖的边缘,所有人都知道并且都是明智的gcc。
为了记录,上面的代码只是一个例子。 我正在编写一个光线跟踪器,当我将我的数学和其他实用程序类的所有代码移动到它们的头文件并使用inline
说明符时,我看到了大量的性能提升。 对于某些场景,字面上要快10倍。
最近的GCC能够通过链接时优化 (LTO)内联编译单元。 你需要编译 – 并链接 – 与-flto
; 请参阅链接时优化和内联和GCC优化选项 。
(实际上,LTO是在链接时由编译器的特殊变量lto1
完成的; LTO通过在目标文件内部序列化,GCC的一些内部表示,也被lto1
;所以-flto
会发生什么呢?使用它编译src1.c
生成的src1.o
包含对象二进制文件外还包含GIMPLE表示;当与gcc -flto src*.o
链接时 , lto1
“前端”从src*.o
内部提取GIMPLE表示src*.o
并且几乎全部重新编译……)
您需要在编译时和链接时显式传递-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
链接你的所有程序(或库) gcc -Wall -flto -O2 src1.o src2.o -o prog -lsomelib
请注意, -flto
会显着减慢您的构建速度(它不会被-O3
传递,因此您需要明确地使用它,并且您还需要与它链接)。 通常,您可以将构建程序的性能提高5%或10%,但代价是构建时间几乎翻倍。 有时您可以获得更多改进。
编译器无法内联它没有的内容。 它需要函数的完整主体来内联其代码。
您必须记住,编译器一次只能处理一个源文件(更准确地说,一次只能处理一个转换单元 ),并且不知道其他源文件及其中的内容。
链接器可能会这样做,因为它看到所有代码,并且一些链接器具有允许一些链接时优化的标志。
inline关键字只不过是对编译器的建议,“我希望这个函数被内联”。 它可以忽略这个关键字,甚至没有警告。
为了使函数func(…)内联,您的编译器/链接器必须支持某种forms的链接时代码生成(和优化)。 因为func()和main()位于不同的代码单元中,所以C ++编译器不能同时看到它们,因此不能在另一个内部内联一个函数。 它需要链接器支持这样做。
有关如何切换链接时间代码genfunction的信息,请参阅构建工具手册,如果它们完全受支持的话。