在C中使用内联函数的优点/缺点是什么?我看到它只会提高编译器输出的代码的性能,但是使用今天的优化编译器,快速CPU,大容量存储器等(不像1980年那样<内存稀缺,一切都必须适合100KB内存)什么他们今天真的有优势吗?
内联是对编译器的建议,可以自由忽略 . 它适用于少量代码 .
如果你的函数是内联的,它基本上插入到函数调用的代码中,而不是实际调用一个单独的函数 . 这可以帮助您提高速度,因为您无需进行实际通话 .
它还协助CPU进行流水线操作,因为它们不必使用调用引起的新指令重新加载管道 .
唯一的缺点是可能增加二进制大小,但只要功能很小,这就不会太重要了 .
我现在倾向于将这些决定留给编译器(好吧,无论如何都是聪明的) . 编写它们的人往往对底层架构有更详细的了解 .
内联函数更快,因为您不需要在堆栈上推送和弹出内容,如参数和返回地址;但是,它确实使你的二进制文件略大 .
它有显着差异吗?在大多数现代硬件上并不明显 . 但它可以产生影响,这对一些人来说已经足够了 .
标记内联内容并不能保证它是内联的 . 这只是对编译器的建议 . 有时它不可能,例如当你有虚函数或有涉及递归时 . 有时编译器只选择不使用它 .
我可以看到这样的情况产生了可检测的差异:
inline int aplusb_pow2(int a, int b) { return (a + b)*(a + b) ; } for(int a = 0; a < 900000; ++a) for(int b = 0; b < 900000; ++b) aplusb_pow2(a, b);
通过在需要的地方内联代码,您的程序将花费更少的时间在函数调用和返回部分 . 它应该使你的代码变得更快,即使它变得更大(见下文) . 内联琐碎的访问器可以是有效内联的一个例子 .
通过将其标记为内联,您可以将函数定义放在头文件中(即它可以包含在多个编译单元中,而不会让链接器抱怨)
它可以使您的代码更大(即,如果您使用内联的非平凡函数) . 因此,它可能会引发分页并破坏编译器的优化 .
它稍微破坏了你的封装,因为它暴露了对象处理的内部(但是,每个"private"成员也会这样) . 这意味着您不能在PImpl模式中使用内联 .
稍微打破了封装2:C内联在编译时解决 . 这意味着如果您更改内联函数的代码,则需要使用它重新编译所有代码以确保它将被更新(出于同样的原因,我避免使用函数参数的默认值)
当在 Headers 中使用时,它会使你的头文件变大,因此会用用户不关心的代码稀释有趣的信息(比如类方法的列表)(这就是我在内部声明内联函数的原因一个类,但会在类主体之后的头文件中定义它,而不会在类主体内部定义它 .
编译器可能会也可能不会内联您标记为内联的函数;它也可能决定内联在编译或链接时没有标记为内联的函数 .
内联的工作方式类似于编译器控制的复制/粘贴,这与预处理器宏完全不同:宏将被强制内联,将污染所有命名空间和代码,不会轻易调试,并且将完成即使编译器会将其裁定为效率低下 .
在类本身内部定义的类的每个方法都被视为"inlined"(即使编译器仍然可以决定不内联它
虚拟方法不应该是无法使用的 . 有时,当编译器可以确切地知道对象的类型(即,对象是在同一函数体内声明和构造)时,甚至虚拟函数也会被内联,因为编译器确切地知道对象的类型 .
模板方法/函数并不总是内联的(它们在 Headers 中的存在不会使它们自动内联) .
"inline"之后的下一步是模板元编程 . 即在编译时你的代码,有时,编译器可以推断出函数的最终结果......所以复杂的算法有时可以简化为一种 return 42 ; 语句 . 这对我来说是极端的内联 . 它在现实生活中很少发生,它使编译时间更长,不会使代码膨胀,并且会使代码更快 . 但是像圣杯一样,不要试图在任何地方应用它,因为大多数处理都无法以这种方式解决......不过,这仍然很酷......:-P
return 42 ;
在古老的C和C中, inline 就像 register :对编译器的可能优化的建议(仅仅是建议) .
inline
register
在现代C中, inline 告诉链接器,如果在不同的翻译单元中找到多个定义(不是声明),它们都是相同的,并且链接器可以自由地保留一个并丢弃所有其他的定义 .
如果在头文件中定义了一个函数(无论多么复杂或"linear"), inline 是必需的,以允许多个源包含它而不会被链接器收到"multiple definition"错误 .
在类中定义的成员函数默认为“内联”,模板函数也是如此(与全局函数相反) .
//fileA.h inline void afunc() { std::cout << "this is afunc" << std::endl; } //file1.cpp #include "fileA.h" void acall() { afunc(); } //main.cpp #include "fileA.h" void acall(); int main() { afunc(); acall(); } //output this is afunc this is afunc
请注意将fileA.h包含在两个.cpp文件中,从而产生两个 afunc() 实例 . 链接器将丢弃其中一个 . 如果未指定 inline ,则链接器将发出抱怨 .
afunc()
内联函数是编译器使用的优化技术 . 可以简单地将内联关键字添加到函数原型中以使函数内联 . 内联函数指示编译器在代码中使用该函数的任何地方插入函数的完整主体 .
Advantages :-
它不需要函数调用开销 .
它还可以在函数调用时节省堆栈上变量push / pop的开销 .
它还节省了函数返回调用的开销 .
它通过利用指令缓存增加了引用的局部性 .
内嵌编译器也可以在指定的情况下应用程序内优化 . 这是最重要的一个,这样编译器现在可以专注于死代码消除,可以给分支预测,感应变量消除等带来更多压力 .
要查看更多信息,可以点击此链接http://tajendrasengar.blogspot.com/2010/03/what-is-inline-function-in-cc.html
我想补充一点,在构建共享库时,内联函数至关重要 . 如果没有内联标记函数,它将以二进制形式导出到库中 . 如果导出,它也将出现在符号表中 . 另一方面,内联函数既不会导出到库二进制文件也不会导出到符号表 .
当要在运行时加载库时,这可能是至关重要的 . 它也可能会遇到二进制兼容感知库 . 在这种情况下不要使用内联 .
在优化期间,即使您没有标记它们,许多编译器也会内联函数 . 如果你知道编译器没有的东西,你通常只需要将函数标记为内联,因为它通常可以自己做出正确的决定 .
inline 允许您将函数定义放在头文件中,并且 #include 该头文件位于多个源文件中,而不违反一个定义规则 .
#include
一般来说,现在任何现代编译器担心内联任何东西都是浪费时间 . 编译器实际上应该通过自己对代码的分析以及传递给编译器的优化标志的规范来优化所有这些注意事项 . 如果您关心速度,请告诉编译器优化速度 . 如果您关心空间,请告诉编译器优化空间 . 正如另一个答案所提到的,如果真的有意义的话,一个体面的编译器甚至会自动内联 .
此外,正如其他人所说,使用内联并不保证内联任何内容 . 如果要保证它,则必须定义宏而不是内联函数来执行此操作 .
何时内联和/或定义宏以强制包含? - 只有当您已经证明并且必须证明已经证明对代码的关键部分的速度有所提高时,才会对应用程序的整体性能产生影响 .
这不仅仅是关于表现 . C和C都用于嵌入式编程,位于硬件之上 . 例如,如果您需要编写中断处理程序,则需要确保代码可以立即执行,而无需交换额外的寄存器和/或内存页 . 那就是内联派上用场的时候 . 好的编译器在需要速度时会自行“内联”,但“内联”会强制它们 .
将函数内联到so库中会遇到同样的麻烦 . 看来内联函数没有编译到库中 . 因此,如果可执行文件想要使用库的内联函数,则链接器会发出“未定义的引用”错误 . (碰巧我用gcc 4.5编译Qt源代码 .
为什么不默认使所有函数内联?因为这是一项工程权衡 . 至少有两种类型的“优化”:加速程序并减小程序的大小(内存占用) . 内联通常会加快速度 . 它消除了函数调用开销,避免推送然后从堆栈中提取参数 . 但是,它也会使程序的内存占用更大,因为现在必须用函数的完整代码替换每个函数调用 . 为了使事情变得更加复杂,请记住CPU将经常使用的内存块存储在CPU的高速缓存中,以实现超快速访问 . 如果你使程序的内存映像足够大,你的程序将无法有效地使用缓存,而在最坏的情况下,内联实际上可能会降低你的程序速度 . 在某种程度上,编译器可以计算出权衡取舍,并且可以做出比你更好的决策,只需查看源代码即可 .
我们的计算机科学教授敦促我们永远不要在c程序中使用内联 . 当被问到为什么时,他善意地解释说使用现代编译器应该检测何时自动使用内联 .
所以是的,内联可以是一种尽可能使用的优化技术,但显然这是已经为你做的事情,无论何时都可以内联函数 .
来自another discussion的结论:
Are there any drawbacks with inline functions?
显然,使用内联函数没有任何问题 .
但值得注意的是以下几点!
过度使用内联实际上可以使程序变慢 . 根据函数的大小,内联它可能会导致代码大小增加或减少 . 内联一个非常小的访问器函数通常会减少代码大小,而内联一个非常大的函数可以大大增加代码大小 . 在现代处理器上,较小的代码通常由于更好地使用指令缓存而运行得更快 . - Google Guidelines
随着函数大小的增加,内联函数的速度优势会逐渐减弱 . 在某些时候,与函数体的执行相比,函数调用的开销变小,并且失去了好处- Source
内联函数可能无法正常工作的情况很少:
对于返回值的函数;如果存在return语句 .
对于不返回任何值的函数;如果存在循环,开关或goto语句 .
如果函数是递归的 . -Source
只有指定了optimize选项时, __inline 关键字才会导致函数内联 . 如果指定了optimize,则是否遵循 __inline 取决于内联优化器选项的设置 . 默认情况下,只要运行优化程序,内联选项就会生效 . 如果指定optimize,则还必须指定noinline选项(如果要忽略 __inline 关键字) . -Source
__inline
14 回答
内联是对编译器的建议,可以自由忽略 . 它适用于少量代码 .
如果你的函数是内联的,它基本上插入到函数调用的代码中,而不是实际调用一个单独的函数 . 这可以帮助您提高速度,因为您无需进行实际通话 .
它还协助CPU进行流水线操作,因为它们不必使用调用引起的新指令重新加载管道 .
唯一的缺点是可能增加二进制大小,但只要功能很小,这就不会太重要了 .
我现在倾向于将这些决定留给编译器(好吧,无论如何都是聪明的) . 编写它们的人往往对底层架构有更详细的了解 .
内联函数更快,因为您不需要在堆栈上推送和弹出内容,如参数和返回地址;但是,它确实使你的二进制文件略大 .
它有显着差异吗?在大多数现代硬件上并不明显 . 但它可以产生影响,这对一些人来说已经足够了 .
标记内联内容并不能保证它是内联的 . 这只是对编译器的建议 . 有时它不可能,例如当你有虚函数或有涉及递归时 . 有时编译器只选择不使用它 .
我可以看到这样的情况产生了可检测的差异:
优点
通过在需要的地方内联代码,您的程序将花费更少的时间在函数调用和返回部分 . 它应该使你的代码变得更快,即使它变得更大(见下文) . 内联琐碎的访问器可以是有效内联的一个例子 .
通过将其标记为内联,您可以将函数定义放在头文件中(即它可以包含在多个编译单元中,而不会让链接器抱怨)
缺点
它可以使您的代码更大(即,如果您使用内联的非平凡函数) . 因此,它可能会引发分页并破坏编译器的优化 .
它稍微破坏了你的封装,因为它暴露了对象处理的内部(但是,每个"private"成员也会这样) . 这意味着您不能在PImpl模式中使用内联 .
稍微打破了封装2:C内联在编译时解决 . 这意味着如果您更改内联函数的代码,则需要使用它重新编译所有代码以确保它将被更新(出于同样的原因,我避免使用函数参数的默认值)
当在 Headers 中使用时,它会使你的头文件变大,因此会用用户不关心的代码稀释有趣的信息(比如类方法的列表)(这就是我在内部声明内联函数的原因一个类,但会在类主体之后的头文件中定义它,而不会在类主体内部定义它 .
内联魔法
编译器可能会也可能不会内联您标记为内联的函数;它也可能决定内联在编译或链接时没有标记为内联的函数 .
内联的工作方式类似于编译器控制的复制/粘贴,这与预处理器宏完全不同:宏将被强制内联,将污染所有命名空间和代码,不会轻易调试,并且将完成即使编译器会将其裁定为效率低下 .
在类本身内部定义的类的每个方法都被视为"inlined"(即使编译器仍然可以决定不内联它
虚拟方法不应该是无法使用的 . 有时,当编译器可以确切地知道对象的类型(即,对象是在同一函数体内声明和构造)时,甚至虚拟函数也会被内联,因为编译器确切地知道对象的类型 .
模板方法/函数并不总是内联的(它们在 Headers 中的存在不会使它们自动内联) .
"inline"之后的下一步是模板元编程 . 即在编译时你的代码,有时,编译器可以推断出函数的最终结果......所以复杂的算法有时可以简化为一种
return 42 ;
语句 . 这对我来说是极端的内联 . 它在现实生活中很少发生,它使编译时间更长,不会使代码膨胀,并且会使代码更快 . 但是像圣杯一样,不要试图在任何地方应用它,因为大多数处理都无法以这种方式解决......不过,这仍然很酷......:-P
在古老的C和C中,
inline
就像register
:对编译器的可能优化的建议(仅仅是建议) .在现代C中,
inline
告诉链接器,如果在不同的翻译单元中找到多个定义(不是声明),它们都是相同的,并且链接器可以自由地保留一个并丢弃所有其他的定义 .如果在头文件中定义了一个函数(无论多么复杂或"linear"),
inline
是必需的,以允许多个源包含它而不会被链接器收到"multiple definition"错误 .在类中定义的成员函数默认为“内联”,模板函数也是如此(与全局函数相反) .
请注意将fileA.h包含在两个.cpp文件中,从而产生两个
afunc()
实例 . 链接器将丢弃其中一个 . 如果未指定inline
,则链接器将发出抱怨 .内联函数是编译器使用的优化技术 . 可以简单地将内联关键字添加到函数原型中以使函数内联 . 内联函数指示编译器在代码中使用该函数的任何地方插入函数的完整主体 .
Advantages :-
它不需要函数调用开销 .
它还可以在函数调用时节省堆栈上变量push / pop的开销 .
它还节省了函数返回调用的开销 .
它通过利用指令缓存增加了引用的局部性 .
内嵌编译器也可以在指定的情况下应用程序内优化 . 这是最重要的一个,这样编译器现在可以专注于死代码消除,可以给分支预测,感应变量消除等带来更多压力 .
要查看更多信息,可以点击此链接http://tajendrasengar.blogspot.com/2010/03/what-is-inline-function-in-cc.html
我想补充一点,在构建共享库时,内联函数至关重要 . 如果没有内联标记函数,它将以二进制形式导出到库中 . 如果导出,它也将出现在符号表中 . 另一方面,内联函数既不会导出到库二进制文件也不会导出到符号表 .
当要在运行时加载库时,这可能是至关重要的 . 它也可能会遇到二进制兼容感知库 . 在这种情况下不要使用内联 .
在优化期间,即使您没有标记它们,许多编译器也会内联函数 . 如果你知道编译器没有的东西,你通常只需要将函数标记为内联,因为它通常可以自己做出正确的决定 .
inline
允许您将函数定义放在头文件中,并且#include
该头文件位于多个源文件中,而不违反一个定义规则 .一般来说,现在任何现代编译器担心内联任何东西都是浪费时间 . 编译器实际上应该通过自己对代码的分析以及传递给编译器的优化标志的规范来优化所有这些注意事项 . 如果您关心速度,请告诉编译器优化速度 . 如果您关心空间,请告诉编译器优化空间 . 正如另一个答案所提到的,如果真的有意义的话,一个体面的编译器甚至会自动内联 .
此外,正如其他人所说,使用内联并不保证内联任何内容 . 如果要保证它,则必须定义宏而不是内联函数来执行此操作 .
何时内联和/或定义宏以强制包含? - 只有当您已经证明并且必须证明已经证明对代码的关键部分的速度有所提高时,才会对应用程序的整体性能产生影响 .
这不仅仅是关于表现 . C和C都用于嵌入式编程,位于硬件之上 . 例如,如果您需要编写中断处理程序,则需要确保代码可以立即执行,而无需交换额外的寄存器和/或内存页 . 那就是内联派上用场的时候 . 好的编译器在需要速度时会自行“内联”,但“内联”会强制它们 .
将函数内联到so库中会遇到同样的麻烦 . 看来内联函数没有编译到库中 . 因此,如果可执行文件想要使用库的内联函数,则链接器会发出“未定义的引用”错误 . (碰巧我用gcc 4.5编译Qt源代码 .
为什么不默认使所有函数内联?因为这是一项工程权衡 . 至少有两种类型的“优化”:加速程序并减小程序的大小(内存占用) . 内联通常会加快速度 . 它消除了函数调用开销,避免推送然后从堆栈中提取参数 . 但是,它也会使程序的内存占用更大,因为现在必须用函数的完整代码替换每个函数调用 . 为了使事情变得更加复杂,请记住CPU将经常使用的内存块存储在CPU的高速缓存中,以实现超快速访问 . 如果你使程序的内存映像足够大,你的程序将无法有效地使用缓存,而在最坏的情况下,内联实际上可能会降低你的程序速度 . 在某种程度上,编译器可以计算出权衡取舍,并且可以做出比你更好的决策,只需查看源代码即可 .
我们的计算机科学教授敦促我们永远不要在c程序中使用内联 . 当被问到为什么时,他善意地解释说使用现代编译器应该检测何时自动使用内联 .
所以是的,内联可以是一种尽可能使用的优化技术,但显然这是已经为你做的事情,无论何时都可以内联函数 .
来自another discussion的结论:
Are there any drawbacks with inline functions?
显然,使用内联函数没有任何问题 .
但值得注意的是以下几点!
过度使用内联实际上可以使程序变慢 . 根据函数的大小,内联它可能会导致代码大小增加或减少 . 内联一个非常小的访问器函数通常会减少代码大小,而内联一个非常大的函数可以大大增加代码大小 . 在现代处理器上,较小的代码通常由于更好地使用指令缓存而运行得更快 . - Google Guidelines
随着函数大小的增加,内联函数的速度优势会逐渐减弱 . 在某些时候,与函数体的执行相比,函数调用的开销变小,并且失去了好处- Source
内联函数可能无法正常工作的情况很少:
对于返回值的函数;如果存在return语句 .
对于不返回任何值的函数;如果存在循环,开关或goto语句 .
如果函数是递归的 . -Source
只有指定了optimize选项时,
__inline
关键字才会导致函数内联 . 如果指定了optimize,则是否遵循__inline
取决于内联优化器选项的设置 . 默认情况下,只要运行优化程序,内联选项就会生效 . 如果指定optimize,则还必须指定noinline选项(如果要忽略__inline
关键字) . -Source