views:

398

answers:

5

In PHP switch statements, does placing more common cases near the top improve performance?

For example, say the following function is called 1,000 times:

<?php 
function foo_user ($op) {
    switch ($op) {
     case 'after_update':
     //Some Stuff
     case 'login':
     //Some other Stuff
    }
}

If in 990 of the 1,000 of the times the function is called the $op argument is 'login', would performance improve by having case: 'login' above case 'after_update': in the switch statement? For example, would the code after case 'after_update': be ignored if $op = login was passed?

I've run some informal tests on this idea, but the difference has been negligible -- perhaps because the code after case: 'login' and case 'after_update': are both trivial. I'd prefer to avoid setting up a more extensive test with non-trivial operations if someone knows the answer outright.

This is specifically a Drupal question, but I imagine it could be addressed by anyone who is familiar with optimizing PHP.

+8  A: 

This is likely going to be called a micro optimisation. I don't believe there would be a large difference.

However, order does mean a lot to the logic of the switch case if you allow the cases to fall through, example

switch ($var) {

    case 0:
    case 1:
       do_it();
       break;
    case 2:
       do_it_else();
       break;

}

The order is important, the case will fall through and execute any code until it hits a break.

I wouldn't be concerned about the speed of the switch case, unless you had say 100 possible cases. But if then, it'd be likely you should refactor your code.

alex
Great answer. Thanks!
anschauung
+2  A: 

If you do not use break; to close each statement PHP will continue to evaluate cases until the end of the switch block which may impact performance in a large enough block. The order then becomes important for getting the behavior you are looking for.

From PHP.Net:

The switch statement executes line by line (actually, statement by statement). In the beginning, no code is executed. Only when a case statement is found with a value that matches the value of the switch expression does PHP begin to execute the statements. PHP continues to execute the statements until the end of the switch block, or the first time it sees a break statement. If you don't write a break statement at the end of a case's statement list, PHP will go on executing the statements of the following case. For example:

<?php
    switch ($i) {
        case 0:
            echo "i equals 0";
        case 1:
            echo "i equals 1";
        case 2:
            echo "i equals 2";
    }
?>

I would caution against relying on this behavior though and use break; for each case as that will remove some ambiguity when you revisit the code later.

Rob Allen
A: 

Your switch statement is equivalent to:

if ($op == 'after_update') {
  // Do stuff
} else if ($op == 'login') {
  // Do stuff
}

Performance difference between switching the statements is negligible.

jakemcgraw
Not true. There's no "break"s so it's not equivalent.
mgroves
Thanks. But while that's certainly true (albeit a pain if you have more than a few cases to test) it doesn't really address my question.
anschauung
A: 

Usually, it's recommended to write the most likely case first, the second one next...

but like Alex wrote,

This is likely going to be called a micro optimisation. I don't believe there would be a large difference.

Luc M
+3  A: 

You will need a whole lot more than 1000 cases to notice a difference, but yes, there is a difference. I wrote up a test:

function test_switch($value) {

  $startTime = time() + microtime();

  for ($i = 0; $i < 10000000; $i++) {

    switch($value) {

    case "abcdefg":
      $j = $j + 1;
      break;
    case "hijklmno":
      $j = $j + 1;
      break;
    }
  }

  $endTime = time() + microtime();

  echo "Total time for argument $value: " . ($endTime - $startTime) . "<br>\n";
}

test_switch("abcdefg");
test_switch("hijklmno");

That is 10 million executions of the switch statement. The output is:

Total time for argument abcdefg: 3.99799704552
Total time for argument hijklmno: 5.38317489624

So there is a difference, but it won't be noticeable until you reach on the order of 10 million executions, depending on your processor of course.

Matt Bridges