在 C++ 中,关键字“inline”有两个用途。首先,它允许定义出现在多个翻译单元中。其次,它是对编译器的一个提示,一个函数应该内联在编译后的代码中。
我的问题:在 GCC 和 Clang/LLVM 生成的代码中,关键字“inline”是否对函数是否内联有任何影响?如果是,在什么情况下?还是完全忽略了提示?请注意,这不是语言问题,而是特定于编译器的问题。
在 C++ 中,关键字“inline”有两个用途。首先,它允许定义出现在多个翻译单元中。其次,它是对编译器的一个提示,一个函数应该内联在编译后的代码中。
我的问题:在 GCC 和 Clang/LLVM 生成的代码中,关键字“inline”是否对函数是否内联有任何影响?如果是,在什么情况下?还是完全忽略了提示?请注意,这不是语言问题,而是特定于编译器的问题。
[警告:不是 C++/GCC 大师] 你需要在这里阅读 inline。
通过使用内联函数说明符提出的建议的有效程度(C99 6.7.4)。
- 如果使用了 -fno-inline 选项或使用了 -O0,GCC 将不会内联任何函数。否则,GCC 可能仍然无法内联函数,原因有很多;-Winline 选项可用于确定函数是否未内联以及为什么不内联。
因此,除非使用您的编译器设置(如-fno-inline
or -O0
),否则编译器似乎会提示。我无法评论 Clang/LLVM(或真正的 GCC)。
-Winline
如果这不是代码高尔夫问题并且您需要知道发生了什么,我建议使用。
gcc的一个有趣的解释:内联函数与宏一样快:
一些调用由于各种原因不能被集成(特别是函数定义之前的调用不能被集成,定义中的递归调用也不能被集成)。如果有一个非集成调用,那么该函数像往常一样被编译为汇编代码。如果程序引用它的地址,该函数也必须像往常一样编译,因为它不能被内联。
请注意,函数定义中的某些用法可能使其不适合内联替换。这些用法包括:可变参数的使用、alloca 的使用、可变大小数据类型的使用(请参阅可变长度)、计算 goto 的使用(请参阅作为值的标签)、非本地 goto 的使用和嵌套函数(请参阅嵌套函数)。使用 -Winline 将在标记为 inline 的函数无法替换时发出警告,并给出失败的原因。
根据 ISO C++ 的要求,GCC 认为在类的主体中定义的成员函数被标记为内联,即使它们没有使用 inline 关键字显式声明。您可以使用 -fno-default-inline 覆盖它;请参阅控制 C++ 方言的选项。
GCC 在不优化时不会内联任何函数,除非您为函数指定 `always_inline' 属性,如下所示:
/* Prototype. */ inline void foo (const char) __attribute__((always_inline)); The remainder of this section is specific
到 GNU C90 内联。
当内联函数不是静态的时,编译器必须假设可能有来自其他源文件的调用;由于一个全局符号在任何程序中只能定义一次,因此不能在其他源文件中定义该函数,因此不能集成其中的调用。因此,非静态内联函数总是以通常的方式自行编译。
如果在函数定义中同时指定 inline 和 extern,则该定义仅用于内联。在任何情况下,该函数都不会自行编译,即使您明确引用其地址也是如此。这样的地址变成了外部引用,就好像您只声明了函数,而没有定义它。
这种 inline 和 extern 的组合几乎具有宏的效果。使用它的方法是将函数定义放在带有这些关键字的头文件中,并将定义的另一个副本(缺少内联和外部)放在库文件中。头文件中的定义将导致对函数的大多数调用被内联。如果该函数的任何用途仍然存在,它们将引用库中的单个副本。
通过阅读 GCC 和 LLVM 项目的代码,可以收集到很多信息。这是通过直接阅读代码收集的一些信息。(注意:这不一定是完全全面的,也没有列出inline
影响每个细节中内联的每一种方式,只是对其中的大部分进行概述)
该信息是从 GCC 和截至 2021 年 11 月 13 日的 LLVM 当前开发 HEAD 收集的,因此将来可能不是最新的。
在 GCC 方面:
inline
为早期内联的函数-finline-small-functions
-O2
-finline-functions
-O2
inline
当调用者不是并且内联不会缩小调用者时,GCC 不会考虑在尝试内联针对大小优化的函数时未声明的函数inline
为稍好一些的函数,除非它们也低于内联函数指令阈值(即max-inline-insns-auto
,稍后解释)(代码很复杂,所以我在这里对它的确切作用有点模糊)inline
将其“坏”值除以 8,这样它们更有可能首先被内联max-inline-insns-auto
在函数未声明为内联时在更多情况下调用的参数值(尽管我可以查看情况并非如此的情况,例如当 GCC 的启发式认为“加速似乎很大”时):此值默认为 15,而该max-inline-insns-single
值在指定 inline 时使用,并且具有默认值70,这意味着声明为 inline 的函数可以被内联,当它比 GCC 考虑的未声明为 inline 的函数大 4.6 倍时(注意:前面的超链接只是使用这些限制的最重要示例,它们也适用于其他一些地方)inline
如果未声明函数且未指定 -finline-small-functions(由 -O2 隐含),则 GCC 不会拆分函数inline
在没有-finline-functions
(included in -O2
) 的情况下调查内联非函数,除非它们非常短,在这种情况下它只需要-finline-short-functions
(included in -O2
)在 LLVM 方面:
inline
将具有inlinehint
由 Clang 附加到 LLVM 字节码函数的属性inlinehint-threshold
值inlinehint
,默认值为 325,否则它将使用其他东西(例如调用站点是热还是冷等),如果没有找到其他内容,将使用inlinedefault-threshold
默认值为 225 的值。该值与 LLVM 计算的启发式算法进行比较,该启发式算法将为每个函数调用计算内联给定函数的成本,如果该值小于阈值,则该函数将被内联,这意味着内联提示将基本上减少如 LLVM 所见,内联函数的成本约为 1.44换句话说,在 GCC 和 Clang 上,这似乎是一个非常强烈的提示。
提示的强度完全取决于您使用的编译选项。大多数编译器可以选择不进行内联,只内联标记为“内联”的那些,或者使用其最佳判断并忽略提示。
最后一个可能效果最好。:-)