When I was a kid I loved magic - simple card tricks to Copperfield’s million dollar illusions, I was easily mesmerized and mystified. People have been bending what we THINK is reality for years. Take a look at the picture - Which way does this window face. Are the people on the inside looking to the left or looking to the right? Are you looking up into the window, or are you looking down into the window?
Same thing goes for programming – what we type out in code does not always translate to "what is" during execution; especially if you’re dealing with a compiler between what you write and assembled code delivered into production.
Take the battle between a couple of conditional processing structures provided in C# (and most other languages): Who’s “better” regarding efficiency and elegance - a switch (case) statement or If-Else conditional statement. The GoOracle would tell you this is a no brainer, and logically we would assume that switch would perform better because the framework does not have to evaluate each statement in the flow.
Let’s assume otherwise and challenge the norm given the following code in pre-compiled form:
private static void SwitchSingleCondition() {
Stopwatch timer = new Stopwatch();
timer.Start(); // Start the timer
string compare = "a";
for (long i = 1; i <= 10000; i++) {
switch (compare) {
case "a":
compare = "b";
break;
case "b":
compare = "a";
break;
default:
compare = "a";
break;
}
}
timer.Stop(); // Stop the timer
Console.WriteLine("Execution time for 'Switch Single Condition' was {0:F1} microseconds.", (decimal)timer.Elapsed.Ticks / 10M);
}
private static void IfSingleCondition() {
Stopwatch timer = new Stopwatch();
timer.Start(); // Start the timer
string compare = "a";
for (long i = 1; i <= 10000; i++) {
if (compare == "a") {
compare = "b";
} else if (compare == "b") {
compare = "a";
} else {
compare = "a";
}
}
timer.Stop(); // Stop the timer
Console.WriteLine("Execution time for 'If Single Condition' was {0:F1} microseconds.", (decimal)timer.Elapsed.Ticks / 10M);
}
When executed we get the following results:
Why did the “if statement” run more efficiently than the switch? Let’s pull back the curtain and take away the mirrors – this is what the code looks like after compilation (Reflector you rock!):
private static void SwitchSingleCondition()
{
Stopwatch timer = new Stopwatch();
timer.Start();
string compare = "a";
for (long i = 1L; i <= 0x2710L; i += 1L) {
string CS$0$0000 = compare;
if (CS$0$0000 == null) {
goto Label_004B;
}
if (!(CS$0$0000 == "a")){
if (CS$0$0000 == "b"){
goto Label_0043;
}
goto Label_004B;
}
compare = "b";
continue;
Label_0043:
compare = "a";
continue;
Label_004B:
compare = "a";
}
timer.Stop();
decimal CS$0$0000 = timer.Elapsed.Ticks / 10M;
Console.WriteLine("Execution time for 'Switch Single Condition' was {0:F1} microseconds.", CS$0$0000);}
private static void IfSingleCondition()
{
Stopwatch timer = new Stopwatch();
timer.Start();
string compare = "a";
for (long i = 1L; i <= 0x2710L; i += 1L) {
if (compare == "a"){
compare = "b";
}
else if (compare == "b"){
compare = "a";
}
Else {
compare = "a";
}
}
timer.Stop();
decimal CS$0$0000 = timer.Elapsed.Ticks / 10M;
Console.WriteLine("Execution time for 'If Single Condition' was {0:F1} microseconds.", CS$0$0000);
}
Are you kidding me – the “if” logic even looks more elegant! So what happened? It seems that behind the smoke, mirrors, and assumptions the .NET framework looks at the code that was written and restructures it to what it thinks will be more efficient using if-else conditions, “goto” statements, and a labeling system. Once your case statement exceeds 4 conditions the compiler will leave the code alone and execute it as a normal switch case.
It turns out that if-else-if statements can and most likely will be more efficient than switch statements when the number conditions do not exceed four. Things aren't always what they seem. Don’t be blinded by the magic – challenge your assumptions - until they become fact.
--nick