宏是强制内联的唯一方法吗?

Are macros the only way to force inline

本文关键字:唯一 方法      更新时间:2023-10-16

我有一个类成员函数,它是我的应用程序中的关键路径。我的应用程序必须尽可能快地提供预期(阅读:而不是希望的(整体性能。

该函数相当复杂,但几乎相同的部分重复了几次。像这样:

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%(。所以有一个性能打击。另一方面 - 性能打击没有我预期的那么高。

的结论是我更喜欢新代码,因为它更容易理解。这也意味着更容易维护、调试和移交给他人。然后,我将不得不通过其他代码块中的增益来找到丢失的性能。

哦,最后一件事 - 接受哪个答案?我真的不知道。所有答案都是有用的输入。但只有两个答案真正解决了最初的问题。对我来说,它们似乎同样好,所以我先拿那个。

不信由你,在某些情况下,函数调用最终比内联代码更快。

因此,我建议以这样一种方式编写函数:编译器可以在if语句中的调用点看到函数体,可以选择将函数标记为内联,然后让编译器确定内联调用是否更好。

然后,您要做的是分析代码(或可能检查生成的程序集(并查看热点的位置。如果它特别显示对函数的调用为热,那么您必须诉诸宏方法。请注意,如果您确实回退到宏,请确保您再次分析并且它确实有帮助。编译器非常擅长优化。

编译器

不会随机/任意地不内联您的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

也就是说,只要条件为 false,当 n 不等于 1 时,您就会分支,因为该函数是内联的,因此下一条指令来自 if 的作用域内。

在某些情况下,"未内联"仅意味着编译器将函数的代码存储在函数主体之外或逻辑序列中写入位置以外的其他位置。它不会自动暗示一个沉重的函数调用,如果内联发生得很琐碎,指令就不是你期望它们的位置。

  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私有帮助程序函数、配置文件,并在发现编译器明显误判并花费大量性能时寻找强制内联机制。

好吧,宏将是获取内联代码的唯一保证方法。

您还可以使用 inline 关键字声明C++函数。这在范围方面有一些影响,但对于大多数编译器来说,这也暗示编译器应该尝试生成与调用方内联的函数代码。

您的编译器是否会这样做是一个单独的问题。它可能还需要也可能不需要启用某些编译优化选项。

您应该尝试编译inline函数,然后检查生成的代码以查看编译器是否内联了函数调用。

编译器在决定是否内联候选函数时使用评分技术。inline关键字对该分数的影响很小。使用 gcc,有许多命令行选项可以调整评分:

-finline-limit
-fmax-inline-insns-single
-fmax-inline-insns-auto

我的建议是,如上所述,首先查看编译器是否真的在内联您的函数。如果是,请离开它。编译器擅长这一点,只有在没有得到想要的东西时,你才应该进行干预。如果不是,并且您使用的是 gcc,接下来您可以尝试调整上述选项。

如果这些都不起作用,并且要回答您的问题,是的,宏是强制内联的唯一方法,即使在具有__forceinline的 MSVC 上也是如此。即便如此,请尽一切可能避免使用它们。

如果对函数使用 inline,请注意编译器可以自动使大型函数或包含循环的函数不内联。

最安全的方法是使用#define宏强制编译器内联函数。