为什么 GCC 没有为我决定内联或不内联这个功能?
why didn't gcc decide inline-or-not for me for this function?
从网络上的一些词中,我知道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没有他们说的那么聪明,内联关键词也不像他们说的那么无用。
在文章末尾,我想为上述代码片段的上下文添加更多描述:
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/64bit
gcc 5.4.0 版本 20160609
架构:英特尔X86常春藤桥移动
9、编译标志(这里还要再写一遍,有人看的时候会错过( -O2 -std=gnu99
根据 GCC 文档,GCC 有一个名为-finline-functions
的优化设置。这实际上是使 GCC在所有函数上使用其启发式内联条件的设置,即使它们未声明为inline
。此设置在优化级别启用-O3
。因此,如果您想让 GCC 完全自由地将其启发式应用于所有函数,您必须至少指定-O3
(或明确指定-finline-functions
(。
如果没有-finline-functions
GCC通常不会尝试内联未声明为inline
的函数,但有一些值得注意的例外:许多其他内联选项也可能导致非内联函数被内联。但是,这些选项针对非常具体的情况
-
-finline-functions-called-once
早在-O1
年就已启用。仅调用一次的静态函数是内联的,即使它们未声明为inline
。 -
-finline-small-functions
在-O2
上启用。如果它导致代码大小减小,即使函数未声明为inline
,也会触发内联。
您的函数显然没有通过这些在-O2
级别激活的特定内联过滤器:它相对较大并且(显然(多次调用。出于这个原因,GCC不认为它在-O2
内联,除非你用inline
关键字明确请求它。请注意,显式inline
关键字基本上类似于仅为该特定功能打开-finline-functions
设置。它将使 GCC考虑将其内联,但不保证内联。
同样,如果您希望 GCC 完全接管这些决定,您需要-finline-functions
或-O3
。显式inline
关键字触发内联 at-O2
的事实意味着 GCC 应该决定在-O3
处内联它,而不管那里是否存在inline
。
- 具有sleep_for和sleep_until功能的 gcc 可能存在的问题
- 是否可以在gcc中只启用特定的C++语言功能
- AVR-GCC:(看似)简单功能中不需要的序言/尾声
- 为什么 GCC 没有为我决定内联或不内联这个功能?
- C 11螺纹 - 非静态成员功能的使用无效 - 工作GCC 5.1.0损坏的GCC 7.3.1
- GCC / DIAB:保持未使用的功能 /部分
- 模板类中的静态功能:GCC与VS
- 为什么GCC违反了调用ABS功能的代码,并用简短的参数进行功能
- 如何求解与GCC/Clang不同的C 功能模板的MSVC处理(无法识别的模板定义)
- GCC称将功能与多个继承过载时,称其为模棱两可,但Clang和MSVC没有
- 未定义的引用指针的变量模板在clang中功能,而不是GCC
- 从GCC功能树节点中检索函数参数
- C++11 具有与不同版本的 GCC 兼容的功能
- 如何获得GCC版本支持特定功能
- 将GCC功能属性放置在自动功能中
- GCC vs Clang:将功能与-FPIC一起插入
- GCC 是否可以修改 C++ 功能的 C 标头
- 在PowerPC编译器上删除功能,而不是GCC上的功能
- C GCC功能模板超载编译器问题
- 要使 gcc 4.8.1 支持 c++11 中的新"auto"功能?