何时使用内联函数,何时不使用它?

我知道内联是对编译器的提示或请求,用于避免函数调用开销。

那么,在什么基础上可以确定一个函数是否是内联的候选人? 在哪种情况下应该避免内联?< / p >
125290 次浏览

我通常遵循一个拇指规则,我用3-4个简单的语句作为内联函数。但最好记住,这只是给编译器的一个提示。最终是否使它内联的调用仅由编译器执行。如果有超过这些语句,我不会用一个愚蠢的编译器进行内联声明,这可能会导致代码膨胀。

内联函数可能通过消除将参数推入堆栈的需要来提高代码性能。 如果有问题的函数是你代码的关键部分,你应该在你的项目的优化部分做出内联而不是内联的决定,

你可以在c++常见问题解答中阅读更多关于内联的内容

告诉编译器内联函数是一种优化,而最重要的优化规则是过早的优化是万恶之源。始终写清晰的代码(使用高效的算法),然后分析你的程序,只优化那些花费太长时间的函数。

如果您发现一个特定的函数非常简短,并且在一个紧凑的内部循环中被调用了数万次,那么它可能是一个很好的候选者。

不过,您可能会感到惊讶——许多c++编译器会自动为您内联小函数——而且它们可能也会忽略您的内联请求。

最好的方法是分析你的程序,并将被多次调用并消耗CPU周期的小函数标记为inline。这里的关键字是“小”——一旦函数调用开销与函数中花费的时间相比可以忽略不计,那么内联它们就没有意义了。

我建议的另一种用法是,如果你有一些小函数在性能关键代码中经常被调用,以至于缓存不相关,你可能也应该内联这些函数。同样,这也是侧写师应该能够告诉你的。

最好的方法是检查和比较生成的内联和非内联指令。然而,省略inline总是安全的。使用inline可能会导致你不想要的麻烦。

避免函数调用的开销只是故事的一半。

做的事:

  • 使用inline代替#define
  • 非常小的函数是inline的良好候选:更快的代码和更小的可执行文件(更多的机会留在代码缓存中)
  • 函数很小,而且经常被调用

别:

  • 大函数:会导致更大的可执行文件,这会显著降低性能,而不管调用开销会导致更快的执行
  • 受I/O限制的内联函数
  • 该函数很少使用
  • 构造函数和析构函数:即使为空,编译器也会为它们生成代码
  • 在开发库时破坏二进制兼容性:
    • 内联现有函数
    • 更改内联函数或使内联函数变为非内联:以前版本的库调用旧的实现
    • 李< / ul > < / >

    在开发一个库的时候,为了让一个类将来可以扩展,你应该:

    • 即使主体为空,也要添加非内联虚析构函数
    • 使所有构造函数都是非内联的
    • 除非类不能按值复制,否则编写复制构造函数和赋值操作符的非内联实现

    记住,inline关键字是对编译器的一个提示:编译器可能决定不内联某个函数,也可能决定内联那些一开始就没有标记为inline的函数。我通常避免标记函数inline(可能在编写非常非常小的函数时分开)。

    关于性能,明智的方法是(一如既往)分析应用程序,然后最终inline一组表示瓶颈的函数。

    引用:


    编辑:Bjarne Stroustrup, c++编程语言:

    函数可以定义为inline。例如:

    inline int fac(int n)
    {
    return (n < 2) ? 1 : n * fac(n-1);
    }
    

    inline说明符是对编译器的一个提示,它应该尝试内联生成fac()调用的代码,而不是一次性为函数设置代码,然后通过通常的函数调用机制进行调用。聪明的编译器可以为调用fac(6)生成常量720。相互递归的内联函数、递归或不依赖于输入的内联函数的可能性,使得不可能保证inline函数的每次调用实际上都是内联的。编译器的聪明程度是无法规定的,所以一个编译器可能生成720,另一个生成6 * fac(5),还有一个生成非内联调用fac(6)

    为了在没有特别聪明的编译和链接工具的情况下实现内联,内联函数的定义(而不仅仅是声明)必须在作用域内(§9.2)。inline表示符不影响函数的语义。特别是,内联函数仍然有唯一的地址,因此内联函数的static变量(§7.1.2)也有唯一的地址。

    EDIT2: ISO-IEC 14882-1998, 7.1.2功能说明

    带有inline说明符的函数声明(8.3.5,9.3,11.4)声明了一个内联函数。内联说明符向实现指示,在调用点对函数体进行内联替换将优先于通常的函数调用机制。实现不需要在调用点执行这种内联替换;然而,即使省略了这个内联替换,7.1.2中定义的内联函数的其他规则仍应遵守。

