何时使用内联功能何时不使用?

Ash*_*ish 173 c c++ inline

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

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

Gre*_*osz 198

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

做:

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

别:

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

在开发库时,为了使类在未来可扩展,您应该:

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

请记住,inline关键字是编译器的提示:编译器可能决定不内联函数,并且可以决定内联inline首先未标记的函数.我通常避免标记功能inline(除非编写非常小的功能).

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

参考文献:


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

可以将函数定义为inline.例如:

inline int fac(int n)
{
  return (n < 2) ? 1 : n * fac(n-1);
}
Run Code Online (Sandbox Code Playgroud)

inline说明符是一种提示,它应该尝试为的呼叫生成代码编译器fac()内联,而不是一次奠定了该函数的代码,然后通过常规的函数调用机制调用.聪明的编译器可以720为调用生成常量fac(6).相互递归的内联函数,依赖于输入的递归或不递归的内联函数的可能性使得无法保证inline函数的每次调用都实际内联​​.编译器的聪明程度不能立法,因此一个编译器可能会生成720另一个编译器,而另一个编译器可能会生成6 * fac(5)一个非内联调用fac(6).

为了在没有异常聪明的编译和链接功能的情况下实现内联,内联函数的定义 - 而不仅仅是声明 - 必须在范围内(第9.2节).一个inlineespecifier不影响功能的语义.特别是,内联函数仍然具有唯一的地址,因此static内联函数的变量(第7.1.2节)也是如此.

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

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

  • `inline`不仅仅是编译器的一个提示.它更改了有关多个定义的语言规则.此外,拥有静态数据并不是避免内联函数的铸铁原因.无论函数是否声明为"inline",实现都必须为每个函数静态分配一个静态对象.如果类具有内联构造函数和虚拟析构函数,那么它们仍然是可扩展的.而空括号析构函数是一个虚拟函数,它有时候是一个好主意. (33认同)
  • 就是这样,当Stroustrup写道"内联说明符是对编译器的暗示"时,我很惊讶我因为引用他而受到指责.无论如何,我花了足够的时间尽可能多地参考这个答案 (25认同)
  • 将其称为"对编译器的提示"是不准确的.实际上,如果编译器感觉像它那样可以内联非`inline`函数.如果编译器决定不内联它们,则不会内联`inline`函数.正如Charles Bailey所说,它改变了语言规则.而不是将其视为优化提示,将其视为完全不同的概念更为准确.`inline`关键字告诉编译器允许多个定义,而不是其他任何定义."内联"优化几乎可以应用于任何函数,无论它是否标记为"内联". (12认同)
  • 从某种意义上说,这是一个暗示,功能并不一定最终被内联(但英语不是我的母语).关于标记为"inline"的函数中的静态,结果是函数没有内联:您为调用付出代价,并且包含和调用函数的每个转换单元都获得自己的代码和静态变量副本.在开发库时不内联构造函数和析构函数的原因是与库的未来版本的二进制兼容性 (2认同)
  • @GregoryPakosz:但是我们并非都使用`inline`来获取函数内联。有时我们想要其他好处,例如绕过ODR。 (2认同)

CB *_*ley 56

inline与优化没什么关系.inline如果给定定义的函数在程序中多次出现,并且承诺定义将在每个使用它的转换中发生,并且在它出现的任何地方它将具有完全相同的定义,则指示编译器不产生错误.

鉴于上述规则,inline适用于简短的函数,其主体不需要包括对声明所需的额外依赖性.每次遇到定义时,都必须对其进行解析,并且可能会生成其主体的代码,因此它意味着在单个源文件中仅定义一次的函数会产生一些编译器开销.

编译器可以内联(即用对该函数执行该操作的代码替换对函数的调用)所选择的任何函数调用.过去,它"显然"无法内联未在与调用相同的转换单元中声明的函数,但随着链接时间优化的使用越来越多,即使现在也不是这样.同样正确的是标记的功能inline可能没有内联.

  • @gert128“过早的优化是万恶之源”,如果您担心性能,只需将“-O3”添加到编译器标志中,编译器就会自行找出要内联的内容。不要添加关键字并期望它们使您的代码更快。我听过一个关于优化的讲座,在LLVM工作的讲师说inline关键字和优化没有太大关系。这只是关于语义/语言规则 (3认同)
  • 不知道为什么您的评论获得了如此多的好评,因为性能是使用内联的主要原因。 (2认同)

dma*_*oni 10

告诉编译器内联函数是一种优化,最重要的优化规则是过早优化是所有邪恶的根源.始终编​​写清晰的代码(使用有效的算法),然后分析您的程序,只优化耗时太长的函数.

如果你发现一个特定的函数非常简短,并且它在一个紧密的内循环中被称为成千上万次,它可能是一个很好的候选者.

但是,您可能会感到惊讶 - 许多C++编译器会自动为您编写小函数 - 他们也可能忽略您的内联请求.

  • `inline`关键字如何阻碍"清除代码"?"过早优化"中的关键字是*过早*,而不是*优化.说你应该主动*避免*优化只是垃圾.该引用的要点是您应该避免可能不必要的优化,并对代码产生有害的副作用(例如使其不易维护).我没有看到`inline`关键字如何使代码不易维护,或者如何将它添加到函数中是有害的. (4认同)
  • jalf,有时内联函数会使代码变慢,而不是更快.一个例子是从代码中的几个不同位置调用函数时; 如果函数没有内联,那么当它从另一个地方调用时它可能仍然在指令缓存中,并且分支预测器可能已经被预热了.有些模式总能提高效率,因此使用它们永远不会受到伤害.内联不是其中之一.它通常对性能没有任何影响,它有时会有所帮助,有时甚至会受到伤害.我支持我的建议:首先是个人资料,然后是内联. (2认同)

Kor*_*icz 6

过早优化是万恶之源!

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

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

Reasuming——写内联的单行函数,以后再担心其他的。


Tim*_*sch 5

找出答案的最佳方法是对程序进行概要分析,并标记被多次调用的小函数,并消耗CPU周期为inline。这里的关键字是“ small”(小)-与函数所花费的时间相比,函数调用的开销可以忽略不计,因此内联它们是没有意义的。

我建议的另一种用法是,如果您有一些经常在性能关键代码中调用的小函数足以使高速缓存未命中,那么您也应该内联这些小函数。同样,这是探查器应该能够告诉您的。