3

I am looking at code that someone else wrote, and it has a lot of debug sections, of type

if(0) { code }

or if(1) { code }

or if(false) { code }

There is even

#if(0)
#endif

(which did not turn gray though - I thought that it should)

I was wondering, if I replace these with some #if 0 (or #ifdef _DEBUG), is it possible to optimize the code ? - or - it will not make any difference ?

I think that it may help, since I have seen code that is within these sections being grayed out - and I thought that this code is removed from the Release executable... Therefore making it faster. Is that true ?

The code that I am thinking of is inside functions that could be called lots of times...

Edit: The code I am referring to is being run millions of times. I am aware that the contents of the if(0) will be ignored...

I am also aware of the benefit of being able to easily debug an issue, by switching a test from 0 to 1...

My question was, the fact that I am adding millions of millions of times the test if(0) does not add overhead... I am trying to figure out what are all the things that could make this code take fewer hours.

Thalia
  • 13,637
  • 22
  • 96
  • 190

5 Answers5

2

If expressions placed inside those IFs are constant and determinable at the time of compilation, then you may be almost sure that the compiler has already removed them off the code for you.

Of course, if you compile in Debug-Mode, and/or if you have optimization-level set to zero, then the compiler may skip that and leave those tests - but with plain zero/one/true/false values it is highly unlikely.

For a compile-time constant branches, you may be sure that the compiler removed the dead ones.

It is able to remove even complex-looking cases like:

const int x = 5;

if( 3 * x * x < 10 ) // ~ 75 < 10
{
    doBlah(); // skipped
}

However, without that 'const' marker at X, the expression's value may be not determinable at the compile time, and it may 'leak' into the actual final product.

Also, the value of expression in following code is not necesarily compile-time constant:

const int x = aFunction();

if( 3 * x * x < 10 ) // ~ 75 < 10
{
    doBlah(); // skipped
}

X is a constant, but it is initialized with value from a function. X will most probably be not determinable at the time of compilation. In runtime the function could return any value*) so the compiler must assume that X is unknown.

Therefore, if you have possibility, then use preprocessor. In trivial cases that won't do much, because the compiler already knew that. But cases are not always trivial, and you will notice the change vrey often. When optimizer fails to deduce the values, it leaves the code, even if it is dead. Preprocessor on the other hand is guaranteed to remove disabled sections, before they get compiled and optimized. Also, using preprocessor to do that at least will speed up the compilation: the compiler/optimizer will not have to traceconstants/calculate/checkbranches etc.

*) it is possible to write a method/function which return value will actually be determinable at the compilation and optimization phases: if the function is simple and if it gets inlined, its result value might be optimized out along with some branches.. But even if you can somewhat rely on removing the if-0 clauses, you cannot rely on the inlining as much..

quetzalcoatl
  • 32,194
  • 8
  • 68
  • 107
  • Translation: I will gain nothing if I make this change ? – Thalia Feb 02 '13 at 01:24
  • compiler error! expecting a variable name or type after `const` – Aniket Inge Feb 02 '13 at 01:29
  • If those are if(0) if(false) - then not. Maybe a little speedup in compilation, but not at runtime. You MAY get speed up in runtime if your compiler is very crappy or if you turned out ALL optimizations. In all other cases, if(0) does will exist at runtime. – quetzalcoatl Feb 02 '13 at 01:29
  • @aniket: so swap int<->const placement, that was just sample, not copy-paste-useme – quetzalcoatl Feb 02 '13 at 01:29
  • isn't it a typical case where one should use asserts? – Étienne Feb 02 '13 at 01:30
  • 1
    hm.. it depends on what actually was tested and what was turned off. Assertions are debugging tools. When failed, they are meant to crash the code with meaningful message. They are not for "turning off" the code. So if those if(0) were to disable logging/throwingerrors - then yes, should be assertions. Otherwise not. – quetzalcoatl Feb 02 '13 at 01:31
  • The if(0) could be anything - including taking out sections from the code path – Thalia Feb 02 '13 at 01:34
  • But what's the advantage of using such if(0) versus commenting the code? I don't really understand the usefullness. – Étienne Feb 02 '13 at 01:35
  • continuous code changes... fast releases... don't want to completely give up on an idea... – Thalia Feb 02 '13 at 01:37
  • For example, that guarantees you that you will turn off that very specific block. With manually commenting it out, you can always make a mistake: you can ommit a line, or leave some brackets or semicolon that will damage your preceding if or while, etc. Moreover, IFs are hierarchical, while comments are not:) Also, as I shown you, you can actually automatically **calculate** an expression during compilation and the optimizer will cut the code depending on the result. The expression may be more complex than the preprocessor is able to handle. Still, for trivial if(0) there's little difference. – quetzalcoatl Feb 02 '13 at 01:39
  • 1
    @Mihaela: rewriting it into preprocessor's directives has a potential for faster compilation. This is always a good thing. It not necessarily will help at runtime, but will make your job friendlier and less time-consuming. Also, IDE will probably gray-out the disabled sections, so you will benefit a bit from increased readability. Unless you really need to calculate complex conditions, use preprocessor instead of hardcoded if-0 – quetzalcoatl Feb 02 '13 at 01:41
  • 1
    @EtienneCordonnier: If the `0` comes from a `const int`, you can easily turn the code on and off, with zero run-time overhead when it's off. Also, if you comment it out, bit rot can destroy the code. For example, someone might remove a variable the code uses, and you wouldn't catch it until you tried to enable the code. (It which time, it may be difficult to figure out what the original variable actually did, since it's now gone.) – David Schwartz Feb 02 '13 at 01:43
2

If you have code inside an if (0) block, the code generated by the compiler will be the same as if that block wasn't there on any reasonable compiler. The code will still be checked for compile-time errors. (Assuming you don't have any jump labels inside it or something weird like that.)

