我正在使用可在运行时启用/禁用报告的日志记录模块。电话通常类似于:

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 in g ++ for some code> x )?

有帮助吗?

解决方案

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

您需要将功能重命名为 WARN2 并添加如下宏:

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

这将阻止在运行时评估s,除非您启用了警告。

do-while东西是一个技巧,允许它在代码中的任何地方使用(裸体语句,支撑if-block中的语句,unbraced if-block中的语句,braced和unbraced while语句等)

其他提示

您可以使用 -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会彻底清理代码。

我猜它只有有机会优化它才能证明没有副作用(编译器可能难以进行昂贵的函数调用)。

我不是一个提升专家,但我猜测有一种方法可以构建一个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...)

不,除非将全局WARNING_ENABLED声明为const,否则编译器应该在任何情况下优化代码。

顺便说一句,如果WARN是内联函数,你仍然会支付消息构造的代价(在你的例子中使用lexical_cast和operator + on strings)效率非常低,即使它被禁用了。

当运行时禁用时,这里有一些有效的(最小的(带分支预测CPU接近零)开销)记录宏,支持函数和流式记录。

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

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)

它将编译两种方式。

至于让优化器意识到括号中没有副作用,你可以在那里放一些非常复杂的语句(即高级字符串操作),这两种方法很难证明。

许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top