编译器内联调用什么

what calls does the compiler inline?

本文关键字:什么 调用 编译器      更新时间:2023-10-16

你怎么知道什么会或什么不会被编译器内联?

有时我被告知一些小的优化是没有意义的,因为编译器会内联某些调用或计算,其他时候类似的优化似乎被推荐。

是什么规则让我们知道什么时候我们需要或不需要优化这些东西?

查看程序集是否内联是唯一可靠的方法。

是否内联完全依赖于编译器——因为编译器对是否内联有最终决定。

不考虑过早的优化:如果它真的很重要(或者如果你只是好奇),你可以使用特定于编译器的pragmas来强制函数内联/不内联,然后适当地配置文件,看看你是否能做出比编译器更好的决定。

然而,在某些情况下,你可以确定一个函数不能内联:

  • 在编译时不能确定类型的虚调用函数。
  • 递归函数永远不能完全内联,除非最大深度可以静态确定。

这可能看起来有点离题,但我认为这很重要。

是什么规则让我们知道什么时候我们需要或不需要优化这些东西?

我想说,在回答这个问题之前,有两条规则可以发挥作用:

  1. 这可能无关紧要。除非您在发布版本中对代码进行了概要分析,并证明与函数调用相关的开销是代码中的主要瓶颈,否则您最好忘记inline代码的性能含义。

  2. 你很少或根本无法控制编译器将内联或不内联什么。如果编译器认为合适的话,可以忽略inline。有些东西不可能内联。有些平台提供了类似force_inline的语言扩展,但即使是这些也可以忽略不计。

没有固定的规则。甚至还有一些测试可以测试您关于优化的假设。

How can you tell what will or what won't be made inline by the compiler?

你不能:)例如,如果你使用内联关键字,它只是一个提示,编译器没有义务尊重它。然而,如果你需要疯狂的优化,似乎你可能需要在比c/c++更低的级别编码,并使用汇编调用

你已经看过了吗?

例如:

GCC是否内联没有'inline'关键字?

为什么不把所有的东西都标记为内联呢?

你问的问题是非常普遍的——因为它取决于诸如哪个编译器之类的事情,更不用说哪个版本了。听起来你所做的是一种不成熟的优化——你应该分析你的代码,找出减慢速度的地方。这将使"这将被内联吗?"的问题变得没有意义,因为您将看到编译器,内联和其他优化的效果。

没有办法确定,关于内联为什么和何时是好的实践的细节,我建议阅读这个c++ FAQ

您无法知道编译器在优化阶段将内联什么:您应该记住精确编译器的启发式。不过,您可以指定想要内联的内容。

微软的编译器很长一段时间都有/Ob优化标志,强制它内联所有且仅显式声明inline的函数。这是非标准的,默认情况下,它的行为符合标准,即将关键字仅作为提示。Intel Parallel Studio编译器的行为类似。

你怎么知道编译器会把什么变成内联的,什么不会呢?

一般情况下不能。

您可能需要阅读Herb Sutter的Inline Redux文章。它公开了许多技术来解释何时可能发生内联,知道后者会发生……你得到的信息就越多。

例如,在JIT环境中,你可以观察一个循环,意识到它的整个主体都是在一个对象上执行的,这个对象经常是Foo类型的,将整个主体专门化为Foo,这允许虚拟方法的完全内联,现在在循环的顶部检查对象是否是Foo,如果是的话,切换到完全内联的主体版本,而不是具有虚拟调用的通用版本。

那么什么可以内联呢?

一般c++编译器的特性:

  • 编译时间内联(历史)
  • 链接时间内联(LTO优化包的一部分)

这意味着它们只能基于静态(在编译时已知)信息进行优化。

在编译时,这意味着如果静态类型是虚拟调用,则可能被去虚拟化(内联的第一步:猜测实际调用的函数;))。这意味着任何定义可见的函数都可以内联。

在库的链接时,你基本上公开了新的函数定义,允许内联一些调用。

在可执行文件的链接时,WPA(整个程序分析)可以通过意识到某些类永远不会派生,从而开始添加一些非虚拟化,因此就该可执行文件而言,可以将其视为final。GCC和LLVM(我怀疑vc++可以)都不能执行这种优化,因为它们没有字节码来保持类层次结构。

如果编译器愿意,可以转储它们拥有的中间表示(某种程度上是字节码),并等待安装内联操作系统特定的调用。虽然我知道没有(c++)这样做。

运行时更改在c++中是相当棘手的,因为内存(例如函数地址)是程序员可以访问的,所以很难只进行安全的转换…