我有一个类成员函数,它是我的应用程序中的关键路径。我的应用程序必须尽可能快地提供预期的(读取:相当希望的)整体性能。
该功能相当复杂,但几次重复几乎相同的部分。类似的东西:
if (condition)
{
//... some code
}
if (another condition}
{
//... nearly identical code which may change condition
}
if (condition)
{
//... some code (same as above)
}
// and so on
为了使代码更易于阅读,理解和维护,我喜欢将其分解并使用函数调用。像:
if (condition)
{
some_function(some_param);
}
if (another condition}
{
some_function(some_other_param);
}
if (condition)
{
some_function(some_param);
}
我买不起任何开销的调用函数,所以我想确保some_function
内联 - 总是。
我搜索了SO并阅读了几篇讨论类似问题的帖子但不完全一样。这些帖子表明唯一的方法是macro
。
我讨厌使用macros
但另一方面我也讨厌当前功能的复杂性。这就像在两个邪恶之间做出选择。
是这样吗?宏是实现这一目标的唯一途径吗?
更新
我得到了很多好的反馈(谢谢)。
几乎所有答案都表明我不会担心/担心强制函数调用是内联的,因为编译器无论如何都会为我做最好的。
所以我决定去做它并做一个测试。我重写了代码,以便在代码片段可以重用时使用函数调用,最终得到更易读(且可维护)的版本。
然后我用100次测试运行测试了新代码和旧代码,并将性能计算为平均值。平均而言,新版本的性能比旧代码低约1%(略低于0.88%)。因此有一个性能受到打击。另一方面 - 表现受到的影响并不像我预期的那么高。
我的结论是我更喜欢新代码,因为它更容易理解。这也意味着更容易维护,调试和移交给其他人。然后我将不得不通过其他代码块中的增益找到丢失的性能。
哦,最后一件事 - 接受哪个答案?我真的不知道。所有答案都是有用的输入。但只有两个答案实际上解决了原始问题。对我来说,他们看起来同样好,所以我会先拿一张贴出来的。
答案 0 :(得分:3)
信不信由你,在某些情况下,函数调用会比内联代码更快
。所以我建议编写函数,使编译器可以在if
语句中的调用点看到函数体,可选地将函数标记为内联,然后让编译器确定它是否&# 39;更好地内联电话。
然后你要做的是分析代码(或者可能检查生成的程序集)并查看你的热点所在。如果它特意显示您的函数调用为热,那么您必须采用宏方法。请注意,如果您确实重新使用宏,请确保再次进行配置文件并确实有所帮助。编译器在优化方面非常擅长。
答案 1 :(得分:2)
编译器不是随意/随意地不内联你的static inline ...
函数,它会出于相当明智的原因这样做。
例如:
if (n == 1) {
n = 2;
// more lines of code
}
if (y == 1) {
// 200 lines of code
}
这会编译为(伪程序集)
cmp n, 1
bne end_if_1
mov 2, n
// ...
end_if_1:
cmp y, 1
bne end_if_2
// result of 200 lines of cod
也就是说,只要条件为假,当n不等于1时就分支,因为函数是内联的,所以下一条指令来自if
范围内。
在某些情况下,“not inlined”只是意味着编译器将函数的代码存储在函数主体之外或者除了编写逻辑序列之外的某个位置。它并不会自动暗示一个重量级的函数调用,如果内联发生的话,指令就不是你所期望的那样。
cmp n, 1
bne end_if_1
mov 2, n
// ...
end_if_1:
cmp y, 1
beq outlined_chunk_1 // <<<<
end_if_2:
cmp ...
// ...
// rest of the function
// ...
ret
outlined_chunk_1:
// ... result of 200 lines of code
oc1_ret:
jmp end_if_2
继续将代码编写为static inline
私有帮助函数,配置文件,并查找强制内联机制,当您发现编译器显然已经错误判断并且花费了很多时间。
答案 2 :(得分:1)
好吧,宏将是唯一获得内联代码的保证方式。
您还可以使用filter
关键字声明C ++函数。这在范围界定方面有一些影响,但对于大多数编译器而言,它也暗示编译器应该尝试生成与调用者内联的函数代码。
您的编译器是否会这样做,是一个单独的问题。它可能也可能不需要启用某些编译优化选项。
您应该尝试编译inline
函数,然后检查生成的代码,看看编译器是否为您调用函数调用。
答案 3 :(得分:0)
编译器在决定是否内联候选函数时使用评分技术。 inline
关键字对该分数的影响很小。使用gcc,有许多命令行选项可以调整得分:
-finline-limit
-fmax-inline-insns-single
-fmax-inline-insns-auto
我的建议是,如上所述,首先看看编译器是否实际内联你的函数。如果是,请离开它。编译器很擅长这一点,如果你没有得到你想要的东西,你应该只进行干预。如果它不是,并且您正在使用gcc,那么接下来可以尝试调整上述选项。
如果这些都不起作用,并且回答你的问题,是的,即使在具有__forceinline
的MSVC上,宏也是强制内联的唯一方法。即便如此,尽一切可能避免使用它们。
答案 4 :(得分:0)
如果对函数使用inline
,请注意编译器可以自动使包含循环的大型函数或函数不是内联的。
最安全的做法是使用#define
宏强制编译器内联函数。