当我检查其他人的代码时,我有时会遇到在头文件中实现的静态内联函数,而不是C文件中的常规函数实现。
例如,cache.h
的{{1}}头文件(https://github.com/git/git/blob/master/cache.h)包含许多此类函数。其中一个复制在下面;
git
我想知道使用静态内联函数与常规函数相比有什么优势。有没有可以用来选择适应哪种风格的指南?
答案 0 :(得分:7)
完成内联以进行优化。然而,一个鲜为人知的事实是inline
也会损害性能:您的CPU具有固定大小的指令缓存,并且内联具有在多个位置复制函数的缺点,这使得指令缓存效率降低。
因此,从性能的角度来看,通常不建议声明函数inline
,除非它们太短以至于它们的调用比它们的执行更昂贵。
将此与此相关:函数调用需要10到30个CPU时间周期(取决于参数的数量)。算术运算通常需要一个周期,但是,来自第一级缓存的内存负载需要三到四个周期。因此,如果你的函数比最多三次内存访问和一些算术的简单序列更复杂,那么内联它就没什么意义了。
我通常采用这种方法:
如果函数就像递增单个计数器一样简单,并且如果它在整个地方使用,我会内联它。这方面的例子很少见,但一个有效的案例是引用计数。
如果某个功能仅在一个文件中使用,我将其声明为static
,而不是inline
。这样,当这样的函数恰好使用一次时,编译器可以看。如果它看到了,它很可能会内联它,无论它有多复杂,因为它可以证明内联没有任何缺点。
所有其他功能既不是static
也不是inline
。
您的问题中的示例是一个边界示例:它包含一个函数调用,因此它看起来太复杂了,无法一眼内联。
然而,memcpy()
函数是特殊的:它被视为语言的一部分而不是库函数。大多数编译器都会内联它,并在大小是一个很小的编译时间常量时对它进行大量优化,这就是相关代码中的情况。
通过该优化,该功能确实简化为简短的序列。我不知道它是否涉及大量内存,因为我不知道复制的结构。如果该结构很小,那么在这种情况下添加inline
关键字似乎是一个好主意。
答案 1 :(得分:2)
inline
允许您在标题中定义函数。
static
使该功能仅在当前翻译单元中可用。
答案 2 :(得分:1)
主要原因是性能:如果使用得当,内联函数可以使编译器生成更高效的代码。
识别性能瓶颈的一个好策略是分析代码。一旦完成,提高性能的最有效方法是关注瓶颈。有许多策略,例如算法改进等。一种这样的策略是简化常用的内联函数。
与其他任何提高性能的尝试一样,需要对结果进行测试,以确保更改真正有益。
答案 3 :(得分:1)
来自Wikipedia:
在C和C ++编程语言中,内联函数是使用关键字
inline
限定的函数;这有两个目的。首先,它用作编译器指令,它建议(但不要求)编译器通过执行内联扩展来替换函数体,即:通过在每个函数调用的地址处插入函数代码,从而保存函数调用的开销。在这方面,它类似于register
存储类说明符,它类似地提供了一个优化提示。
static
关键字也用作封装,因此外面没有文件可以使用此功能。
关于http://en.wikipedia.org/wiki/Inline_function
答案 4 :(得分:0)
内联的潜在优势是,可以避免函数调用,这可以节省一些执行时间和堆栈内存,牺牲了可执行文件的一些空间(如果函数被多次使用)。它还可以通过消除死代码来允许进一步的优化(例如,为无效参数返回错误代码的函数,调用执行相同检查的函数,其中在内联时可以移除第二个相同的检查)。注意,内联作为优化技术和内联定义(由C标准定义)是两个不同的方面:编译器可以内联它看到定义的每个函数,并且可以决定对{{执行实际函数调用。 1}}功能。
在严格符合的程序中声明inline
的每个函数都可以声明为static
。这只是编译器的一个提示,并没有任何语义含义(nb,对于具有外部链接的函数,存在差异)。
有时,inline
被视为宏功能的类型检查替代方案,因此可以被视为服务于某些文档目的。
将函数记录为static inline
并在标题中定义(或至少可能是static
并在标题中定义)非常重要,因为这样的用户标题不能假设在不同的翻译单元中获取函数的地址会产生相同的结果。
如果一个定义应该在标题中(允许内联),我个人更喜欢具有外部链接的内联函数,因为地址比较相等,编译器仍然可以内联,如果它认为它是值得的。