编译器如何确定是否值得使我的函数内联?
How does a compiler decide whether it's worth making my functions inline or not?
我想知道是否有人知道c++编译器通常用来决定是否在编译时内联函数的逻辑(假设内联已被请求)。
这类事情是公众所知的吗?
对于另一个问题,我给出了更彻底的答案。
基本上编译器有基于成本分析的启发式,引用我自己的话(这是否意味着我老了?)
如果你考虑内联及其后果,你就会意识到:
- 你避免一个函数调用(与所有的寄存器保存/帧调整)
- 您向优化器暴露了更多上下文(死存储,死代码,公共子表达式消除…)
- 以重复代码为代价(增加指令缓存和可执行文件大小等)
当然,也有部分内联,其中只有部分函数是内联的,通常是if
的前置守卫,如foo(T* t) { if (!t) { return; } <many many things> }
。
是的,这类信息是公共知识。尤其是现在市面上有大量的开源编译器。
我相信要读的书是龙书。他们把所有这些都做了一遍,不是吗?
但是基本上:函数调用是有成本的——设置寄存器,跳转,收集结果。这可以用循环来计算。函数体也有以周期衡量的成本。比较两者。考虑缓存局部性的额外加分。
我认为编译器使用某种启发式方法来决定是否内联函数。
如果函数只从一个地方调用,内联它可以节省一些指令和周期,提高大小和速度。
如果它是一个小函数,内联也可以这样做。
如果函数在紧密循环中被多次调用,内联可以提高性能。
与此同时,内联会导致整体代码更大,并且由于缓存更多代码,也可能对性能产生一些负面影响。
编译器试图做出最佳选择,但他们也必须考虑到诸如可配置优化选项(程序员可能会说他更喜欢速度而不是大小或相反)以及他们做出这些决定和生成代码所需的时间(你不希望编译器大量优化所有内容,并花费许多额外的时间来编译代码)。这是有权衡的。
同样,编译器也不是万能的。他们只是不能把某些二和二放在一起,不是因为这很贵,而是因为这几乎是不可能的。这就是为什么我们做性能和负载测试,有分析器,让人写代码,因为人类通常比他们的程序更聪明。
启发式将取决于您传递给编译器的选项,以及也取决于编译器。没有一个通用的答案。大多数现在的编译器有内联的选项组合什么都没有。大多数还具有内联选项的组合将取决于程序早期运行的分析器输出。否则:从实现质量的角度来看,它是期望编译器内联函数声明inline
是合理的比未申报inline
的更具攻击性。通常会的只有当最激进的优化被激活时类中没有定义的函数将被内联翻译单元。最后,某些东西在功能上可能会受到抑制内联:一些编译器可能不会内联递归函数,例如示例(尽管至少在某些情况下,g++确实如此)。
- 为什么我的删除节点函数实际上没有删除节点?
- 为什么在我的函数类型后使用引用运算符 (&) 允许我修改它返回的值?
- 模板,函数使用错误的构造函数来复制我的对象
- 为什么我的递归函数按降序打印,然后按升序打印?
- 为什么我的模板化函数需要从一个迭代器转换到另一个迭代器?
- 在哪里放置我的函数?进入我的母语 Gui 还是进入我的演示者?
- 为什么我的数组双精度函数不起作用?
- 移动赋值运算符;尝试引用已删除的函数.我该如何解决这个问题?
- 为什么我的双向链表删除函数会删除多个节点?
- 为什么在我的 main 函数中声明整数后我的整数数组中会出现错误?
- 如何返回实际值(在我的例子中是无符号字符数组)而不是来自 C++ 函数的指针?
- 为什么我的运算符 + 重载尽管是通过引用传递的,但仍调用我的复制构造函数?
- 为什么在尝试测量函数返回所需的时间时,我的运行时编号是错误的?
- 查找定义我的 C/C++ 函数/宏的文件比'grep'更简单的方法
- 为什么我的 while 函数没有终止?
- 为什么我的 constexpr 对象在我的函数中不是 constexpr?
- 我关于函数"Assert"的C++代码有问题
- 如何检测除整数以外的任何内容是否传递给我的类构造函数?
- 链表堆栈类的复制构造函数:我的正在向后复制
- 为什么在这种情况下没有调用复制构造函数-我的推理正确吗?