6

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

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

“编译器比你聪明。它比你更清楚什么时候应该内联函数。永远不要使用 C 数组。总是使用 std::vector!”

- 其他 StackOverflow 用户

真的吗?那么调用 func(5) 并打印结果比打印 32 更快吗?我会盲目地跟着你走出悬崖边上,全知全能,全知全能的 gcc。

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

4

3 回答 3

10

最近的 GCC 能够通过链接时优化(LTO) 跨编译单元内联。您需要编译 - 并链接 - with -flto; 请参阅链接时优化以及内联GCC 优化选项

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

您需要-flto在编译时和链接时显式传递(请参阅this)。如果使用 aMakefile你可以试试make CC='gcc -flto';否则,使用 eg 编译每个翻译单元gcc -Wall -flto -O2 -c src1.c(同样用于src2.cetc...)并将所有程序(或库)链接到gcc -Wall -flto -O2 src1.o src2.o -o prog -lsomelib

请注意,这-flto会显着减慢您的构建速度(它不会被传递,-O3因此您需要显式使用它,并且还需要与它链接)。通常,您可以将构建程序的性能提高 5% 或 10%,但代价是构建时间几乎翻倍。有时您可以获得更多改进。

于 2013-10-22T12:14:48.353 回答
3

编译器不能内联它没有的东西。它需要完整的函数体来内联其代码。

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

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

于 2013-10-22T12:14:34.220 回答
1

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

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

请查阅您的构建工具手册,了解如何打开链接时间代码生成功能(如果它们完全受支持)。

于 2013-10-22T12:19:48.573 回答