过早的优化是万恶之源!

根据经验,我通常只内联“getter”和“setter”。一旦代码可以工作并且稳定,分析就可以显示哪些函数可以从内联中受益。

另一方面,大多数现代编译器都有很好的优化算法,并且会内联您应该内联的内容。

推理——编写内联单行函数,以后再考虑其他函数。

在决定是否使用内联时,我通常记住以下想法:在现代机器上,内存延迟可能是比原始计算更大的瓶颈。众所周知,经常调用的内联函数会增加可执行文件的大小。此外,这样的函数可以存储在CPU的代码缓存中,当需要访问代码时,这将减少缓存失败的数量。

因此,您必须自己决定:内联是否会增加或减少生成的机器代码的大小?调用该函数会导致缓存丢失的可能性有多大?如果它遍布整个代码,那么我认为可能性很高。如果它被限制在一个单一的紧密循环,那么可能性很低。

我通常使用内联的情况下,我列出如下。然而,当您真正关心性能时,概要分析是必不可少的。此外,您可能希望检查编译器是否真的接受了提示。

  • 在紧密循环中调用的简短例程。
  • 非常基本的访问器(get / set)和包装器函数。
  • 不幸的是,头文件中的模板代码会自动获得内联提示。
  • 像宏一样使用的短代码。(例如min() / max())
  • 简短的数学程序。

我经常使用内联函数不是为了优化,而是为了使代码更具可读性。有时代码本身比注释、描述性名称等更短、更容易理解。例如:

void IncreaseCount() { freeInstancesCnt++; }

读者可以立即了解代码的完整语义。

inline与优化几乎没有关系。inline是对编译器的指令,如果给定的函数定义在程序中多次出现,则不会产生错误,并承诺该定义将在使用它的每次翻译中出现,并且在它出现的任何地方都具有完全相同的定义。

根据上述规则,inline适用于短函数,其函数体不需要包含超出声明所需的额外依赖项。每次遇到定义时,都必须对其进行解析,并可能生成其主体的代码,因此对于在单个源文件中只定义一次的函数,这意味着一些编译器开销。

编译器可以内联(即用执行该函数的操作的代码替换对该函数的调用)它选择的任何函数调用。过去的情况是,它“显然”不能内联一个没有在同一个翻译单元中声明的函数,但随着链接时间优化的使用越来越多,现在这种情况也不存在了。同样正确的事实是,标记为inline的函数可能不能内联。

此外,在维护大型项目时,内联方法有严重的副作用。当内联代码被更改时,所有使用它的文件将由编译器自动重建(如果它是一个好的编译器)。这可能会浪费大量的开发时间。

inline方法被转移到源文件并且不再内联时,整个项目必须重新构建(至少这是我的经验)。当方法转换为内联时也是如此。

我读了一些答案,发现有些东西缺失了。

我使用的规则是不使用内联,除非我希望它是内联的。看起来很傻,现在解释一下。

编译器足够聪明,短函数总是内联的。永远不要让长函数作为内联函数,除非程序员说这样做。

我知道内联是一个提示或请求编译器

实际上inline是编译器的顺序,它没有选择,并且在inline关键字之后使所有代码内联。所以你永远不能使用inline关键字,编译器将设计最短的代码。

那么什么时候使用inline呢?

如果您想内联一些代码,则使用。我只知道一个例子,因为我只在一种情况下使用它。即用户认证。

例如,我有这样一个函数:

inline bool ValidUser(const std::string& username, const std::string& password)
{
//here it is quite long function
}

不管这个函数有多大,我都想把它作为内联的,因为它使我的软件更难破解。

只有当函数代码很小时,才应该使用内联函数限定符。如果函数比较大,则应该使用普通函数,因为节省内存空间可以换取相对较小的执行速度牺牲。

当你认为你的代码足够小,可以作为内联使用,记住内联函数复制你的代码,并在函数被调用时粘贴它,这样可能会增加你的执行时间,但也会增加内存消耗。 当你使用循环/静态变量/递归/切换/goto/虚函数时,你不能使用内联函数。 虚的意思是等到运行时,内联的意思是在编译期间,所以它们不能同时使用。< / p >