Question

I'm using a logging module that can have reporting enabled/disabled at runtime. Calls generally go something like:

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

I'm using an inline function for WARN, but I'm curious as to how much optimization is going on behind the scenes -- evaluation of the arguments throughout the entire program would be costly. The WARN function goes something like this:

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

Given that constructing the string argument has no side-effects, will the compiler optimize it out? Is a certain level of optimization required (-Ox in g++ for some x)?

Was it helpful?

Solution

If you need to be able to selectively enable and disable the warnings at run-time, the compiler will not be able to optimize out the call.

What you need is to rename your function to WARN2 and add a macro something like:

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

This will prevent the evaluation of s at run-time unless you have warnings enabled.

The do-while stuff is a trick that allows it to be used anywhere in the code (naked statement, statement within a braced if-block, statement within an unbraced if-block, braced and unbraced while statements and so on).

OTHER TIPS

You can check what GCC/G++ do by using the -S option. This will output the code before it actually gets assembled – see gcc(1).

GCC and G++ more or less behave the same in this case. So I first translated the code into C to make some further tests:

char WARNINGS_ENABLED = 0;

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

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

run gcc -O3 -S file.c and look into the output file 'file.s'
You will see that GCC didn't remove anything!

That's not what you asked for, but in order to give the compiler the opportunity to optimize that code out, you would have to make WARNINGS_ENABLED constant. An alternative is to make it static and not changing the value within that file. But: making it static has the side-effect that the symbol gets not exported.

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 then completely cleans up the code.

I'd guess that it only has a chance to optimize it out if it can prove that there are no side effects (which might be difficult for the compiler to do for an expensive function call).

I'm not a boost expert, but I'm guessing there's a way to construct a lambda that will only be evaluated to generate the string if WARNINGS_ENABLED is true. Something like...

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...)

No, compiler should not optimize the code out in any case unless the global WARNING_ENABLED is declared const.

BTW, if WARN is inline function, you'll still pay the price of message construction (which is very inefficient in your example with lexical_cast and operator+ on strings), even if it's disabled.

Here are some efficient (minimal (close to zero with branch predicting CPU) overhead when runtime disabled) logging macros that support both function and stream style logging.

Can't you just define the whole thing out using the preprocessor?

void inline void LogWarning(const string &message) 
{
  //Warning
}

#ifdef WARNINGS_ENABLED
#define WARN(a) LogWarning(a)
#else
#define WARN(a)
#endif

This is just how the ASSERT() macro works. All the code inside the brackets in WARN does not even make it through the preprocessor to the compiler. That means you can do other stuff like

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

And it will compile both ways.

As for getting the optimiser to realise that there are no side-effects in the brackets, you can put some pretty complex statements in there (i.e high level string manipulation) that are hard to prove either way.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top