我知道内联是对编译器的提示或请求,用于避免函数调用开销。
那么,在什么基础上可以确定一个函数是否是内联的候选人? 在哪种情况下应该避免内联?
我知道内联是对编译器的提示或请求,用于避免函数调用开销。
那么,在什么基础上可以确定一个函数是否是内联的候选人? 在哪种情况下应该避免内联?
我通常遵循一个拇指规则,我用3-4个简单的语句作为内联函数。但最好记住,这只是给编译器的一个提示。最终是否使它内联的调用仅由编译器执行。如果有超过这些语句,我不会用一个愚蠢的编译器进行内联声明,这可能会导致代码膨胀。
内联函数可以通过消除将参数推入堆栈的需要来提高代码性能。 如果有问题的函数在你的代码的关键部分,你应该在项目的优化部分做出内联而不是内联的决定,
你可以在c++ FAQ中阅读更多关于内联的内容
告诉编译器内联函数是一种优化,而最重要的优化规则是过早的优化是万恶之源。始终写清晰的代码(使用高效的算法),然后分析你的程序,只优化那些花费太长时间的函数。
如果您发现一个特定的函数非常简短,并且在一个紧凑的内部循环中被调用了数万次,那么它可能是一个很好的候选者。
不过,您可能会感到惊讶——许多c++编译器会自动为您内联小函数——而且它们可能也会忽略您的内联请求。
最好的方法是分析你的程序,把那些被多次调用并消耗CPU周期的小函数标记为内联函数。这里的关键字是“小”——一旦函数调用开销与函数中花费的时间相比可以忽略不计,那么内联它们就没有意义了。
我建议的另一种用法是,如果你有一些小函数在性能关键代码中经常被调用,以至于缓存不相关,你可能也应该内联这些函数。同样,这也是侧写师应该能够告诉你的。
避免函数调用的开销只是故事的一半。
do:
使用inline代替#define 非常小的函数适合内联:更快的代码和更小的可执行文件(更多的机会留在代码缓存中) 这个函数很小,并且经常被调用
别:
large functions: leads to larger executables, which significantly impairs performance regardless of the faster execution that results from the calling overhead inline functions that are I/O bound the function is seldom used constructors and destructors: even when empty, the compiler generates code for them breaking binary compatibility when developing libraries: inline an existing function change an inline function or make an inline function non-inline: prior version of the library call the old implementation
在开发一个库的时候,为了让一个类将来可以扩展,你应该:
即使主体为空,也要添加非内联虚析构函数 使所有构造函数都是非内联的 除非类不能按值复制,否则编写复制构造函数和赋值操作符的非内联实现
记住,inline关键字是给编译器的一个提示:编译器可能决定不内联某个函数,也可能决定内联那些一开始就没有标记为内联的函数。我通常避免将函数标记为内联(可能在编写非常非常小的函数时分开)。
关于性能,明智的方法(一如既往)是分析应用程序,然后最终内联一组表示瓶颈的函数。
引用:
内联还是不内联 [9]内联函数 c++的策略/二进制兼容性问题 GotW #33:内联 内联回来的 有效的c++ -项目33:明智地使用内联
编辑:Bjarne Stroustrup, c++编程语言:
函数可以定义为内联的。例如:
inline int fac(int n)
{
return (n < 2) ? 1 : n * fac(n-1);
}
The inline specifier is a hint to the compiler that it should attempt to generate code for a call of fac() inline rather than laying down the code for the function once and then calling through the usual function call mechanism. A clever compiler can generate the constant 720 for a call fac(6). The possibility of mutually recursive inline functions, inline functions that recurse or not depending on input, etc., makes it impossible to guarantee that every call of an inline function is actually inlined. The degree of cleverness of a compiler cannot be legislated, so one compiler might generate 720, another 6 * fac(5), and yet another an un-inlined call fac(6). To make inlining possible in the absence of unusually clever compilation and linking facilities, the definition–and not just the declaration–of an inline function must be in scope (§9.2). An inline especifier does not affect the semantics of a function. In particular, an inline function still has a unique address and so has static variables (§7.1.2) of an inline function.
EDIT2: ISO-IEC 14882-1998, 7.1.2功能说明
使用内联说明符的函数声明(8.3.5,9.3,11.4)声明了一个内联函数。内联说明符向实现指示,在调用点对函数体进行内联替换将优先于通常的函数调用机制。实现不需要在调用点执行这种内联替换;然而,即使省略了这个内联替换,7.1.2中定义的内联函数的其他规则仍应遵守。
过早的优化是万恶之源!
根据经验,我通常只内联“getter”和“setter”。一旦代码可以工作并且稳定,分析就可以显示哪些函数可以从内联中受益。
另一方面,大多数现代编译器都有很好的优化算法,并且会内联您应该内联的内容。
推理——编写内联单行函数,以后再考虑其他函数。
在决定是否使用内联时,我通常记住以下想法:在现代机器上,内存延迟可能是比原始计算更大的瓶颈。众所周知,经常调用的内联函数会增加可执行文件的大小。此外,这样的函数可以存储在CPU的代码缓存中,当需要访问代码时,这将减少缓存失败的数量。
因此,您必须自己决定:内联是否会增加或减少生成的机器代码的大小?调用该函数会导致缓存丢失的可能性有多大?如果它遍布整个代码,那么我认为可能性很高。如果它被限制在一个单一的紧密循环,那么可能性很低。
我通常使用内联的情况下,我列出如下。然而,当您真正关心性能时,概要分析是必不可少的。此外,您可能希望检查编译器是否真的接受了提示。
在紧密循环中调用的简短例程。 非常基本的访问器(get / set)和包装器函数。 不幸的是,头文件中的模板代码会自动获得内联提示。 像宏一样使用的短代码。(例如min() / max()) 简短的数学程序。
我经常使用内联函数不是为了优化,而是为了使代码更具可读性。有时代码本身比注释、描述性名称等更短、更容易理解。例如:
void IncreaseCount() { freeInstancesCnt++; }
读者可以立即了解代码的完整语义。
内联与优化关系很小。内联是给编译器的指令,如果给定的函数定义在程序中多次出现,则不会产生错误,并承诺该定义将出现在使用它的每次翻译中,并且在出现它的任何地方都具有完全相同的定义。
根据上述规则,内联适用于短函数,其函数体不需要包含超出声明所需的额外依赖项。每次遇到定义时,都必须对其进行解析,并可能生成其主体的代码,因此对于在单个源文件中只定义一次的函数,这意味着一些编译器开销。
编译器可以内联(即用执行该函数操作的代码替换对函数的调用)它选择的任何函数调用。过去的情况是,它“显然”不能内联一个没有在同一个翻译单元中声明的函数,但随着链接时间优化的使用越来越多,现在这种情况也不存在了。同样正确的是,标记为内联的函数可能不是内联的。
此外,在维护大型项目时,内联方法有严重的副作用。当内联代码被更改时,所有使用它的文件将由编译器自动重建(如果它是一个好的编译器)。这可能会浪费大量的开发时间。
当内联方法被传输到源文件而不再内联时,整个项目必须重新构建(至少这是我的经验)。当方法转换为内联时也是如此。
我读了一些答案,发现有些东西缺失了。
我使用的规则是不使用内联,除非我希望它是内联的。看起来很傻,现在解释一下。
编译器足够聪明,短函数总是内联的。永远不要让长函数作为内联函数,除非程序员说这样做。
我知道内联是一个提示或请求编译器
实际上,内联是编译器的一个命令,它没有选择,并且在内联关键字之后使所有代码内联。所以你永远不能使用内联关键字和编译器将设计最短的代码。
那么什么时候使用内联呢?
如果您想内联一些代码,则使用。我只知道一个例子,因为我只在一种情况下使用它。即用户认证。
例如,我有这样一个函数:
inline bool ValidUser(const std::string& username, const std::string& password)
{
//here it is quite long function
}
不管这个函数有多大,我都想把它作为内联的,因为它使我的软件更难破解。
当你认为你的代码足够小,可以作为内联使用,记住内联函数复制你的代码,并在函数被调用时粘贴它,这样可能会增加你的执行时间,但也会增加内存消耗。 当你使用循环/静态变量/递归/切换/goto/虚函数时,你不能使用内联函数。 虚的意思是等到运行时,内联的意思是在编译期间,所以它们不能同时使用。