C++ 编译器对传递参数的优化

问题描述 投票:0回答:5

我正在使用一个日志记录模块,可以在运行时启用/禁用报告。电话通常是这样的:

WARN(
     "Danger Will Robinson! There are "
     + boost::lexical_cast<string>(minutes)
     + " minutes of oxygen left!"
);

我正在使用 WARN 的内联函数,但我很好奇幕后进行了多少优化——对整个程序中的参数进行评估将是昂贵的。

WARN
函数是这样的:

bool WARNINGS_ENABLED = false;
inline void WARN(const string &message) {
    if (!WARNINGS_ENABLED) {
       return;
    }
    // ...
}

考虑到构造字符串参数没有副作用,编译器会优化它吗?是否需要一定程度的优化(

-Ox
中的
g++
对于某些
x
)?

c++ optimization arguments
5个回答
12
投票

如果您需要能够在运行时有选择地启用和禁用警告,编译器将不能能够优化调用。

您需要的是将 function 重命名为

WARN2
并添加一个宏,例如:

#define WARN(s) do {if (WARNINGS_ENABLED) WARN2(s);} while (false)

这将阻止在运行时对 s 求值,除非启用了警告。

do-while 是一个技巧,允许它在代码中的任何地方使用(裸语句、带花括号的 if 块内的语句、不带花括号的 if 块内的语句、带花括号和不带花括号的 while 语句等)。


6
投票

您可以使用 -S 选项检查 GCC/G++ 的功能。这将在实际组装之前输出代码 - 请参阅gcc(1)

在这种情况下,GCC 和 G++ 的行为或多或少是相同的。 所以我先将代码翻译成C来进行一些进一步的测试:

char WARNINGS_ENABLED = 0;

inline void WARN(const char* message) {
    if (!WARNINGS_ENABLED) {
        return;
    }
    puts(message);
}

int main() {
    WARN("foo");
    return 0;
}

运行 gcc -O3 -S file.c 并查看输出文件 'file.s'
你会看到 GCC 没有删除任何东西

这不是您所要求的,但为了让编译器有机会优化该代码,您必须使 WARNINGS_ENABLED constant。另一种方法是使其成为“静态”并且不更改该文件中的值。 但是:使其成为静态会产生符号无法导出的副作用。 static const char WARNINGS_ENABLED = 0; inline void WARN(const char* message) { if (!WARNINGS_ENABLED) { return; } puts(message); } int main() { WARN("foo"); return 0; }

GCC 然后完全清理代码。


1
投票

我不是 boost 专家,但我猜测有一种方法可以构造一个 lambda,只有在 WARNINGS_ENABLED 为 true 时才会对其进行求值以生成字符串。 类似...

inline void warnFunc(some_boost_lambda &message_generator) { if (WARNINGS_ENABLED) { cerr << message_generator() << endl; } } #define WARN(msg) warnFunc(...insert boost magic here to turn msg into a lambda...)



1
投票
优化代码,除非全局 WARNING_ENABLED 被声明为 const。

顺便说一句,如果 WARN 是内联函数,您仍然需要付出消息构造的代价(在字符串上使用 lexical_cast 和运算符+的示例中,这是非常低效的),即使它被禁用。

这里有一些高效(禁用运行时时最小(分支预测 CPU 接近于零)开销)

记录宏

,支持函数和流式日志记录。

你不能使用预处理器定义整个事情吗?

0
投票
void inline void LogWarning(const string &message) { //Warning } #ifdef WARNINGS_ENABLED #define WARN(a) LogWarning(a) #else #define WARN(a) #endif

这就是 ASSERT() 宏的工作原理。 WARN 中括号内的所有代码甚至都没有通过预处理器到达编译器。这意味着您可以做其他事情,例如

#ifdef WARNINGS_ENABLED // Extra setup for warning #endif //.... WARN(uses setup variables)

它会以两种方式编译。

为了让优化器认识到括号中没有副作用,您可以在其中放置一些非常复杂的语句(即高级字符串操作),这些语句很难以任何方式证明。

© www.soinside.com 2019 - 2024. All rights reserved.