Posted By: Prodev | May 31st, 2004 @ 12:35 PM
page 1 of 1
Comments: 13 | Views: 19321
Prodev
Prodev
Smile, I see you!

Hi, a best practice and technical inquiry on the decision to use a switch statement over sequential ifs.

I may be performing tests to answer these questions, however if any of you know these please save me the time.

// option 1.      (use for conditions where only a single 
//                     set of actions
//                      per statement call are required)

private void SomeMethod(string someCondition)
{
   switch(someCondition)
   {
      case "a":
      case "b":   
         //do something
         break;
      case "c":
         //do something else
         break;
      case "d":
         // and so on 
         break;
      default: // a global "else"   ???
         break;
   }
}


// option 2.   ( use for more complex logic,
//                  multiple conditions with actions without exiting the
//                statement)

private void SomeMethod(string someCondition)
{
   if(someCondition == "a")
   if(someCondition == "b")
   {
         //do something
   }      
   if(someCondition == "c")
   {
         //do something else
   }      
   if(someCondition == "d")
   {
         //and so on   
   }
   else
   {
        
   }
}
      

Aside from the obvious uses (see comment's for each option), when two statements are performing a sequential search for a condition match I have these questions in mind...

1. Is there a use case making one more desirable than the other? Say such as performance at compile|run time due to a large number of options?

2. This is specifically about optimization of switching and state-management. I require "Best Practices" and information with respect to using c-style (specifically c#) statements for optimization of "Conditional Branching Statements" where a large quantity of options and sub-logic are present.

3. Lastly, consider that far more complex logic calls to nest switches and if statements, and I would like your experiences with that as well.

TIA,

JB


PS, consecutive switches vs consecutive ifs?

private void SomeMethod(string someCondition)
{
   switch(someCondition)
   {
      case "a":
      case "b":   
         //do something
         break;
   }
   switch(someCondition)
   {
      case "c":
      case "d":   
         //do something
         break;
   }
}

JB

I think your seriously over-complicating this.. Just:

If you have to do more than three comparisons against the same data then use switch.
christopher
christopher
Oooh, Shiny
nit: your two examples are not equiv:
switch(someCondition)
   {
      case "a":
      case "b":   
         //do something
         break;...
will work as "when a or b, do something", but:
  if(someCondition == "a")
   if(someCondition == "b")
   {
         //do something[...]
will perform something iff a and b are both true. i think you want:
  if(someCondition == "a" || someCondition == "b")
   {
         //do something[...]

In this particular example (with my correction) both should be nearly identical if a is true, since both in the worst case emit a jump statement to the beginning of the a code block. But if b is true, things could be different, since a switch statement might possibly optimize away the comparison with a via lookup table. But this is trading memory for performance, so i'm not sure compilers use that trick. .NET does use lookup tables for string switches, however (a hash-table like structure), so switch is probably faster for large cases consisting of strings (like your example). But of course, string comparison is also optimized with string interning (Avoiding IComparable invocation, in theory).

I would go out on a limb and say switch statements are always at least as fast as a set of if statements for a fair number of items, and your logic involves lots of or's, and your if's are properly ordered and optimized so as to put the least likely candidates first.

Plus, switches are very semantically specific, and I like using the most explicit operator for the meaning of my code. (code is Documentation)==true, after-all.

For complex high-perf boolean logic, your better served with a single logical concatenation of boolean functions, which almost entirely avoids 'jump' instructions, causing your code to run much faster on modern 'fat-cache' processors. This is most effective for nested if's in tight loops, i've seen 1.5x performance increases by using this method in certain projects. Unfortunately, it is very hard for some programmers to follow boolean logic (!?!?) so I dont use it as much anymore.

edit:
For an enormous amount of options, you might want to look into binary trees or something in order to perform fast lookups. having such huge switches in code might be a maintainence problem anyway; so having a tree means you can store your choices in a file, and 'prime the tree' at startup.
samdruk
samdruk
samdruk
There's enormous and there's enormous. I can say that most modern compilers internally transform all conditional logic into a similar structure, so use away. There are some corner cases, however, and no compiler can exactly predict your use case. As christopher mentions, cache performance is a serious consideration in certain types of code. If you want a guide, build a quick program to generate comparable statements that contain the number of conditionals you expect. However, if you simply must wring every last erg of perf out of it, you should re-compare for a snapshot of your actual code when you have your app running. There's a host of things that can swing the numbers.
GooberDLX
GooberDLX
Avatar.Image = Jake.GetFemales();

Present compiler logic would defintely be able to handle the statements fairly, as have been said previously...

Code readability is basically what we have to put up with now-a-days... They used to teach us in school with If statements and Switch statements, just making sure we do as little checks as needed... and that the compiler would help us with the rest

Jake

Can I play Devil's Advocate at this point and say that if you have a particularly large or complicated switch construct you should probably consider redesigning your classes to make better use of inheritance.
samdruk
samdruk
samdruk

I should mention: if you really can't get the perf you need out of the compiler after testing (I'd be shocked, but it's possible) then you might want to look into generating computed jump tables.

jsrfc58 wrote:
That is what I was wondering about...if you could create some type of "lookup table" where you would go directly from your incoming condition straight to a function (for instance) instead of testing against a large set of "if" statements individually (or something like that).  You know, back in the olden days...they used to have this statement, ON-GOTO, where you could write ON x goto 100, 200, 300, 400...okay, I'll shut up now. 


Inheritance and Virtual Functions...

You know it makes sense. Wink
samdruk wrote:

I should mention: if you really can't get the perf you need out of the compiler after testing (I'd be shocked, but it's possible) then you might want to look into generating computed jump tables.



That is what I was wondering about...if you could create some type of "lookup table" where you would go directly from your incoming condition straight to a function (for instance) instead of testing against a large set of "if" statements individually (or something like that).  
pauluskc
pauluskc
watch out for the glare from the forehead!
I know I'm just the new guy, but if there's a huge number of options, say a system where there is an account number assigned to people that they use through a universal login and from that login get sent to different data or email or to porn or to whatever their account # dictates, what about utilizing a database or hash?  The database server/system can handle a larger number of options than a hash in memory but either one can be your case/if engine.  Please bear with me, just using general programming terms as I use several diff ones and I'm sure this can be done in different languages..

Example with database:

  table login {
    acct bigint,
    function char(30),
    [...]
  }

  sql = "SELECT function FROM login WHERE acct=" _
       & someCondition

  rs = runSQL(sql)

  eval(rs["function"])


--
That's the general gist of it - you store the function call in a table and reference that table by the somecondition.  So if condition a calls function bob, and condition a0e calls function frank, it can all be handled to the limits of the database (depending on your systems, could be millions->billions of conditions and functions).  if indexed properly, pretty quick (how does google find stuff in the middle of billions of web pages in 0.09 seconds)?

This would be the lookup table in one sense.

-------
Example using hash:   (no, not hashish)

janet = array(
         bob=>'function1()';
         frank=>'function12()';
        )
eval(janet[someCondition])

--
This is essentially another lookup table that is faster than the database query as it just accesses local memory but is limited by that local memory.  It is similar to the case statement, but you do have to add a little more error handling logic into that (mainly to cover the Else functionality).  But these are cool because you can populate them from a database table or by hand in another function or include file or whatever and have it somewhat dynamic, yet easily so.

As you can tell, I do like databases, especially for managing large datasets.  But hashes can be setup without using a database, just comes into code management which can be done with files or functions or just one big phat hash table (woo-hoo!).

as a side note:  what would make the if, if, if, else much faster than how it was written is with if, else if, else if, else  then it's a one time comparison instead of repetitive comparisons.


AndyC wrote:
jsrfc58 wrote: That is what I was wondering about...if you could create some type of "lookup table" where you would go directly from your incoming condition straight to a function (for instance) instead of testing against a large set of "if" statements individually (or something like that).  You know, back in the olden days...they used to have this statement, ON-GOTO, where you could write ON x goto 100, 200, 300, 400...okay, I'll shut up now. 


Inheritance and Virtual Functions...

You know it makes sense. Wink