views:

545

answers:

3

I just noticed that given the following code:

if (x.ID > 0 && !x.IsCool)

the Microsoft C# 3.0 (VS2008 SP1) compiler will optimize it to this:

if (!((x.Id <= 0) || x. IsCool))

This is on Debug build without Optimization enabled. Why does the compiler do that? Is it faster in terms of execution?

I used Reflector to find that out (I was actually looking for something different)

+8  A: 

I think these two expressions are exactly equivalent from a language semantics point of view. Both ways involve short-circuiting.

I am kind of flabbergasted that Andrew's answer already has ten upvotes; it sounds like nonsense to me, but perhaps I really am missing something subtle here.

EDIT

So just to sum up:

The OP's question asks "why does this optimization happen".

In fact there's no 'optimization' happening. The two C# source codes are logically equivalent. ".Net Reflector" or whatever other disassembly tool is perhaps as likely to decompile the same IL into one or the other. At the IL level, there's just a bunch of conditional jumps, and so there's not necessarily a way to know "which way is if and which is else" or other similar DeMorgan equivalences.

Fascinatingly, people are wildly happy to vote up or down answers to this question, even when (or perhaps because) the original question does not make much sense (or relies on a faulty assumption).

Happily, eventually the wisdom of the crowds (and smart individuals like @Mehrdad) prevails. Hurray for StackOverflow!

(I am making my answer a wiki, because I don't want rep for "storytelling about a question" when rep should be awarded to "good answers to a question". But I think the story of this question is interesting.)

Brian
@Brian - I was flabbergasted as well considering the fact that I was 100% incorrect :) That is what I get for posting off-the-cuff like that without thinking.
Andrew Hare
You’re absolutely right, a case differentiation reveals that the **exact same** branches are generated by both codes, hence the same short-circuiting. However, this is subtle and on first sight I actually thought that Andrew was right (then I did all the cases in my head).
Konrad Rudolph
:) I am glad that the design of StackOverflow (editing, wiki style, voting) means that eventually everything works out, even if it can be a rollercoaster ride during the first few minutes of a question. :)
Brian
+47  A: 

The C# compiler certainly does not generate an equivalent C# code for your snippet. It's compiled down to IL. Basically, what you are seeing (from Reflector, I guess) is the equivalent C# code that a decompiler spits out for that IL.

  1. The language specification does not say what an "unoptimized" code is. The C# compiler is allowed to generate any valid, functionally equivalent code. Even without optimization switch on, the compiler might do basic optimizations. Beside that, you can't say what is natural for the compiler and whether the compiler deliberately optimized it or not.

  2. The if statement as a whole is evaluated as a sequence of conditional branches based on the values of each individual expression specified in the "and" clause. The expression is not evaluated in a single code block with "and" instructions. The output of the decompiler is something inferred from those branches. The decompiler cannot always infer the original expression you wrote. It just outputs something equivalent.

Similarly, the difference between this snippet:

if (a) { something(); }
else { somethingElse(); }

and this snippet:

if (!a) { somethingElse(); }
else { something(); }

is not something you'd distinguish by seeing the compiled code.

Mehrdad Afshari
+1 This is the right answer and succinctly explained to boot. :)
Andrew Hare
I wanted to no if this actually is an "optimization" (hence the quotes).It's the || values together that I was wondering about. I thought IL would use real boolean or/and operations, but this is not the case. under the hood only a combination of ble.s, br.s and brtrue.s is used.
Johannes Rudolph
+6  A: 

The if compiles to a conditional jump opcode. Lifting the negation out of the expression allows it to be optimized out by swapping the conditional jump target with the fall through block.

Ants Aasma