Which Is Faster In Assembler, Switch-Case or If-Elseif

Started by
19 comments, last by Spoonbender 19 years, 7 months ago
Quote:Original post by smr
A switch statement and an if-else-else if wouldn't necessarily compile out the same. A switch statement always compares against one expression that can be pre-calculated before any comparisons are made:

*** Source Snippet Removed ***

Examples one and two could potentially generate the same code. The left hand expression ((x + 100) % 4) doesn't need to be re-evaluated for each if statement, so it would work like a switch statement.

In example three the expression needs to be re-evaluated for each if statement. This would compile differently than a switch statement.

The compiler is a lot smarter than you think it is. Your third example:
/* example three */if(x - 10 == 100)    printf("one hundred\n");else if(x + 100 == 10)    printf("ten\n");

could easily be changed to:
if(x == 110)  printf("one hundred\n");else if(x == -90)  printf("ten\n");

And the compiler will easily catch such conditions and evaluate them so.

In time the project grows, the ignorance of its devs it shows, with many a convoluted function, it plunges into deep compunction, the price of failure is high, Washu's mirth is nigh.

Advertisement
Don't forget that it very much depends on how x is defined in the above case. If x is a global non-const variable it will have to be re-evaluated a lot since other code may have updated it. I don't *think* the case does this (thanks to jump tables), while the if probably will. Anyway, there's a lot of factors effecting the assembly output.
The best method is to actually check what is generated and then consider wether it's all good or wether you failed to provide enough information to the compiler for it to optimize your code as much as you would expect it to.
Your compiler probably has a lot better overview of your code than you do ;) It's just a matter of providing it with the correct information. With unspecific input you get unspecific output.
Quote:Original post by asp_
Don't forget that it very much depends on how x is defined in the above case. If x is a global non-const variable it will have to be re-evaluated a lot since other code may have updated it. I don't *think* the case does this (thanks to jump tables), while the if probably will. Anyway, there's a lot of factors effecting the assembly output.
The best method is to actually check what is generated and then consider wether it's all good or wether you failed to provide enough information to the compiler for it to optimize your code as much as you would expect it to.
Your compiler probably has a lot better overview of your code than you do ;) It's just a matter of providing it with the correct information. With unspecific input you get unspecific output.

Uh, it will only need to reevaluate it if x is a volatile variable. Any other case and the compiler only needs to evaluate it once, and in most cases will evaluate it only once.

In time the project grows, the ignorance of its devs it shows, with many a convoluted function, it plunges into deep compunction, the price of failure is high, Washu's mirth is nigh.

Quote:Original post by washu
The compiler is a lot smarter than you think it is. Your third example:
/* example three */if(x - 10 == 100)    printf("one hundred\n");else if(x + 100 == 10)    printf("ten\n");

could easily be changed to:
if(x == 110)  printf("one hundred\n");else if(x == -90)  printf("ten\n");

And the compiler will easily catch such conditions and evaluate them so.


Thanks for pointing that out. :-) I chose a bad example. Essentially what I was trying demonstrate is that you may have two completely unrelated expressions in an if-elseif-else block. It is possible using that construct that the compiler may have to generate more code.

Also, I don't know how true this is today, but for the if-elseif-else block you should make sure that the most likely possibility is the first in the list, followed by the second most likely and so on until the least likely scenario. This will reduce the number of expressions to evaluate for each iteration.

Switch statements can work better for large decisions, specially if the compiler can optimize it to use jump tables (or even multiple jump tables). Look at the assembly output

Another option that gives you advantage is to divide your decisions.

if(a==1) {     } else if(a==2) {     } else if(a==3) {     } else if(a==4) {     } else if(a==5) {     } else if(a==6) {     } else if(a==7) {     } else if(a==8)     {     }  if(a<=4) {         if(a==1)     {         }  else if(a==2)  {         }  else if(a==3)  {         }  else if(a==4)   {               }     }     else     {         if(a==5)  {         } else if(a==6)   {         } else if(a==7)  {         } else if(a==8)  {         }     }
Quote:Original post by antareus
You won't be able to tell which one is faster, and they may compile down to the same code anyway. Nano-optimization sucks, too.


Hah, that depends on the size of your switch statement, and how often you run it.

I could think of plenty of situations where it'd make a big difference. What if you're emulating a cpu? For every instruction, you need to look up its opcode, and you want that to go damn fast because, well, you do this for *every* emulated cycle.
Just for fun compile time switch....

Quote:


enum Case { value1, value2 };

template
class Switch {
public:
static inline void do_action()
{ /*default-statement*/ }
};

class Switch {
public:
static inline void do_action()
{ /*statement1*/ }
};

class Switch {
public:
static inline void do_action()
{ /*statement2*/ }
};

int main() {

const int I = 2;
Switch::do_action();

return 0;
}

Just for fun compile time switch....


enum Case { value1, value2 };template <Case c>struct Switch {static inline void do_action(){ /*default-statement*/ }};struct Switch<value1> {static inline void do_action(){ /*statement1*/ }};struct Switch<value2> {static inline void do_action(){ /*statement2*/ }};int main() {  const int I = 2;  Switch<I>::do_action();  return 0;}
Quote:Original post by Spoonster
Quote:Original post by antareus
You won't be able to tell which one is faster, and they may compile down to the same code anyway. Nano-optimization sucks, too.


Hah, that depends on the size of your switch statement, and how often you run it.

I could think of plenty of situations where it'd make a big difference. What if you're emulating a cpu? For every instruction, you need to look up its opcode, and you want that to go damn fast because, well, you do this for *every* emulated cycle.

Then don't use a switch or an if. There are much better ways of decoding instructions than a switch statement. table lookups combined with shifts for instance. Requires only a few if statements then.

Chances are that if you have a huge switch statement, or a huge set of if/else if/else you are looking at things wrong and may want to consider redesigning. There are, of course, cases where this isn't true, such as the Win32 WndProc.

In time the project grows, the ignorance of its devs it shows, with many a convoluted function, it plunges into deep compunction, the price of failure is high, Washu's mirth is nigh.

Premature optimisation is the root of all evil!
Everyone here can assume that you have not determined that this is a bottleneck in your code, as you have not told us that you have profiled and found that to be so.

Use whichever is most appropriate. Readability is far more important than saving a few clock cycles when initially developing something.

That said, a lookup table is fastest if it's possible to use it instead of a switch statement. But we can't tell that without seeing some code. Try-it-and-see is about the only way you're going to find out.

"In order to understand recursion, you must first understand recursion."
My website dedicated to sorting algorithms

This topic is closed to new replies.

Advertisement