Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

What is quickest? Conditions inside a loop or outside?

What is quickest? To do a condition based on a variable outside the loop inside a loop or outside, or does it even matter (does the compiler do it for you), or should you maybe use a completely different workaround?

Example #1 with the condition inside the loop (a single foreach):

$test = 2;  
foreach ($list as $listItem) {
    if ($test == 1) {
        $listItem .= " - one";
    } else if ($test == 2) {
        $listItem .= " - two";
    } else if ($test == 3) {
        $listItem .= " - three";
    }
}

Example #2 with the condition outside the loop (quite ugly with multiple foreach's):

$test = 2;  
if ($test == 1) {
    foreach ($list as $listItem) {
        $listItem .= " - one";
    }
} else if ($test == 2) {
    foreach ($list as $listItem) {
        $listItem .= " - two";
    }
} else if ($test == 3) {
    foreach ($list as $listItem) {
        $listItem .= " - three";
    }
}
like image 259
h2ooooooo Avatar asked Sep 18 '12 15:09

h2ooooooo


2 Answers

As you probably could've guessed, example #2 is quicker, and the compiler doesn't do it for you.

Overview

condition inside or outside loop graph


Performance tests

Example #1, condition inside of loop:

Time taken: 0.2501 seconds
Time taken: 0.2336 seconds
Time taken: 0.2335 seconds
Time taken: 0.2319 seconds
Time taken: 0.2337 seconds 
Average:    0.2364 seconds

And the code:

<?php
    $list = array();
    for ($i = 0; $i < 1000000; $i++) {
        $list[] = md5(rand(0, 100000) * $i);
    }
    $a = microtime(true);
    $test = 2;
    foreach ($list as $listItem) {
        if ($test == 1) {
            $listItem .= " - one";
        } else if ($test == 2) {
            $listItem .= " - two";
        } else if ($test == 3) {
            $listItem .= " - three";
        }
    }
    echo "Time taken: " . number_format(microtime(true) - $a, 4) . " seconds";
?>

Example #2, condition outside of loop:

Time taken: 0.1424 seconds
Time taken: 0.1426 seconds
Time taken: 0.1364 seconds
Time taken: 0.1348 seconds
Time taken: 0.1347 seconds
Average:    0.1382 seconds

And the code:

<?php
    $list = array();
    for ($i = 0; $i < 1000000; $i++) {
        $list[] = md5(rand(0, 100000) * $i);
    }
    $a = microtime(true);
    $test = 2;
    if ($test == 1) {
        foreach ($list as $listItem) {
            $listItem .= " - one";
        }
    } else if ($test == 2) {
        foreach ($list as $listItem) {
            $listItem .= " - two";
        }
    } else if ($test == 3) {
        foreach ($list as $listItem) {
            $listItem .= " - three";
        }
    }
    echo "Time taken: " . number_format(microtime(true) - $a, 4) . " seconds";
?>

Alright, that's a pretty obvious win for conditions outside of loops, but what if we try to compare with a boolean instead of an int?

Example #3, condition inside of loop, but conditioning with a boolean instead:

Time taken: 0.1845 seconds
Time taken: 0.1821 seconds
Time taken: 0.1745 seconds
Time taken: 0.1777 seconds
Time taken: 0.1767 seconds
Average:    0.1791 seconds

And the code:

<?php
    $list = array();
    for ($i = 0; $i < 1000000; $i++) {
        $list[] = md5(rand(0, 100000) * $i);
    }
    $a = microtime(true);
    $test = 2;
    $result1 = ($test == 1);
    $result2 = ($test == 2);
    $result3 = ($test == 3);
    foreach ($list as $listItem) {
        if ($result1) {
            $listItem .= " - one";
        } else if ($result2) {
            $listItem .= " - two";
        } else if ($result3) {
            $listItem .= " - three";
        }
    }
    echo "Time taken: " . number_format(microtime(true) - $a, 4) . " seconds";
?>

Interesting. What if we use a built in function such as array_walk?

Example #4, array_walk:

Time taken: 0.4950 seconds
Time taken: 0.4946 seconds
Time taken: 0.4947 seconds
Time taken: 0.4937 seconds
Time taken: 0.4918 seconds
Average:    0.4940 seconds

And the code:

<?php
    function append_string($value, $suffix) {
        return $value . $suffix;
    }

    $list = array();
    for ($i = 0; $i < 1000000; $i++) {
        $list[] = md5(rand(0, 100000) * $i);
    }
    $a = microtime(true);
    $test = 2;
    if ($test == 1) {
        array_walk($list, "append_string", " - one");
    } else if ($test == 2) {
        array_walk($list, "append_string", " - two");
    } else if ($test == 3) {
        array_walk($list, "append_string", " - three");
    }
    echo "Time taken: " . number_format(microtime(true) - $a, 4) . " seconds";
?>

What?! You might think an inbuilt function would abstract it over a C or C++ function, and it might very well, however the problem is that the function calls make this method very very slow.


Pros/cons

Example #1 pros

  • You don't have to declare the loop multiple times (which might be annoying if you have several conditions)

Example #1 cons

  • It is the slowest one of all the first 3 tests (71% slower than example #2 and 32% slower than example #3)

Example #2 pros

  • The very quickest one of them all

Example #2 cons

  • It requires you to declare the loop several times (as many times as the amount of conditions you have)

Example #3 pros

  • You don't have to declare the loop multiple times, just like in example #1
  • It is quicker than #example 1

Example #3 cons

  • It is still slower than #example 2 (about 30%)
  • It looks more messy

Example #4 pros

  • The syntax looks pretty and easy

Example #4 cons

  • It is the slowest one of the lot (about 100% slower than the second slowest (example #1)). Possibly due to it having to call a function 1,000,000 times.
  • You're out of the global scope and will have to global all variables from the global scope.

Conclusion

The PHP compiler doesn't do it for you, and if you want performance you should go for example #2, however this test is made with an array that has a million entries. Chances are that your array isn't going to have that many entries, and therefore you might go with example #1 or example #3 instead. Oh, and do not go for example #4.

like image 182
h2ooooooo Avatar answered Sep 20 '22 11:09

h2ooooooo


If the results of the conditional tests aren't going to change during the loop, then moving the tests out of the loop is always going to be faster. (Doing something once is obviously faster than doing that same thing multiple times).

However, when moving the conditionals out of the loop you don't want to repeat the same code over and over for each case. This is where array_map comes in. For example:

function append_str($value, $suffix) {
    return $value . $suffix;
}

if ($test == 1)
    array_map("append_str", $list, " - one");
else if ($test == 2)
    array_map("append_str", $list, " - two");
else if ($test == 3)
    array_map("append_str", $list, " - three");

This allows you to check the conditionals once and reuse your code for handling each case. As shown in the example you can parametrize the function call for each case, and you can make the function (append_str) as complex as you want.

Note: it would be worth benchmarking this for comparison, as array_map's looping is probably performed in C/C++ which should be faster than native PHP loops (similar to Perl's map function).

like image 40
Brad Mace Avatar answered Sep 21 '22 11:09

Brad Mace