我正在使用可在运行时启用/禁用报告的日志记录模块。电话通常类似于:
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
?{/ p>
答案 0 :(得分:12)
如果您需要能够在运行时有选择地启用和禁用警告,编译器将不能够优化呼叫。
您需要将功能重命名为WARN2
并添加以下内容的宏:
#define WARN(s) do {if (WARNINGS_ENABLED) WARN2(s);} while (false)
这将阻止在运行时评估s,除非您启用了警告。
do-while东西是一个技巧,允许它在代码中的任何地方使用(裸体语句,支撑if-block中的语句,unbraced if-block中的语句,braced和unbraced while语句等)
答案 1 :(得分: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 常量。另一种方法是使其静态,而不是更改该文件中的值。 但是:使它静态会产生副作用,即符号不会被导出。
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会彻底清理代码。
答案 2 :(得分:1)
我猜它只有有机会优化它才能证明没有副作用(编译器可能难以进行昂贵的函数调用)。
我不是一个提升专家,但我猜测有一种方法可以构建一个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...)
答案 3 :(得分:1)
不,除非将全局WARNING_ENABLED声明为const,否则编译器应该不在任何情况下优化代码。
顺便说一句,如果WARN是内联函数,你仍然会支付消息构造的代价(在你的例子中使用lexical_cast和operator + on strings)效率非常低,即使它被禁用了。
以下是一些有效的(在运行时禁用时最小(接近零,具有分支预测CPU)开销)logging macros,它支持函数和流样式日志记录。
答案 4 :(得分: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)
它将编译两种方式。
至于让优化器意识到括号中没有副作用,你可以在那里放一些非常复杂的语句(即高级字符串操作),这两种方法很难证明。