何时使用内联函数以及何时不使用它?

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

那么在什么基础上可以确定函数是否是内联的候选者? 在这种情况下,应该避免内联?

避免函数调用的成本只是故事的一半。

做:

  • 使用inline而不是#define
  • 非常小的函数适合inline :更快的代码和更小的可执行文件(留在代码caching中的机会更多)
  • function很小而且经常打电话

别:

  • 大的function:导致更大的可执行文件,无论由于调用开销导致执行速度的快慢,都会严重影响性能
  • 内联函数是I / O绑定的
  • 该function很less使用
  • 构造函数和析构函数:即使是空的,编译器也会为它们生成代码
  • 开发库时打破二进制兼容性:
    • 内联一个现有的function
    • 更改内联函数或者使内联函数非内联:库的先前版本调用旧实现

在开发一个图书馆时,为了使这个图书馆在未来具有可扩展性,你应该:

  • 添加非内联虚拟析构函数,即使主体为空
  • 使所有构造函数非内联
  • 编写复制构造函数和赋值运算符的非内联实现,除非类不能被值复制

请记住, inline关键字是编译器的一个提示:编译器可能决定不内联一个函数,它可以决定内inline第一个没有标记的函数。 我通常避免标记函数inline (除了编写非常小的函数)。

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

参考文献:

  • 内联或不内联
  • [9]内联函数
  • 使用C ++的策略/二进制兼容性问题
  • GotW#33:内联
  • 内联Redux
  • 有效的C ++ – 第33项:明智地使用内联

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

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

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

inline说明符是编译器的一个提示,它应该试图为内联调用fac()生成代码,而不是一次性为该函数编写代码,然后通过通常的函数调用机制调用代码。 聪明的编译器可以为呼叫fac(6)生成常数720 。 相互recursion内联函数,recursion或不依赖于input的内联函数等的可能性使得不可能保证inline函数的每个调用都被内联。 一个编译器的聪明程度不能被规定,所以一个编译器可能产生720 ,另一个6 * fac(5) ,而另一个非内联的call fac(6)

为了在没有exception聪明的编译和链接function的情况下进行内联,定义(而不仅仅是内联函数的声明)必须在范围内(§9.2)。 inline especifier不影响函数的语义。 特别是,一个内联函数仍然有一个唯一的地址,所以有一个内联函数的staticvariables(第7.1.2节)。

EDIT2:ISO-IEC 14882-1998,7.1.2函数说明符

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

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

鉴于上述规则, inline适用于短的函数,其主体不需要包含对声明所需的额外依赖。 每遇到定义,都必须进行parsing,并且可以生成其正文的代码,这意味着在一个源文件中只定义一次函数的编译器开销。

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

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

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

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

过早的优化是万恶之源!

作为一个经验法则,我通常只内联“getters”和“setters”。 一旦代码运行并且稳定,分析可以显示哪些函数可以从内联中受益。

另一方面,大多数现代编译器都有相当不错的优化algorithm,并且将embedded你应该为你内联的内容。

重新开始 – 写一行内联函数,稍后再担心别人。

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

我build议的另一个用途是,如果你有足够小的函数在性能严重的代码中被调用,使caching缺失相关,你应该也可以内联这些函数。 再次,这是探查器应该能够告诉你的东西。

内联函数可以通过消除将参数推入堆栈来提高代码性能。 如果有问题的函数处于代码的关键部分,则应该在项目的优化部分进行内联而不是内联决策,

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

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

 void IncreaseCount() { freeInstancesCnt++; } 

读者立即知道代码的完整语义。

我通常遵循一个拇指规则,我用3-4个简单的语句作为内联函数。 但是,记住这只是编译器的一个提示而已。 最后一个调用内联或不内联仅由编译器进行。 如果不止这些许多陈述,我不会像一个愚蠢的编译器一样声明为内联,它可能导致代码膨胀。

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

在决定是否使用内联时,我通常会牢记以下想法:在现代计算机上,内存延迟可能比原始计算瓶颈更大。 内联经常调用的函数已知会增加可执行文件的大小。 而且,这样的函数可以存储在CPU的代码caching中,当代码需要被访问时,这将减lesscaching未命中的次数。

因此,你必须自己决定:内联是增加还是减less生成的机器码的大小? 调用该函数的可能性是否会导致caching未命中? 如果在整个代码中都有这样的话,那么我认为可能性很高。 如果将其限制在一个单一的紧密环路中,那么可能性很低。

我通常在下面列出的情况下使用内联。 但是,如果您真的关心性能,分析是至关重要的。 此外,你可能想要检查编译器是否真的采取了提示。

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

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

当一个inline方法被转移到一个源文件,而不是内联,整个项目必须重build(至less这是我的经验)。 而且当方法转换为内联。

只有当函数代码很小时,才应该使用内联函数限定符。如果函数较大,则应该更喜欢正常的函数,因为内存空间的保存值得牺牲执行速度。

我已经阅读了一些答案,看到有一些东西丢失。

我使用的规则是不使用内联,除非我想要内联。 看起来很傻,现在解释。

编译器足够聪明,而且短的函数总是内联的。 除非程序员说这样做,否则永远不会像在线那样长时间工作。

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

实际上, inline是编译器的命令,它没有select, inline关键字使所有代码内联。 所以你永远不能使用inline关键字,编译器会devise最短的代码。

那么何时使用inline

要使用,如果你想有一些代码内联。 我只知道一个例子,因为我只在一种情况下使用它。 这是用户authentication。

比如我有这个function:

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

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