什么时候使用内联函数,什么时候不使用?

时间:2022-10-16
本文介绍了什么时候使用内联函数,什么时候不使用?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着跟版网的小编来一起学习吧!

问题描述

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

那么根据什么可以确定一个函数是否适合内联呢?在哪种情况下应该避免内联?

解决方案

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

做:

  • 使用 inline 而不是 #define
  • 非常小的函数非常适合内联:更快的代码和更小的可执行文件(更多机会留在代码缓存中)
  • 这个函数很小并且经常被调用

不要:

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

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

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

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

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

参考文献:

  • 内联或不内联
  • [9] 内联函数
  • C++ 的政策/二进制兼容性问题
  • GotW #33:内联
  • 内联 Redux
  • 有效的 C++ - 第 33 条:明智地使用内联
<小时>

Bjarne Stroustrup,C++ 编程语言:

<块引用>

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

inline int fac(int n){返回 (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 节).

ISO-IEC 14882-1998,7.1.2 功能说明符

<块引用>

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

I know that inline is a hint or request to compiler and its used to avoid function call overheads.

So on what basis one can determine whether a function is a candidate for inlining or not ? In which case one should avoid inlining ?

解决方案

Avoiding the cost of a function call is only half the story.

do:

  • use inline instead of #define
  • very small functions are good candidates for inline: faster code and smaller executables (more chances to stay in the code cache)
  • the function is small and called very often

don't:

  • 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

when developing a library, in order to make a class extensible in the future you should:

  • add non-inline virtual destructor even if the body is empty
  • make all constructors non-inline
  • write non-inline implementations of the copy constructor and assignment operator unless the class cannot be copied by value

Remember that the inline keyword is a hint to the compiler: the compiler may decide not to inline a function and it can decide to inline functions that were not marked inline in the first place. I generally avoid marking function inline (apart maybe when writing very very small functions).

About performance, the wise approach is (as always) to profile the application, then eventually inline a set of functions representing a bottleneck.

References:

  • To Inline or Not To Inline
  • [9] Inline functions
  • Policies/Binary Compatibility Issues With C++
  • GotW #33: Inline
  • Inline Redux
  • Effective C++ - Item 33: Use inlining judiciously

EDIT: Bjarne Stroustrup, The C++ Programming Language:

A function can be defined to be inline. For example:

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 Function specifiers

A function declaration (8.3.5, 9.3, 11.4) with an inline specifier declares an inline function. The inline specifier indicates to the implementation that inline substitution of the function body at the point of call is to be preferred to the usual function call mechanism. An implementation is not required to perform this inline substitution at the point of call; however, even if this inline substitution is omitted, the other rules for inline functions defined by 7.1.2 shall still be respected.

这篇关于什么时候使用内联函数,什么时候不使用?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持跟版网!

上一条:C 或 C++ 中好的 goto 示例 下一条:如何将枚举类型变量转换为字符串?

相关文章

最新文章