在宏中过度使用会损害性能吗?

Posted

技术标签:

【中文标题】在宏中过度使用会损害性能吗?【英文标题】:can overuse in Macros hurt performance? 【发布时间】:2014-03-20 20:47:15 【问题描述】:

我有一个很长的代码,它被调用了数百万次, 我注意到,如果我将所有宏更改为内联函数,代码运行速度会快很多。

你能解释这是为什么吗?宏不只是文本替换吗?与可以调用函数的内联函数相反?

【问题讨论】:

类似于#define MAX(x, y) ((x) > (y) ? (x) : (y)),它会多次评估其参数。 宏只是文本替换。要查看您的编译器对它们做了什么,请使用 -E 标志进行编译,例如 g++ -E file.cpp MAX(expensivecall(), otherexpensivecall()) 将执行其中一个昂贵的调用两次。使用多个宏,错误会变得更糟。 如果使用 C++,请不要使用宏。内联函数会更好 @androidy - 编译器理解语言的语义。预处理器没有。因此编译器在优化时可以有更好的 bash 【参考方案1】:

宏是一种文本替换,因此通常会生成更多可执行代码。每次调用宏时,都会插入代码(好吧,不一定,宏可能是空的......但原则上)。 另一方面,内联函数可能与宏的工作方式相同,但它们也可能根本不是内联的。

一般来说,inline 关键字是一个弱提示,而不是一个要求,编译器现在将基于启发式,主要是伪指令的数量明智地内联函数(或将放弃这样做)。

因此,内联函数可能会导致编译器不内联该函数,或者内联几次,然后再将其称为非内联函数。 令人惊讶的是,不内联实际上可能比内联更快,因为它减少了整体代码大小,从而减少了缓存和 TLB 未命中的数量。

【讨论】:

【参考方案2】:

这取决于您使用的特定宏和函数调用。一个特定的宏实际上可以编译成比内联函数更长的操作集。通常最好不要对某些进程使用宏。内联函数将允许编译器进行类型检查和优化各种过程。宏会出现许多错误,实际上会导致各种低效率(例如必须将变量移入和移出存储)。

无论如何,由于您在代码中确实看到了这种情况,您可以看出编译器能够优化您的内联代码,而不是盲目地放入文本扩展中。

请注意,谷歌搜索“宏与内联”显示了许多关于此的讨论。

【讨论】:

我读了大部分。他们没有为宏性能变差提供很好的例子。就像错误的输出以及为什么内联更好之类的东西 @Gilad 就个人而言,我会设置预处理器选项和编译器选项来扩展代码区域,以查看宏和内联版本的不同之处。我也可能会告诉它为一小段(甚至最少)代码生成程序集,以查看优化在做什么。我也会尝试不同级别的优化,看看发生了什么。您还可以在问题中显示实际的宏和内联代码。【参考方案3】:

除了强制内联之外,如果没有仔细编写宏以不对参数进行两次评估,宏也会对速度产生不利影响。以这个类似函数的小宏及其等效的内联函数为例:

#define square(x) ((x)*(x))

inline long square(long x)  return x*x; 

现在,当您使用变量square(foo) 调用它们时,它们是等价的。宏版本扩展为((foo)*(foo)),这是一个乘法,就像内联函数一样。

但是,如果您使用square(expensiveComputation(foo)) 调用它们,则宏的结果是,expensiveComputation() 被调用了两次。相比之下,内联函数的行为类似于任何函数:它的参数在函数体执行之前被计算一次。

当然,您可以使用复合语句的 gnu 扩展来编写宏(请参阅http://gcc.gnu.org/onlinedocs/gcc/Statement-Exprs.html 以获取相关文档)以避免像这样的双重评估:

#define square(x) ( \
    long square_temp_variable = (x); \
    square_temp_variable*square_temp_variable; \
)

但这很麻烦,而且代码不可移植。所以,最好坚持使用内联函数。

【讨论】:

不仅如此,在宏范围内创建局部变量 square_temp_variable 也会导致处理速度变慢。它可能是最小的,但超过数百万次,它可以加起来 @sabbahillel 这不是真的,与内联函数调用相比,定义新的局部变量没有开销。请记住,函数调用将为所有函数参数创建变量。即使编译器内联调用,生成的代码也必须就像这些变量被创建一样。当然,复制省略是允许的,但如果您在宏中创建局部变量也是如此。【参考方案4】:

一般来说,尽可能用内联函数替换函数样式宏是一个很好的建议。

您不仅摆脱了一些讨厌的陷阱a = MIN(i++, 50),例如,您还获得了类型安全性,并且正如在某些 cmets 中所述,您避免了对争论的多次评估,这可能对性能产生非常糟糕的影响。

【讨论】:

我真的觉得这不能很好地回答这个问题。 OP 想知道 为什么 宏会导致更差的性能,但是你给了他各种其他(不相关的)反对使用宏的论据,并且非常简短地提到了对参数的多重评估(没有任何详细说明),这已经在评论中得到了更好的处理。通过删除不相关的内容并扩展性能解释,可以改进答案。

以上是关于在宏中过度使用会损害性能吗?的主要内容,如果未能解决你的问题,请参考以下文章

Symfony 3在宏中使用宏?

在宏中使用用户在表单中输入的信息

在宏中使用 Objective C API 登录静态库

在宏中拆分字符串

确保每个字符串文字都包含在宏中

在宏中调用函数