2

I was coding away today when I came across something I do all the time without thinking as wondered if it had any after affects.

Here are two ways of doing the same thing

if(foo != true)
{
bar ++;
}

if(foo == true)
{
}
else
{
bar ++;
}

Now I know the compiler would probably optimise this to the same thing but I want to know the difference because you cannot always count on them.

My question is really would the second option incur some kind of penalty because it adds another command to the check ?

Yes it was a typo.

Skeith
  • 2,512
  • 5
  • 35
  • 57
  • 7
    There's a bug in the second example, you meamt `if (foo == true)`. Actually, you should just just use `if (foo)` and `if (!foo)`. –  May 27 '11 at 13:20
  • 3
    Micro-optimisation = fail. Most times, never ever think about micro-optimisation unless profiling proves it's needed. – C. K. Young May 27 '11 at 13:20
  • 2
    @Chris I disagree. If, all other things being equal, one variant **always** performs better then there’s good incentive to know this and always use this variant. Compare `x++` vs. `++x`. – Konrad Rudolph May 27 '11 at 13:23
  • The command line for GCC to see the generated assembler is `gcc -S `. Using it removes all doubt. – JUST MY correct OPINION May 27 '11 at 13:23
  • is the typo (= instead of ==) intentional? – RvdK May 27 '11 at 13:25
  • @Konrad: But it doesn't perform better. With any halfway decent compiler, it will most propably be identical - and in **many, many** cases it doesn't matter *even if* the compiler doesn't optimize anything thanks to the 80-20 rule. That doesn't mean you should introduce redundant code because you know it won't matter. You should simply write the most readable and maintainable code regardless of any potential, tiny differences in efficiency. –  May 27 '11 at 13:28
  • @delnan In this case, yes, it doesn’t perform better. But the question is still worth asking, and in particular micro-optimization != automatic fail. Some micro-optimizations are worth knowing. – Konrad Rudolph May 27 '11 at 13:31
  • possible duplicate: http://stackoverflow.com/questions/3702903/portable-branch-prediction-hints – Paul Groke May 27 '11 at 13:33
  • 1
    @Konrad: Some micro-optimizations are worth knowing if they apply. For 90% of the code most of us write, they don't. And if you're writing software that's performance critical, you still get much more performance improvements by larger-scale optimizations instead of trying to "optimize" every single line. Yeah, sometimes you have to micro-optimize the living daylight out of something. But that's the exception and hence it shouldn't be *promoted* in any way. If some code is really **too** slow, optimize it. Otherwise, don't even worry. –  May 27 '11 at 13:35
  • @delnan All of this is really irrelevant here, sorry. **If** either of the above codes were indeed more efficient than the other *even by a single cycle*, then this would constitute a powerful argument for using it exclusively, just as the potential advantage of prefix-`++` in *some* special cases constitutes enough reason to make “always use prefix-increment” an established best-practice in C++. Yes, most of the time it doesn’t matter but the practice still prevails because there’s simply no draw-back, and no need to ever second-guess it. – Konrad Rudolph May 27 '11 at 13:43
  • @Konrad: Agree about the things that are "always" true, like the `++x` vs `x++`, and using `const` variables whenever feasible (e.g., some COW implementations of strings copy the string when calling `operator[]` on a non-const string, rather than returning a proxy object). But those are not the cases that newbies usually obsess about (and sometimes I wish they did a little more). :-) – C. K. Young May 27 '11 at 13:49
  • @Chris @delnan @Konrad First off the question was an attempt to increase my knowledge base, anyone who dismissed learning is an idiot. secondly on an embedded system with 16kb memory it does make a difference a big one in fact. – Skeith May 27 '11 at 14:00
  • @Konrad: On the subject of `++x`, yes you can find cases where it matters. I've *never* encountered one. Often the argument goes like "If it might save you a micro-penny, then you should always do it." Well, OK, but it starts to sound like what [Henry Kissinger supposedly said about academic disputes](http://ask.metafilter.com/80812/Academic-politics-are-vicious-because-the-stakes-are-so-low). – Mike Dunlavey May 27 '11 at 18:12
  • @Mike You never used a non-trivial iterator? Or you’ve never used one in a high-performance situation? FWIW my perception may be skewed because my domain is performance sensitive – and for me it matters all. the. time. Then again, why would one use C++ if performance didn’t matter? Sounds masochistic. ;-) – Konrad Rudolph May 28 '11 at 11:34
  • @Konrad: Generation gap alert :-) I'm sure you've seen this [war story](http://stackoverflow.com/questions/926266/performance-optimization-strategies-of-last-resort/927773#927773). Iterators are fine, but at the first sign of being a performance issue - over the side they go. – Mike Dunlavey May 28 '11 at 14:06
  • @Mike I’m working on a generic library. We **cannot** just change the public API for some cases (and not for others), the whole point of the API is that it’s *general* (and still provides maximum performance). And luckily, we don’t need to. I also think that since 1993, compilers (and in particular their inlining) have improved tremendously, to the point that you don’t have to compromise generality for the sake of performance. Which, again, is the whole point of C++. Otherwise I would use a simpler language. – Konrad Rudolph May 29 '11 at 11:16
  • @Konrad: Somehow I've managed to get on the other side of a big divide. It amazes me that programming seems to be all about data structure, container classes (w. iterators) etc. etc. My style is to use as little data structure as possible. My stuff is performance critical, like yours, and always has been. I'm on a good-size team, and whenever there's a performance problem guess where it is - in the data structure. Notifications, serialization, zipping, grid controls, tree controls, GC, plotting objects, massive files, on and on... – Mike Dunlavey May 29 '11 at 12:42
  • @Mike So if I paraphrase, you seem to be saying: “whenever there’s a performance problem it’s where stuff is actually happening.” I might be thick, but I don’t see the alternative to algorithms and data structures. By definition, this is what all programs are *made of*. – Konrad Rudolph May 29 '11 at 15:05
  • @Konrad: I'm saying for any given problem there are a spectrum of ways to approach it, and people are not really taught to examine that and select on the basis of pros and cons. It's hard to generalize, but I nearly always see that people err on the side of more data structure, usually because of concerns about performance, and then that excess data structure ends up being the reason for poor performance. If you define the problem as requiring a certain data structure, then you're boxed in, but often the real-world problem doesn't specifically require more than a certain amount. – Mike Dunlavey May 29 '11 at 15:50
  • 1
    @Konrad: FWIW, I wrote a [book about all this](http://www.amazon.com/Building-Better-Applications-Efficient-Development/dp/0442017405) because I felt so strongly about it. It's long out of print (and going for a ridiculous price on Amazon), but I'm trying to see if I can get it up on Google. The basic idea is don't look at software as algorithms & data structures. Look at them in information-theoretic terms (Shannon & Kolmogorov). Algorithm = channel. Data = storage of info between acquisition & need. Source text = encoding the problem. Then you have more freedom of representation. – Mike Dunlavey May 29 '11 at 16:30
  • @Mike I’m still not entirely sure that I understand your vocabulary because I’ve been taught (and live) that code = algorithms + data structure (+ boiler plate). But I suspect that you could be referring to the bottleneck of piping the data from one data structure to the next (“friction loss”). Or something like that. Your book sounds fascinating anyway. – Konrad Rudolph May 29 '11 at 18:15
  • @Konrad: That's conventional. Having survived the MIT wringer, I was looking for something more basic, and if computation has an underlying quantitative "physics", information theory, entropy, etc. looks like it. So I've made a pest of myself on SO talking about simplicity, performance, algorithms, and domain-specific languages. In mechanical engineering, we had physics and thermodynamics (plus math) to base everything on. In CS/AI, I felt a need for an underlying science, and went looking for it (FWIW). – Mike Dunlavey May 29 '11 at 18:46

7 Answers7

10

Neither is good. Apart from the fact that the second contains a typo (= instead of ==), comparing booleans to constants is just redundant. Just test their values directly:

if (! foo) …
// Instead of
if (foo != true) …

// or

if (foo) …
// Instead of
if (foo == true) …

First of all, it removes the possibility of creating bugs though typos (as you have graciously shown). But apart from that it’s just more logical.

(Note however that it’s not more efficient. The statements are strictly equivalent.)

Konrad Rudolph
  • 530,221
  • 131
  • 937
  • 1,214
  • I disagree. Unless the first branch is effectively empty, and some branch prediction is going on, generated code is going to differ. I'm editing my answer for more details, if you're interested. – King_DuckZ May 27 '11 at 14:06
  • @King Yes; that was the OP’s point though (as far as I understand it): the first branch is empty. That said, regardless of branches my answer *still* counts. There is **never** a reason to equality compare to a boolean constant explicitly. – Konrad Rudolph May 27 '11 at 14:22
5

Those have the same effect and the same machine code will likely be emitted by any decent compiler - checking a boolean and selecting what to do will be done the fastest way possible. "Adding else" is not how it works inside - that's just the if-else statement that must have certain effect, it's up to the compiler how to achieve that effect. Just adding a keyword doesn't necessarily cause extra code emission.

If you really care you should inspect the emitted machine code and see what the compiler emits in each case.

sharptooth
  • 167,383
  • 100
  • 513
  • 979
3

I assume you did a typo in writing = instead of ==. Those blocks are not equivalent: modern CPUs try to pre-fetch and pre-execute assembly instructions as a program runs, and when a conditional jump is met, the CPU pre-runs the code it would execute if the condition is true.

So I usually put code that is likely to be executed more often on top, although branch prediction and other optimizations the compiler does might change this and do a much better job.

Edit: Please have a look at Branch prediction on Wikipedia, and especially to the Static prediction section. Unless you're sure 100% of what optimizations the compiler will do and what CPU will run your code, you're best bet is to assume that the first block is run faster. In the worst case, you get no benefit and no loss. In the best case, you're making code that is easier to read and runs faster.

Counter-example:

if (someCondition)
    AssertNotReached();
else
    DoRealWork();
King_DuckZ
  • 238
  • 1
  • 6
  • If the code is run anywhere near enough to be important from a performance point of view on a modern x86 CPU (can't speak for ARM but I'd wager too) and there's a noticeable statistical deviation in which branch is taken, the branch predictor will worry about it. So I wouldn't waste even a second thinking about this - I think it's fair to say that this is pretty much THE prime example of a micro optimization. – Voo May 27 '11 at 16:39
  • The poster asked if there is a difference, and what I'm trying to say is that it depends on the underlying architecture and the compiler. If that difference is relevant to Skeith is beyond the purpose of my answer. I do agree however that unless you meet a no brainer case like the one I pointed out (which has the side effect to be more readable as well), putting effort in reordering branches is a loss of time. – King_DuckZ May 27 '11 at 16:53
  • Sorry I thought you actually championed putting the most often executed branch at top. I agree with your counter example - in that case the readability is quite improved which is always worth a second or two :) – Voo May 28 '11 at 16:25
2

Both are not same, in your second code always the if statement gets executed as its given as foo = true.

NirmalGeo
  • 773
  • 4
  • 12
1

There is a big difference, the first inc's bar base on a condition, the second sets foo to true.

if(foo != true)
{
bar ++;
}

if(foo = true) //this sets foo to true and takes the true branch of the statement, any optmizing compiler will remove the else section
{
}
else
{
bar ++;
}

Assuming that the above was a typo, they are the same at machine level, but they are different in terms of parse time and time it takes to write out.

Necrolis
  • 25,836
  • 3
  • 63
  • 101
0

It depends on the compiler. In the worse case, it adds a couple extra jump instructions.

Daniel A. White
  • 187,200
  • 47
  • 362
  • 445
0

Semantically this is the same. What happens beyond this depends on the compiler. Usually, with an if statement, the compiler will assume that the "if" branch is taken more likely and optimize for this. If this happens, the second example would indeed have some performance penalty. But it really depends on many thing we don't know from the context of the question.

Waldheinz
  • 10,399
  • 3
  • 31
  • 61