c++ - 为什么 gcc 不为我决定内联或不内联这个功能?

标签 c++ c gcc

从网上的一些话我知道 GCC 很聪明,可以决定是否内联一个函数。 inline 关键字只是一个提示:
GCC 可以内联一个普通函数,而不能内联一个内联函数

但是对于我项目中的这个功能:

struct vb_pos{
    union{
        struct{
            int offset;
            int l;
        };
        unsigned long long g_offset;    
    };
};
static inline void vi_write_vtail_smart(struct vi *vi){
    struct vb_pos *vhead, *vtail, *cursor;
    vhead = &vi->v_head;                    
    vtail = &vi->v_tail;
    cursor = &vi->cursor;

    int curoff = vi->curr - vi->lines[vi->currl].buf;
    cursor->offset = curoff;    

    if(cursor->g_offset >= vhead->g_offset){
        *vtail = *cursor;
    }
    else{
        *vtail = *vhead;
        *vhead = *cursor;
    }
}

使用 -O2 编译。
我检查了汇编代码,知道这个函数是按预期内联的。
然而,当我删除它的inline修饰符,重新编译时,我发现它不是孤独的内联。它的函数体出现在最终的二进制文件中:

0000000000000000 <vi_write_vtail_smart>:
       0:       48 63 47 14             movslq 0x14(%rdi),%rax
       4:       48 8b 17                mov    (%rdi),%rdx
       7:       48 8d 04 40             lea    (%rax,%rax,2),%rax
       b:       48 8d 04 c2             lea    (%rdx,%rax,8),%rax
       f:       48 8b 57 18             mov    0x18(%rdi),%rdx
      13:       48 2b 10                sub    (%rax),%rdx
      16:       89 57 10                mov    %edx,0x10(%rdi)
      19:       48 8b 47 10             mov    0x10(%rdi),%rax
      1d:       48 3b 47 38             cmp    0x38(%rdi),%rax
      21:       73 0d                   jae    30 <vi_write_vtail_smart+0x30>
      23:       48 8b 57 38             mov    0x38(%rdi),%rdx
      27:       48 89 47 38             mov    %rax,0x38(%rdi)
      2b:       48 89 57 40             mov    %rdx,0x40(%rdi)
      2f:       c3                      retq   
      30:       48 89 47 40             mov    %rax,0x40(%rdi)
      34:       c3                      retq

我想知道,既然GCC足够聪明,为什么它没有自己的决定呢?为什么它在我指定时内联执行,而在我不指定时不执行?

因为他没有找到足够的线索来做出强有力的决定?或者,因为他已经有了一个决定,他的决定是:内联与不内联没有太大区别,既然你问我,我就为你内联;否则,我将其保留为通用函数。

我想知道真正的原因。 如果是第一种情况,我想我们可能需要重新考虑一下这篇文章开头的观点(网上很流行)————至少,GCC 没有他们说的那么聪明,inline 关键字并不像他们说的那么无用。

在文末,我想对上面代码片段的上下文添加更多的描述:

1、我本来想把vi_write_vtail_smart()内联到函数A()B()中,作为库导出API 和两者都将经常被用户调用。

2、A()B()vi_write_vtail_smart()在同一个文件中。

3、vi_write_vtail_smart()只在A()B()中使用,其他地方没有。

4、A()的函数体大小约为450字节,B()类似。

5、A()‖和B()基本上是纯机器码,没有大循环和繁重的计算,只调用一个子函数,除了vi_write_vtail_smart()。该子函数在另一个文件中。

6,我做了一个小测试,我在if(cursor->g_offset >= vhead->g_offset){之前加了一行return;,(我想要看看当这个函数足够小时会发生什么),即:

...
int curoff = vi->curr - vi->lines[vi->currl].buf;
cursor->offset = curoff;    

return;
if(cursor->g_offset >= vhead->g_offset){
...

并且在没有inline修饰符的情况下编译,并检查了汇编代码————这次GCC对其进行了内联,其函数定义从最终的二进制文件中消失了。

7、我的开发环境:
ubuntu-16.04/64位
gcc 版本 5.4.0 20160609
架构:intel X86 Ivybridge Mobile

9,Compile flag(这里又得写一遍,有些人看的时候漏了) -O2 -std=gnu99

最佳答案

根据 GCC 文档,GCC 有一个名为 -finline-functions 的优化设置。这实际上是使 GCC 在所有 函数上使用其启发式内联标准的设置,即使它们未声明为inline。此设置在 -O3 优化级别启用。因此,如果您想给予 GCC 充分的自由以将其启发式方法应用于所有函数,您必须至少指定 -O3(或明确指定 -finline-functions)。

没有 -finline-functions GCC 通常不会尝试内联未声明为 inline 的函数,但有一些值得注意的异常(exception):许多其他内联选项也可能导致非内联函数被内联。但是,这些选项针对的是非常特殊的情况

  • -finline-functions-called-once 早在 -O1 就已启用。只调用一次的静态函数是内联的,即使它们没有被声明为inline

  • -finline-small-functions-O2 处启用。如果它导致代码大小减少,它会触发内联,即使函数未声明为 inline

您的函数显然没有通过这些在 -O2 级别激活的特定内联过滤器:它相对较大并且(显然)被调用了不止一次。出于这个原因,GCC 不考虑在 -O2 处进行内联,除非您使用 inline 关键字明确请求它。请注意,显式 inline 关键字基本上就像 -finline-functions 设置仅为该特定功能打开。它将使 GCC 考虑将其内联,但不保证内联。

同样,如果您希望 GCC 完全接管这些决策,您需要 -finline-functions-O3。事实上,显式 inline 关键字在 -O2 处触发内联意味着 GCC 应该决定在 -O3 处内联它,而不管 inline 出现在那里。

关于c++ - 为什么 gcc 不为我决定内联或不内联这个功能?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/44875934/

相关文章:

c++ - 将多个文件 (gz) 通过管道传输到 C 程序中

c++ - 我可以将 list 文件添加到其他人的 exe 中吗?

c++ - 更改字节顺序的最快方法

c - "variable"使用 C 预处理器进行长度初始化

python - 安装 MySQL_python 1.2.2 (Mac OS 10.6 Snow Leopard) 时出现 GCC 错误

c++ - 过载 ">>"如何操作?

c - Makefile LINK.cc - main.o 放在链接库之后并导致错误

android - 为什么arm-linux-androideabi-gcc强制执行-fpic?

c++ - 用 g++ .cpp 文件编译 gcc .o 文件

c++ - const 参数值为 0 的重载决策不正确