If you have code inside an if (1) block, the code generated by the compiler will be the same as if the code was just inside braces. It's a common way to give a block of code its own scope so that local variables are destructed where desired.

If you ifdef out code, then the compiler ignores it completely. The code can be completely nonsense, contain syntax errors, or whatever and the compiler will not care.

David Schwartz
  • 179,497
  • 17
  • 214
  • 278
2

Typically, #if 0 is used to remove code whilst still keeping it around - for example to easily compare to options, I sometimes do:

#if 1
   some sort of code 
#else
   some other code
#endif

That way, I can quickly switch between the two alternatives.

In this case, the preprocessor will just leave one of the two options in the code.

The constructs of if(0) or if(1) is similar - the compiler will pretty much remove the if, and in the case of 0 also remove the rest of the if-statement.

I think it's rather sloppy to leave this sort of stuff in "completed" code, but it's very useful for debugging/development.

Say for example you are trying a new method for doing something that is much faster:

if (1) 
{
   fast_function();
}
else
{
    slower_function();
}

Now, in one of your testcases, the result shows an error. So you want to quickly go back to slower_funcion and see if the result is the same or not. If it's the same, then you have to look at what else has changed since it last passed. If it's OK with slower_function, you go back and look at why fast_function() is not working as it should in this case.

Mats Petersson
  • 126,704
  • 14
  • 140
  • 227
  • If you use a decent version control system, you can keep different branches wit/without said variants around. – vonbrand Feb 02 '13 at 01:35
  • Absolutely, but if you have other changes in the same file, it can get quite messy after a while. It's fine if you are the only one working on the code, but when there are dozens of others, it's often hard enough to keep up to date with other peoples changes, without having to try to dig out a five versions old version of your source that you are trying to make better, that won't compile because some other changes has happened. I'm by no means suggesting that these sort of things get left in the code permanently - just while making sure the new code works like the old code. – Mats Petersson Feb 02 '13 at 01:45
  • well.. saying the same briefly, `if(0) { ... }` is much quicker to write than `#define OOPS ... #ifndef OOPS .. #endif` :)))) – quetzalcoatl Feb 02 '13 at 01:54
1

It's true (depending on your build settings and preprocessor).

Putting debug code in #ifdef _DEBUG (or similar) is a standard way to keep these completely out of your release builds. Usually the debug build #defines it, and the release build does not.

Usually, though, a compiler should also remove code such as if (0), if given the proper optimization flags, but this puts extra work on the compiler, and on the programmer (now you have to go change them all!). I'd definitely leave this to the preprocessor.

Plynx
  • 11,341
  • 3
  • 32
  • 33
1

You're correct. If you compile with #define DEBUG 0 then you will be actually removing all the #if DEBUG blocks at compile time. Hence, there will be lot less code, and it will run faster.

Just make sure you release your code after making #define DEBUG 0 at release time.

A good optimizing compiler(GCC, MSVC) will remove if(0) and if(1) from code completely... the translation to machine code will NOT test for these conditions...

Aniket Inge
  • 25,375
  • 5
  • 50
  • 78