将C++;链接器自动内联函数(不带“inline”关键字,头中不带实现)? < > > C++链接器将自动内联在“头”中未定义的“传递”函数,而不是明确要求通过“代码>内联< /COD>关键字”-< /强> “内联”。

将C++;链接器自动内联函数(不带“inline”关键字,头中不带实现)? < > > C++链接器将自动内联在“头”中未定义的“传递”函数,而不是明确要求通过“代码>内联< /COD>关键字”-< /强> “内联”。,c++,optimization,linker,inline,compiler-optimization,C++,Optimization,Linker,Inline,Compiler Optimization,例如,以下情况经常发生,并且应该始终受益于“内联”,因此每个编译器供应商似乎都应该通过链接器“内联”来“自动”处理它(在可能的情况下): 我知道MSVS链接器将通过其链接时代码生成(LTGCC)执行一些“内联”,并且GCC工具链还支持链接时优化(LTO)(请参阅:) 此外,我知道在某些情况下这是无法“内联”的,例如链接器无法“使用”实现(例如,跨越共享库边界,发生单独链接) 但是,如果这段代码链接到一个不跨越DLL/共享库边界的可执行文件中,我希望编译器/链接器供应商能够自动内联该函数,作为一种

例如,以下情况经常发生,并且应该始终受益于“内联”,因此每个编译器供应商似乎都应该通过链接器“内联”来“自动”处理它(在可能的情况下):

我知道MSVS链接器将通过其链接时代码生成(LTGCC)执行一些“内联”,并且GCC工具链还支持链接时优化(LTO)(请参阅:)

此外,我知道在某些情况下这是无法“内联”的,例如链接器无法“使用”实现(例如,跨越共享库边界,发生单独链接)

但是,如果这段代码链接到一个不跨越DLL/共享库边界的可执行文件中,我希望编译器/链接器供应商能够自动内联该函数,作为一种简单而明显的优化(既有利于性能又有利于大小)


我的希望太天真了吗?

是的,如果设置了适当的优化标志,并且编译器认为这是一种性能奖励,那么任何一个好的编译器都完全能够内联该函数


如果您确实想知道,请在调用函数之前添加一个断点,编译程序并查看程序集。如果您这样做,就会非常清楚。

编译后的代码必须能够看到函数的内容,以便有机会进行内联。通过使用unity文件和LTCG,这种情况发生的可能性更大。

以下是我对编译器将如何处理函数的理解:

如果函数定义在类定义内,并且假设不存在阻止“内联”函数的场景(如递归),则函数将为“内联-d”

如果函数定义在类定义之外,则函数将不会是“inline-d”,除非函数定义明确包含inline关键字

这是Ivor Horton的开始Visual C++ 2010:

的摘录。 内联函数

对于内联函数,编译器尝试在函数体中展开代码,而不是调用函数。这避免了调用函数的大部分开销,因此加快了代码的速度

编译器可能并不总是能够插入内联函数的代码(例如递归函数或已获得地址的函数),但通常情况下,它可以工作。它最好用于非常简短的简单函数,例如CBox类中的Volume(),因为这样的函数执行速度更快,插入主体代码不会显著增加可执行模块的大小

当函数定义在类定义之外时,编译器将函数视为普通函数,函数调用将以通常方式工作;但是,也可以告诉编译器,如果可能的话,您希望将函数视为内联函数。这只需将关键字inline放在函数头的开头即可完成。因此,对于该函数,定义如下:

inline double CBox::Volume()
{
    return l * w * h;
}

视情况而定。大多数编译器(实际上是链接器)都支持这种优化。但是为了实现这一点,整个代码生成阶段几乎必须推迟到链接时间。MSVC调用链接时间代码生成(LTCG)选项,默认情况下,它在发布版本IIRC中启用

GCC有一个类似的选项,名称不同,但我不记得哪个-O级别(如果有的话)启用了它,或者是否必须显式启用它

但是,“传统”C++编译器已经单独编译了一个翻译单元,之后链接器只绑定松散的端,确保当翻译单元A调用翻译单元B中定义的函数时,查找正确的函数地址并插入到调用代码中。 如果遵循此模型,则不可能内联在另一个转换单元中定义的函数

这不仅仅是一些“简单”的优化,可以“在飞行中”完成,比如说,循环展开。它需要链接器和编译器合作,因为链接器必须接管编译器通常完成的部分工作


请注意,编译器乐意内联未标记有
inline
关键字的函数。但前提是它知道函数在调用它的站点上是如何定义的。如果它看不到定义,那么就不能内联调用。这就是为什么您通常在头文件中定义这样小的“拟内联”函数,使它们的定义对所有调用者都可见。

下面是对您的示例的快速测试(使用一个只返回
42
MyA::foo()
实现)。所有这些测试都是针对32位目标的——可能会针对64位目标看到不同的结果。还值得注意的是,使用
-flto
选项(GCC)或
/GL
选项(MSVC)可以实现完全优化-只要调用
MyB::foo()
,它就会被
42
替换

根据通用条款(MinGW 4.5.1):

对MyB::foo()的调用未被优化<代码>MyB::foo()本身略微优化为:

Dump of assembler code for function MyB::foo() const:
   0x00401350 <+0>:     push   %ebp
   0x00401351 <+1>:     mov    %esp,%ebp
   0x00401353 <+3>:     sub    $0x8,%esp
=> 0x00401356 <+6>:     leave
   0x00401357 <+7>:     jmp    0x401360 <MyA::foo() const>
然后thunk就跳到了MyA::foo():

同样,这主要(完全?)是由编译器执行的,因为如果您查看链接之前生成的目标代码,
MyB::foo()
被编译成一个简单的跳转到
MyA::foo()

因此,将所有这些归结起来——看起来没有显式调用LTO/LTCG,link
gcc -g -O3 -o test.exe myb.cpp mya.cpp test.cpp
Dump of assembler code for function MyB::foo() const:
   0x00401350 <+0>:     push   %ebp
   0x00401351 <+1>:     mov    %esp,%ebp
   0x00401353 <+3>:     sub    $0x8,%esp
=> 0x00401356 <+6>:     leave
   0x00401357 <+7>:     jmp    0x401360 <MyA::foo() const>
0:000> u myb!MyB::foo
myb!MyB::foo:
001a1030 e9d0ffffff      jmp     myb!ILT+0(?fooMyAQBEHXZ) (001a1005)
myb!ILT+0(?fooMyAQBEHXZ):
001a1005 e936000000      jmp     myb!MyA::foo (001a1040)