我应该何时在C中为函数/方法编写关键字 inline ?
inline
在看到一些答案后,一些相关的问题:
我应该什么时候 not 为C中的函数/方法写一个关键字'inline'?
编译器什么时候不知道何时使函数/方法'内联'?
当一个应用程序为函数/方法写'inline'时 multithreaded 是否重要?
哦,男人,我的一个宠儿小便 .
inline 更像是 static 或 extern ,而不是告诉编译器内联函数的指令 . extern , static , inline 是链接指令,几乎完全由链接器使用,而不是编译器 .
static
extern
据说 inline 提示编译器你认为函数应该内联 . 这可能是在1998年,但十年后编译器不需要这样的提示 . 更不用说人类在优化代码时通常是错误的,因此大多数编译器都会忽略'hint' .
static - 变量/函数名称不能用于其他翻译单元 . 链接器需要确保它不会意外地使用来自另一个翻译单元的静态定义的变量/函数 .
extern - 在此翻译单元中使用此变量/函数名称,但不要定义't complain if it isn't . 链接器将对其进行排序,并确保尝试使用某些extern符号的所有代码都具有其地址 .
inline - 此功能将在多个翻译单元中定义,不用担心 . 链接器需要确保所有转换单元都使用变量/函数的单个实例 .
Note: 通常,声明模板 inline 是没有意义的,因为它们已经具有 inline 的链接语义 . 但是, explicit 要使用的模板require inline的特化和实例化 .
explicit
您问题的具体答案:
我应该何时为C中的函数/方法写一个关键字'inline'?
仅当您希望在 Headers 中定义函数时 . 更确切地说,只有当函数的定义可以显示在多个翻译单元中时 . 最好在头文件中定义小(如在一个线性中)函数,因为它为编译器提供了更多信息,以便在优化代码时使用 . 它还会增加编译时间 .
我什么时候不应该为C中的函数/方法写关键字'inline'?
不要仅仅因为您认为如果编译器内联它会使代码运行得更快而添加内联 .
通常,编译器将能够比您更好地完成此操作 . 但是,编译器没有函数定义 . 在最大限度优化的代码中,无论您是否要求,通常都会内联所有 private 方法 .
private
另外,为防止在GCC中内联,请使用 __attribute__(( noinline )) ,并在Visual Studio中使用 __declspec(noinline) .
__attribute__(( noinline ))
__declspec(noinline)
当一个应用程序为函数/方法写'内联'时,多线程是否重要?
多线程不会以任何方式影响内联 .
我想通过一个令人信服的例子来为这个主题中的所有重要答案做出贡献,以驱散任何剩余的误解 .
给出两个源文件,例如:
#include <iostream> void bar(); inline int fun() { return 111; } int main() { std::cout << "inline111: fun() = " << fun() << ", &fun = " << (void*) &fun; bar(); }
#include <iostream> inline int fun() { return 222; } void bar() { std::cout << "inline222: fun() = " << fun() << ", &fun = " << (void*) &fun; }
编译:
g++ -std=c++11 inline111.cpp inline222.cpp
输出:
inline111: fun() = 111, &fun = 0x4029a0 inline222: fun() = 111, &fun = 0x4029a0
讨论:
即使你应该对你的内联函数有相同的定义,如果不是这样的话,C编译器也不会标记它(实际上,由于单独的编译它没有办法检查它) . 确保这一点是你自己的责任!
链接器不会抱怨一个定义规则,因为 fun() 被声明为 inline . 但是,因为 inline111.cpp 是由编译器处理的第一个转换单元(实际上调用 fun() ),所以编译器在 inline111.cpp 中的第一次调用遇到时实例化 fun() . 如果编译器决定 not 在程序中的任何其他地方(例如从 inline222.cpp )调用 fun() 时,对 fun() 的调用将始终链接到从 inline111.cpp 生成的实例( inline222.cpp 内部的 fun() 调用也可能产生一个实例翻译单位,但它将保持不相关) . 实际上,从相同的印刷品中可以看出这一点 .
fun()
最后,尽管建议编译器实际扩展单行 fun() ,但它完全忽略了你的建议,这很清楚,因为两行中的 fun() = 111 .
fun() = 111
编译(注意逆序):
g++ -std=c++11 inline222.cpp inline111.cpp
inline111: fun() = 222, &fun = 0x402980 inline222: fun() = 222, &fun = 0x402980
此案例断言 Case A 中讨论过的内容 .
请注意一点,如果您在 inline222.cpp 中注释掉 fun() 的实际调用(例如完全注释掉 cout 中的 cout -statement),那么,尽管你的翻译有编译顺序单位, fun() 将在 inline111.cpp 遇到第一次调用时被实例化,导致 Case B 的打印输出为 inline111: fun() = 111, &fun = 0x402980 .
cout
inline111: fun() = 111, &fun = 0x402980
编译(注意-O2):
g++ -std=c++11 -O2 inline222.cpp inline111.cpp
要么
g++ -std=c++11 -O2 inline111.cpp inline222.cpp
inline111: fun() = 111, &fun = 0x402900 inline222: fun() = 222, &fun = 0x402900
-O2
-fno-inline
&fun
在进行模板特化时,您仍需要显式内联函数(如果专门化在.h文件中)
1)如今,几乎从来没有 . 如果内联函数是个好主意,编译器将在没有您帮助的情况下执行此操作 .
2)总是 . 见#1 .
(编辑反映你把问题分成两个问题......)
如果函数在 .cpp 文件中定义,则不应编写关键字 .
.cpp
什么时候编译器不知道何时使函数/方法'内联'?
没有这种情况 . 编译器无法使函数内联 . 它所能做的就是内联部分或全部函数调用 . 如果它没有得到函数的代码就不能这样做(在这种情况下,链接器需要这样做才能这样做) .
当一个应用程序为函数/方法写'内联'时,是否多线程是否重要?
不,这根本不重要 .
这取决于使用的编译器 . 不要盲目相信现在的编译器比人类更了解如何内联,你不应该出于性能原因使用它,因为它是连接指令而不是优化提示 . 虽然我同意在意识形态上这些论点正确地遇到现实可能是另一回事 .
在阅读了多个线程后,我尝试了好奇内联对我正在工作的代码的影响,结果是我为GCC获得了可测量的加速,并且没有为英特尔编译器加速 .
(更多细节:在类外定义的几个关键函数的数学模拟,GCC 4.6.3(g -O3),ICC 13.1.0(icpc -O3);向关键点添加内联导致GCC代码加速6%) .
因此,如果您将GCC 4.6限定为现代编译器,结果是如果您编写CPU密集型任务并知道确切的瓶颈位置,则内联指令仍然很重要 .
实际上,几乎从来没有 . 你所做的只是建议编译器内联一个给定的函数(例如,替换对该函数/它的主体的所有调用) . 当然没有保证:编译器可能会忽略该指令 .
编译器通常会很好地检测优化这样的事情 .
默认情况下,gcc在没有启用优化的情况下编译时不会内联任何函数 . 我不知道visual studio - deft_code
我通过使用/ FAcs编译并查看汇编代码来检查Visual Studio 9(15.00.30729.01):编译器在 debug 模式下生成了对成员函数的调用而未启用优化 . 即使函数标记为 __forceinline ,也不会生成内联运行时代码 .
你想在返回类型之前把它放在最开头 . 但大多数编译器都忽略它 . 如果已定义,并且它具有较小的代码块,则大多数编译器无论如何都认为它是内联的 .
在开发和调试代码时,请退出 inline . 它使调试变得复杂 .
添加它们的主要原因是帮助优化生成的代码 . 通常,这会增加代码空间以提高速度,但有时 inline 会节省代码空间和执行时间 .
在算法完成之前,对这种关于性能优化的想法是premature optimization .
当一个人应该内联:
1.当要求参数传递,控制传递,控制返回等函数调用时,要避免发生事情的开销 .
2.函数应该很小,经常被调用并且使内联非常有利,因为根据80-20规则,尝试使这些函数内联,这对程序性能有重大影响 .
我们知道内联只是一个类似于寄存器的编译器请求,它将花费你的对象代码大小 .
除非您正在编写库或有特殊原因,否则您可以忘记 inline 并使用链接时优化 . 它消除了一个函数定义必须在头文件中的要求,以便考虑跨编译单元进行内联,这正是 inline 允许 .
(但见Is there any reason why not to use link time optimization?)
12 回答
哦,男人,我的一个宠儿小便 .
inline
更像是static
或extern
,而不是告诉编译器内联函数的指令 .extern
,static
,inline
是链接指令,几乎完全由链接器使用,而不是编译器 .据说
inline
提示编译器你认为函数应该内联 . 这可能是在1998年,但十年后编译器不需要这样的提示 . 更不用说人类在优化代码时通常是错误的,因此大多数编译器都会忽略'hint' .static - 变量/函数名称不能用于其他翻译单元 . 链接器需要确保它不会意外地使用来自另一个翻译单元的静态定义的变量/函数 .
extern - 在此翻译单元中使用此变量/函数名称,但不要定义't complain if it isn't . 链接器将对其进行排序,并确保尝试使用某些extern符号的所有代码都具有其地址 .
inline - 此功能将在多个翻译单元中定义,不用担心 . 链接器需要确保所有转换单元都使用变量/函数的单个实例 .
Note: 通常,声明模板
inline
是没有意义的,因为它们已经具有inline
的链接语义 . 但是,explicit
要使用的模板require inline的特化和实例化 .您问题的具体答案:
仅当您希望在 Headers 中定义函数时 . 更确切地说,只有当函数的定义可以显示在多个翻译单元中时 . 最好在头文件中定义小(如在一个线性中)函数,因为它为编译器提供了更多信息,以便在优化代码时使用 . 它还会增加编译时间 .
不要仅仅因为您认为如果编译器内联它会使代码运行得更快而添加内联 .
通常,编译器将能够比您更好地完成此操作 . 但是,编译器没有函数定义 . 在最大限度优化的代码中,无论您是否要求,通常都会内联所有
private
方法 .另外,为防止在GCC中内联,请使用
__attribute__(( noinline ))
,并在Visual Studio中使用__declspec(noinline)
.多线程不会以任何方式影响内联 .
我想通过一个令人信服的例子来为这个主题中的所有重要答案做出贡献,以驱散任何剩余的误解 .
给出两个源文件,例如:
编译:
输出:
讨论:
即使你应该对你的内联函数有相同的定义,如果不是这样的话,C编译器也不会标记它(实际上,由于单独的编译它没有办法检查它) . 确保这一点是你自己的责任!
链接器不会抱怨一个定义规则,因为
fun()
被声明为inline
. 但是,因为 inline111.cpp 是由编译器处理的第一个转换单元(实际上调用fun()
),所以编译器在 inline111.cpp 中的第一次调用遇到时实例化fun()
. 如果编译器决定 not 在程序中的任何其他地方(例如从 inline222.cpp )调用fun()
时,对fun()
的调用将始终链接到从 inline111.cpp 生成的实例( inline222.cpp 内部的fun()
调用也可能产生一个实例翻译单位,但它将保持不相关) . 实际上,从相同的印刷品中可以看出这一点 .最后,尽管建议编译器实际扩展单行
fun()
,但它完全忽略了你的建议,这很清楚,因为两行中的fun() = 111
.编译(注意逆序):
输出:
讨论:
此案例断言 Case A 中讨论过的内容 .
请注意一点,如果您在 inline222.cpp 中注释掉
fun()
的实际调用(例如完全注释掉cout
中的cout
-statement),那么,尽管你的翻译有编译顺序单位,fun()
将在 inline111.cpp 遇到第一次调用时被实例化,导致 Case B 的打印输出为inline111: fun() = 111, &fun = 0x402980
.编译(注意-O2):
要么
输出:
讨论:
-O2
optimization鼓励编译器实际扩展可以内联的函数(另请注意,-fno-inline
是默认情况下没有优化选项) . 从这里的外印中可以明显看出,fun()
实际上已经内联扩展(根据其在特定翻译单元中的定义),导致两个不同的打印输出 . 尽管如此,仍然只有一个全局链接的fun()
实例(根据标准的要求),从相同的&fun
打印输出可以看出 .在进行模板特化时,您仍需要显式内联函数(如果专门化在.h文件中)
1)如今,几乎从来没有 . 如果内联函数是个好主意,编译器将在没有您帮助的情况下执行此操作 .
2)总是 . 见#1 .
(编辑反映你把问题分成两个问题......)
如果函数在
.cpp
文件中定义,则不应编写关键字 .没有这种情况 . 编译器无法使函数内联 . 它所能做的就是内联部分或全部函数调用 . 如果它没有得到函数的代码就不能这样做(在这种情况下,链接器需要这样做才能这样做) .
不,这根本不重要 .
这取决于使用的编译器 . 不要盲目相信现在的编译器比人类更了解如何内联,你不应该出于性能原因使用它,因为它是连接指令而不是优化提示 . 虽然我同意在意识形态上这些论点正确地遇到现实可能是另一回事 .
在阅读了多个线程后,我尝试了好奇内联对我正在工作的代码的影响,结果是我为GCC获得了可测量的加速,并且没有为英特尔编译器加速 .
(更多细节:在类外定义的几个关键函数的数学模拟,GCC 4.6.3(g -O3),ICC 13.1.0(icpc -O3);向关键点添加内联导致GCC代码加速6%) .
因此,如果您将GCC 4.6限定为现代编译器,结果是如果您编写CPU密集型任务并知道确切的瓶颈位置,则内联指令仍然很重要 .
实际上,几乎从来没有 . 你所做的只是建议编译器内联一个给定的函数(例如,替换对该函数/它的主体的所有调用) . 当然没有保证:编译器可能会忽略该指令 .
编译器通常会很好地检测优化这样的事情 .
默认情况下,gcc在没有启用优化的情况下编译时不会内联任何函数 . 我不知道visual studio - deft_code
我通过使用/ FAcs编译并查看汇编代码来检查Visual Studio 9(15.00.30729.01):编译器在 debug 模式下生成了对成员函数的调用而未启用优化 . 即使函数标记为 __forceinline ,也不会生成内联运行时代码 .
你想在返回类型之前把它放在最开头 . 但大多数编译器都忽略它 . 如果已定义,并且它具有较小的代码块,则大多数编译器无论如何都认为它是内联的 .
在开发和调试代码时,请退出
inline
. 它使调试变得复杂 .添加它们的主要原因是帮助优化生成的代码 . 通常,这会增加代码空间以提高速度,但有时
inline
会节省代码空间和执行时间 .在算法完成之前,对这种关于性能优化的想法是premature optimization .
当一个人应该内联:
1.当要求参数传递,控制传递,控制返回等函数调用时,要避免发生事情的开销 .
2.函数应该很小,经常被调用并且使内联非常有利,因为根据80-20规则,尝试使这些函数内联,这对程序性能有重大影响 .
我们知道内联只是一个类似于寄存器的编译器请求,它将花费你的对象代码大小 .
除非您正在编写库或有特殊原因,否则您可以忘记
inline
并使用链接时优化 . 它消除了一个函数定义必须在头文件中的要求,以便考虑跨编译单元进行内联,这正是inline
允许 .(但见Is there any reason why not to use link time optimization?)