关于内联性能的问题

时间:2010-09-12 21:48:59

标签: c++ c optimization inline

我有一些关于在C和C ++中使用内联函数的问题。我被告知要在我经常使用的小功能上使用它,但我想知道它是如何工作的。这只是一个例子的片段。

static inline point3D createPoint3D(float x, float y, float z){
   point3D newPosition;
   newPosition.x = x;
   newPosition.y = y;
   newPosition.z = z;
   return newPosition;
}
  1. 它究竟是做什么的,为什么它能帮助代码更快地运行?这是90年代过时的优化吗?

  2. 为什么我只能在小功能上使用它?如果我为大功能做了它会不会很糟糕?

  3. 在大量功能上使用它是不是很糟糕?

4 个答案:

答案 0 :(得分:4)

请参阅C ++常见问题here中的详细信息。引用这个内联函数..

  

当编译器内联扩展函数调用时,函数的代码   被插入到调用者的代码中   流(概念上类似于什么   发生在#define宏中。这个   可以,取决于其他的   事情,提高表现,因为   优化器可以在程序上   整合被调用的代码 - 优化   被调用的代码进入调用者。

第9.3节

  

内联函数可能会让它更快:   如上所示,程序集成   可能会删除一堆不必要的东西   说明,可能会有所作为   跑得更快。

     

内联函数可能会使速度变慢:   内联过多可能会导致代码   臃肿,这可能导致“颠簸”   按需分页的虚拟内存   系统。换句话说,如果   系统可执行的大小太大了   可能会把大部分时间花在外面   到磁盘来获取下一个块   代码。

     

内联函数可能会使其更大:   这是代码膨胀的概念,如   如上所述。例如,如果是   系统每个都有100个内联函数   其中扩展到100个字节   可执行代码,在100中调用   地方,这增加了1MB。是   1MB会导致问题吗?谁   知道,但有可能   最后1MB可能会导致系统崩溃   “捶打”,这可能会减慢事情   下来。

     

内联函数可能会成功   较小:编译器经常生成   更多推送/弹出的代码   寄存器/参数比它要的   内联扩展功能的主体。   这种情况非常小   功能,它也发生在   优化器的大函数   能够删除大量冗余代码   通过程序整合 - 那   是,当优化器能够制作时   大功能小。

     

内联函数可能会导致   颠簸:内联可能会增加   二进制可执行文件的大小,和   这可能会引起颠簸。

     

内联函数可能会阻止   颠簸:工作集大小   (需要的页数   记忆一下即使可能会下降   可执行文件大小上升。当f()   调用g(),代码通常是两个   不同的页面;什么时候编译   程序上整合了代码   g()到f(),代码经常在   同一页。

     

内联函数可能会增加   缓存未命中数:内联可能   导致内循环跨越   多行内存缓存,   这可能会引起骚扰   存储器高速缓存中。

     

内联函数可能会减少   缓存未命中数:内联   通常会改善参考的位置   在二进制代码中,可能   减少缓存行数   需要存储内部的代码   环。这最终可能会导致   CPU绑定的应用程序运行得更快。

     

内联函数可能无关紧要   速度:大多数系统都没有   CPU绑定。大多数系统都是I / O绑定的,   数据库绑定或网络绑定,   意味着系统的瓶颈   整体表现是文件   系统,数据库或网络。   除非您的“CPU计量器”被挂钩   100%,内联函数可能不会   让您的系统更快。 (即使在   CPU绑定系统,内联将有所帮助   只有在瓶颈内使用   本身,瓶颈是   通常只占一小部分   代码。)

     

没有简单的答案:你有   玩它看看什么是最好的。   不要满足于简单的答案   比如,“永远不要使用内联函数”或   “始终使用内联函数”或“使用   内联函数当且仅当   功能小于N行   代码。“这些一刀切的规则   可能很容易写下来,但他们   将产生次优结果。

答案 1 :(得分:4)

  1. 这更像是70年代或(最多)80年代的过时优化。几乎任何有能力的编译器都可以在没有任何帮助的情况下选择内联扩展功能,除了启用优化之外。

  2. 它应该做的是消除调用函数的开销。这对于诸如微小功能之类的东西来说几乎是非常重要的。实际上,这些很常见,即使用C ++实现中等性能,也几乎要求编译器或多或少地自动扩展函数。

  3. 根本不使用它。

  4. 通常不会 - 如上所述,当内联函数有一个好处时,编译器通常可以自动执行此操作。

  5. 需要注意以下两点:1)大多数编译器可以/将生成内联函数而不使用inline关键字,2)大多数编译器可以/将忽略inline关键字,如果他们认为该函数不适合内联扩展(但是,只有FWIW,微软有__forceinline来克服后者,如果你确实确定你比编译器更清楚了。)

答案 2 :(得分:3)

不要担心。 在衡量之前一切都是一样的。一旦衡量,你就不会注意到在没有inline的情况下使用ot编译的版本之间存在很大差异。

1)inline是编译器建议直接在代码流中“内联”函数而不是“调用”它。这绕过了设置堆栈的需要,并做了调用函数所需的其他事务

        NOT INLINE                    INLINE
        ...                           ...
        code                          code
        call fx    -\                 code from fx
        code        |                 code from fx
        call fx   --|                 code from fx
        ...         |                 code
                    |                 code from fx
        code <------/                 code from fx
        ...                           code from fx
        return                        ...

2)在任何地方使用它。编译器很可能会忽略您的建议

3)与2)相同

4)措施。实验和比较

答案 3 :(得分:2)

inline关键字表示您认为此函数非常适合包含对函数的调用。它最适用于较小的函数,因为每次使用它都会在使用点放置函数体的新副本。过度使用可能会大大增加调用代码的大小。

这很有价值,因为有时优化器可以在一个小函数内部看到更好的工作。通过将函数体内联,优化器获得了这个机会。它还改善了执行线程的引用局部性,可以提高指令缓存和管道的性能。

在经典C中,获得此效果的唯一方法是使用宏,但宏具有明显的缺点,即它们是纯文本替换,因此它们将在每次出现时对其每个参数进行求值。替换文本。如何安全地允许宏具有局部变量也是不明显的。

在C ++中,通常允许作为语言的常用习语的小型访问器函数内联,以便在类定义中定义其主体的函数被隐式标记{{1} }。

一个好的优化器会自己决定何时实际使用内联函数以及何时正常调用它,因此对inline的自由标记函数通常没有太大的负面影响。