C ++如何确定函数是否具有内联能力并且实际上是?
C++ How to determine if function has capability for being inlined and actually was?
我对 C++ 中的 inline
函数有疑问。我知道类似的问题已经出现过很多次了。我希望我的有点不同。
我知道,当您将某个函数指定为 inline
时,它对编译器来说只是一个 "suggestion"。所以万一:
inline int func1()
{
return 2;
}
Some code later
cout << func1() << endl; // replaced by cout << 2 << endl;
所以这里没有什么神秘之处,但是像这样的情况呢:
inline int func1()
{
return 2;
}
inline int func2()
{
return func1() * 2;
}
inline int func3()
{
return func2() * func1() * 2;
}
And so on...
这些函数中的哪些有机会成为内联的,它是否有益以及如何检查编译器实际做了什么?
Which of these functions have a chance to become inlined
如果执行内联的工具(1) 可以访问函数的定义(= 函数体)...
is it benefitial
...并认为这样做是有益的。如今,优化器的工作是确定内联的意义所在,对于 99.9% 的程序,程序员能做的最好的事情就是远离优化器。剩下的几个案例是像 Facebook 这样的程序,其中 0.3% 的性能损失是 huge 回归。在这种情况下,手动调整优化(连同分析、分析、 和 分析)是可行的方法。
how to check what compiler actually did
通过检查生成的程序集。每个编译器都有一个标志,使其以 "human-readable" 格式输出程序集,而不是(或除此之外)二进制格式的目标文件。
(1) 通常,此工具是编译器,内联是编译步骤的一部分(将源代码转换为 assembly/object 文件)。这也是为什么您可能需要使用 inline
关键字来实际允许编译器内联的唯一原因:因为函数的定义必须在正在编译的翻译单元(=源文件)中可见,而且经常这意味着将函数定义放入头文件中。如果没有 inline
,如果头文件包含在多个翻译单元中,这将导致多重定义错误。
请注意,编译并不是可以进行内联的唯一阶段。当您启用全程序优化(也称为 Link-时间代码生成)时,一旦创建了所有目标文件,就会在 link 时间再进行一次优化。此时,inline
关键字完全无关紧要,因为 linking 可以访问所有函数定义(否则二进制文件不会 link 成功)。因此,这是 从内联中获得最大好处的方法,而无需在编写代码时考虑它。缺点是时间:WPO 需要时间 运行,对于大型项目,可以将 link 时间延长到不可接受的水平(我个人经历过一个有点病态的案例,启用 WPO 需要程序的 link 时间从 7 分钟减少到 46).
认为 inline
只是对编译器的 提示 ,有点像 register
是 旧版本的 C++ 和 C 标准。警告,register
正在被废弃(在 C++17 中)。
Which of these functions have a chance to become inlined, is it benefitial
相信您的编译器 可以做出明智的内联决定。为了使某个特定的调用发生,编译器需要知道被调用函数的主体。您不应该关心编译器是否内联(理论上)。
实际上,使用 GCC 编译器:
inlining is not always improving the performance (e.g. because of CPU cache issues, TLB, branch predictor,等等等等....)
内联决策取决于 批次 optimization options。 -O3
比 -O1
更有可能发生;有许多大师选项(如 -finline-limit=
和其他)来调整它。
请注意各个调用是否内联。很可能第 123 行的某些调用事件如 foo(x)
是内联的,但另一个调用事件(对 same 函数 foo
)如 foo(y)
在其他一些地方,如第 456 行没有内联。
当调试时,您可能希望禁用内联(因为这使调试更方便)。这可以通过 -fno-inline
GCC 优化标志(我经常将其与 -g
一起使用,它要求调试信息)。
always_inline
function attribute"forces"内联,noinline
阻止它。
如果编译和link时使用link时间优化 (LTO)例如 -flto -O2
(或 -flto -O3
),例如在 Makefile
中使用 CXX=g++ -flto -O2
,内联可以发生在多个翻译单元(例如 C++ 源文件)之间。然而,LTO 至少使编译时间加倍(而且通常更糟)并在编译期间消耗内存(那么最好有大量 RAM),并且通常只提高几个百分点的性能(除了这个经验法则的奇怪例外) .
您可能会以不同的方式优化函数,例如与#pragma GCC optimize ("-O3")
或function attributeoptimize
另请查看 profile-guided optimizations with instrumentation options like -fprofile-generate
and latter optimizations with -fprofile-use
with other optimization flags。
如果您对哪些调用是内联的(有时,有些不会)感到好奇,请查看生成的汇编程序(例如,使用 g++ -O2 -S -fverbose-asm
并查看 .s
汇编程序文件),或者使用一些内部 dump options.
代码的可观察行为(性能除外)不应取决于编译器做出的内联决定。换句话说,不要指望内联发生(或不发生)。如果您的代码在有或没有一些优化的情况下表现不同,则很可能存在错误。所以请阅读 undefined behavior。
另请参阅 MILEPOST GCC 项目(使用机器学习技术进行优化)。
我对 C++ 中的 inline
函数有疑问。我知道类似的问题已经出现过很多次了。我希望我的有点不同。
我知道,当您将某个函数指定为 inline
时,它对编译器来说只是一个 "suggestion"。所以万一:
inline int func1()
{
return 2;
}
Some code later
cout << func1() << endl; // replaced by cout << 2 << endl;
所以这里没有什么神秘之处,但是像这样的情况呢:
inline int func1()
{
return 2;
}
inline int func2()
{
return func1() * 2;
}
inline int func3()
{
return func2() * func1() * 2;
}
And so on...
这些函数中的哪些有机会成为内联的,它是否有益以及如何检查编译器实际做了什么?
Which of these functions have a chance to become inlined
如果执行内联的工具(1) 可以访问函数的定义(= 函数体)...
is it benefitial
...并认为这样做是有益的。如今,优化器的工作是确定内联的意义所在,对于 99.9% 的程序,程序员能做的最好的事情就是远离优化器。剩下的几个案例是像 Facebook 这样的程序,其中 0.3% 的性能损失是 huge 回归。在这种情况下,手动调整优化(连同分析、分析、 和 分析)是可行的方法。
how to check what compiler actually did
通过检查生成的程序集。每个编译器都有一个标志,使其以 "human-readable" 格式输出程序集,而不是(或除此之外)二进制格式的目标文件。
(1) 通常,此工具是编译器,内联是编译步骤的一部分(将源代码转换为 assembly/object 文件)。这也是为什么您可能需要使用 inline
关键字来实际允许编译器内联的唯一原因:因为函数的定义必须在正在编译的翻译单元(=源文件)中可见,而且经常这意味着将函数定义放入头文件中。如果没有 inline
,如果头文件包含在多个翻译单元中,这将导致多重定义错误。
请注意,编译并不是可以进行内联的唯一阶段。当您启用全程序优化(也称为 Link-时间代码生成)时,一旦创建了所有目标文件,就会在 link 时间再进行一次优化。此时,inline
关键字完全无关紧要,因为 linking 可以访问所有函数定义(否则二进制文件不会 link 成功)。因此,这是 从内联中获得最大好处的方法,而无需在编写代码时考虑它。缺点是时间:WPO 需要时间 运行,对于大型项目,可以将 link 时间延长到不可接受的水平(我个人经历过一个有点病态的案例,启用 WPO 需要程序的 link 时间从 7 分钟减少到 46).
认为 inline
只是对编译器的 提示 ,有点像 register
是 旧版本的 C++ 和 C 标准。警告,register
正在被废弃(在 C++17 中)。
Which of these functions have a chance to become inlined, is it benefitial
相信您的编译器 可以做出明智的内联决定。为了使某个特定的调用发生,编译器需要知道被调用函数的主体。您不应该关心编译器是否内联(理论上)。
实际上,使用 GCC 编译器:
inlining is not always improving the performance (e.g. because of CPU cache issues, TLB, branch predictor,等等等等....)
内联决策取决于 批次 optimization options。
-O3
比-O1
更有可能发生;有许多大师选项(如-finline-limit=
和其他)来调整它。请注意各个调用是否内联。很可能第 123 行的某些调用事件如
foo(x)
是内联的,但另一个调用事件(对 same 函数foo
)如foo(y)
在其他一些地方,如第 456 行没有内联。当调试时,您可能希望禁用内联(因为这使调试更方便)。这可以通过
-fno-inline
GCC 优化标志(我经常将其与-g
一起使用,它要求调试信息)。always_inline
function attribute"forces"内联,noinline
阻止它。如果编译和link时使用link时间优化 (LTO)例如
-flto -O2
(或-flto -O3
),例如在Makefile
中使用CXX=g++ -flto -O2
,内联可以发生在多个翻译单元(例如 C++ 源文件)之间。然而,LTO 至少使编译时间加倍(而且通常更糟)并在编译期间消耗内存(那么最好有大量 RAM),并且通常只提高几个百分点的性能(除了这个经验法则的奇怪例外) .您可能会以不同的方式优化函数,例如与
#pragma GCC optimize ("-O3")
或function attributeoptimize
另请查看 profile-guided optimizations with instrumentation options like
-fprofile-generate
and latter optimizations with-fprofile-use
with other optimization flags。
如果您对哪些调用是内联的(有时,有些不会)感到好奇,请查看生成的汇编程序(例如,使用 g++ -O2 -S -fverbose-asm
并查看 .s
汇编程序文件),或者使用一些内部 dump options.
代码的可观察行为(性能除外)不应取决于编译器做出的内联决定。换句话说,不要指望内联发生(或不发生)。如果您的代码在有或没有一些优化的情况下表现不同,则很可能存在错误。所以请阅读 undefined behavior。
另请参阅 MILEPOST GCC 项目(使用机器学习技术进行优化)。