我们有一个用于在公共实用程序库中发出错误信号的宏,如下所示:
#define OurMacro( condition ) \
if( condition ) { \
} else { \
CallExternalFunctionThatWillThrowAnException( parametersListHere ); \
} \
我称之为parametersListHere
的是逗号分隔的常量和宏列表,在每次宏扩展时由编译器填充。
该函数调用始终解析为调用 - 函数实现不会向编译器公开。该函数有六个参数,在调试配置中,它们都具有有意义的值,而在发布配置中,只有两个具有有意义的值,而其他值则传递相同的默认值。
通常情况会成立,所以我不关心调用的速度有多快,我只关心代码膨胀。使用6个参数调用该函数需要七个x86指令(6 push
es和一个call
),如果函数签名更改为有两个push
,则可以避免4个inline
es仅参数 - 这可以通过引入以这种方式实现的中间“门”函数来完成,其实现对于编译器是不可见的。
我需要估计是否应该坚持这一改变。到目前为止,我期望的主要改进是减少参数数量将在每次调用时丢弃4条指令,这意味着围绕宏扩展的代码将变得更小,编译器将更可能内联它并进一步优化发出的代码。
如果不实际尝试并重新编译所有代码并仔细分析发出的代码,我怎么能估计呢?每次我读到{{1}}时都会有一个声明,说明编译器决定是否内联函数。
我是否可以看到函数内部如何影响编译器内联决策的一套确切规则?
答案 0 :(得分:3)
GCC有一套相当多的选项可以揭示他们的流程如何运作,记录here。它当然不准确,因为它会随着时间的推移而调整,并且它依赖于CPU。
第一条规则是“他们的身体小于预期的函数调用代码”。 第二条规则是“静态函数调用一次”。
还有影响过程的参数,例如: max-inline-insns-single
。 insn
是GCC编译器中的伪指令,在此用作函数复杂度的度量。参数max-inline-insns-auto
的文档清楚地表明,手动声明函数inline
可能会导致它被考虑用于内联,即使它对于自动内联来说太大了。
内联不是一个全有或全无的过程,因为有一个-fpartial-inlining
标志。
当然,你不能孤立地考虑内联。常见的Subexpression Elimination(CSE)使代码更简单。这是一个优化过程,可以使函数小到足以内联。在内联之后,可能会发现新的公共子表达式,因此应该再次运行CSE传递,这反过来可能会触发进一步的内联。 CSE并不是唯一需要重新运行的优化。
答案 1 :(得分:1)
关于哪些函数内联以及在什么条件下(例如选定的优化级别)的规则是特定于每个编译器的,因此我建议您检查编译器的文档。但是,一个只转发到另一个函数的函数(如你所建议的)应该是支持它的任何编译器内联的一个很好的候选者。
某些编译器有一种机制,您可以通过该机制标记您确实需要内联函数,例如: MSVC ++有__forceinline。
答案 2 :(得分:1)
如果您使用的是Visual C ++,则可以使用__forceinline 强制编译器内联函数。