cco*_*com 15 c c++ optimization gcc
根据我的经验,有许多代码明确使用内联函数,这需要权衡:
问题是:链接时优化(例如,在GCC中)是否呈现手动内联,例如,在C99中声明一个"内联"函数并提供一个实现,已经过时了?我们是否真的不需要考虑自己内联大多数函数?那些总是从内联中受益的函数呢,例如deg_to_rad(x)?
澄清:我不是在考虑同一个翻译单元中的函数,而是考虑逻辑上应该存在于不同翻译单元中的函数.
更新:我经常看到反对"内联",并建议过时.但是,就个人而言,我确实经常看到明确的内联函数:作为类体中定义的函数.
Cro*_*ey9 11
即使使用LTO,编译器仍然必须使用启发式方法来确定是否为每个调用内联函数(注意它不是按函数决定,而是每次调用).启发式考虑了因素 - 例如它是循环,是否展开循环,函数有多大,全局调用的频率等等.编译器肯定永远无法准确确定代码的调用频率,以及代码扩展是否可能在编译时烧掉特定CPU的指令/跟踪/循环/微代码高速缓存.
配置文件引导优化应该是解决这个问题的一个步骤,但是如果你曾经尝试过,你很可能已经注意到你可以获得0-2%的性能提升,它可以在无论方向!:-)这仍然是一项正在进行中的工作.
如果性能是您的最终目标,并且您真的知道自己在做什么,并且真正对代码进行全面分析,那么真正需要的是一种告诉编译器内联或不内联的方法,而不是每个功能提示.在实践中,我通过使用编译器特定的"force_no_inline"类型提示来处理这个,我不想内联的情况,以及一个单独的"force_inline"副本(或在极少数情况下这个失败的宏)我希望它内联的函数.如果有人知道如何以更清晰的方式使用编译器特定的提示(对于任何C/C++编译器),请告诉我.
要专门解决您的观点:
1.代码变得不那么简洁,而且可维护性稍差.
通常,不 - 它只是一个控制内联方式的关键字提示.但是如果你像我在上一段中描述的那样跳过篮球,那么是的.
2.有时,内联可以大大提高运行时性能.
将编译器留给自己的设备时 - 是的,它肯定可以,但大多数情况下都没有.编译器具有良好的启发式,虽然不总是最佳的内联决策,但仍然很好.特别是对于关键字,编译器可能完全忽略关键字,或者使用关键字作为弱提示 - 一般来说,它们似乎对内联代码有害,因为红色标记了它们的启发式(比如将16k函数内联到一个循环中展开16x).
3.内联是在一个固定的时间点决定的,可能没有对其用途的非常好的预知,或者没有考虑所有(未来)周围环境.
是的,它使用静态分析.动态分析可以来自您的洞察力,您可以手动控制每个呼叫的内联,或理论上来自PGO(仍然很糟糕).
GCC 9 Binutils 2.33实验证明LTO可以内联
对于那些好奇是否ld跨对象文件内联的人,这里有一个快速实验,证实它可以:
主程序
int notmain(void);
int main(void) {
return notmain();
}
Run Code Online (Sandbox Code Playgroud)
notmain.c
int notmain(void) {
return 42;
}
Run Code Online (Sandbox Code Playgroud)
使用LTO编译并反汇编:
gcc -O3 -flto -ggdb3 -std=c99 -Wall -Wextra -pedantic -c -o main.o main.c
gcc -O3 -flto -ggdb3 -std=c99 -Wall -Wextra -pedantic -c -o notmain.o notmain.c
gcc -O3 -flto -ggdb3 -std=c99 -Wall -Wextra -pedantic -o main.out notmain.o main.o
gdb -batch -ex "disassemble/rs main" main.out
Run Code Online (Sandbox Code Playgroud)
反汇编输出:
0x0000000000001040 <+0>: b8 2a 00 00 00 mov $0x2a,%eax
0x0000000000001045 <+5>: c3 retq
Run Code Online (Sandbox Code Playgroud)
所以我们看到没有跳转callq或其他跳转,这意味着调用是跨两个对象文件内联的。
然而,如果没有-flto,我们会看到:
0x0000000000001040 <+0>: f3 0f 1e fa endbr64
0x0000000000001044 <+4>: e9 f7 00 00 00 jmpq 0x1140 <notmain>
Run Code Online (Sandbox Code Playgroud)
那么怎么有一个JMPQ,这意味着调用没有内联。
请注意,编译器选择了 JMPQ,它不会像更简单的 CALLQ 那样进行任何堆栈更改作为优化,我认为这是尾调用优化的一个微不足道的最小情况。
所以是的,如果您正在使用-flto,则无需担心将定义放入标头中以便将它们内联。
在标头中定义的主要缺点是它们可能会减慢编译速度。对于 C++ 模板,您可能还对显式模板实例化感兴趣:显式模板实例化 - 何时使用?
在 Ubuntu 19.10 amd64 上测试。
Ed *_*eal -2
第 33 项 - Scott Myers - 第二版 - 高效的 C++ 浮现在脑海中。
您必须记住关键字 static wrt inline!现在有马蜂窝了!