Why is foreach so slow?

10,631

Solution 1

Maybe it has to do with the fact that foreach works on a copy of the array ?

Or maybe it has to do with the fact that, when looping with foreach, on each iteration, the internal array pointer is changed, to point to the next element ?

Quoting the relevant portion of foreach's manual page :

Note: Unless the array is referenced, foreach operates on a copy of the specified array and not the array itself. foreach has some side effects on the array pointer.


As far as I can tell, the third test you linked to doesn't do any of those two things -- which means both tests don't do the same thing -- which means you are not comparing two way of writing the same code.

(I would also say that this kind of micro-optimization will not matter at all in a real application -- but I guess you already know that, and just asked out of curiosity)

There is also one thing that doesn't feel right in this test : it only does the test one time ;; for a "better" test, it might be useful to test all of those more than once -- with timings in the order of 100 micro-seconds, not much is required to make a huge difference.
(Considering the first test varies between 300% and 500% on a few refreshes...)


For those who don't want to click, here's the first test (I've gotten 3xx%, 443%, and 529%) :

foreach($aHash as $key=>$val) {
    $aHash[$key] .= "a";
}

And the third one (100%) :

$key = array_keys($aHash);
$size = sizeOf($key);
for ($i=0; $i<$size; $i++) {
    $aHash[$key[$i]] .= "a";
}

Solution 2

I'm sorry, but the website got it wrong. Here's my own script that shows the two are almost the same in speed, and in fact, foreach is faster!

<?php

function start(){
    global $aHash;
    // Initial Configuration
    $i   = 0;
    $tmp = '';
    while($i < 10000) {
      $tmp .= 'a';
      ++$i;
    }
    $aHash = array_fill(100000000000000000000000, 100, $tmp);
    unset($i, $tmp);
    reset($aHash);
}

/* The Test */
$t = microtime(true);
for($x = 0;$x<500;$x++){
    start();
    $key = array_keys($aHash);
    $size = sizeOf($key);
    for ($i=0; $i<$size; $i++) $aHash[$key[$i]] .= "a";
}
print (microtime(true) - $t);

print ('<br/>');

$t = microtime(true);
for($x = 0;$x<500;$x++){
    start();
    foreach($aHash as $key=>$val) $aHash[$key] .= "a";
}
print (microtime(true) - $t);
?>

If you look at the source code of the tests: http://www.phpbench.com/source/test2/1/ and http://www.phpbench.com/source/test2/3/ , you can see that $aHash isn't repopulated to the initial data after each iteration. It is created once at the beginning, then each test is ran X times. In this sense, you are working with an ever growing $aHash for each iteration... in psuedocode:

iteration 1: $aHash[10000000000000]=='aaaaaa....10000 times...a';
iteration 2: $aHash[10000000000000]=='aaaaaa....10001 times...a';
iteration 2: $aHash[10000000000000]=='aaaaaa....10002 times...a';

Over time, the data for all the tests is getting larger for each iteration, so of course by iteration 100, the array_keys method is faster because it'll always have the same keys, where as the foreach loop has to contend with an ever growing data set and store the values in arrays!

If you run my code provided above on your server, you'll see clearly that foreach is faster AND neater AND clearer.

If the author of the site intended his test to do what it does, then it certainly is not clear, and otherwise, it's an invalid test.

Share:
10,631
Matchu
Author by

Matchu

Hello, world!

Updated on September 06, 2022

Comments

  • Matchu
    Matchu over 1 year

    PHPBench.com runs quick benchmark scripts on each pageload. On the foreach test, when I load it, foreach takes anywhere from 4 to 10 times as long to run than the third example.

    Why is it that a native language construct is apparently slower than performing the logic oneself?

  • Amber
    Amber about 14 years
    In addition, the non-foreach doesn't have to set variable values and then read them back out of the variables for $key and $val each loop - it just does the array lookups.
  • Matchu
    Matchu about 14 years
    And then the second also does impressively well, even though it seems to increment the pointer. (Also note, readers, that the first percentage here varies, as the test is re-run on page load.)
  • Dor
    Dor about 14 years
    Have you read how PHP variables handling works? derickrethans.nl/talks/phparch-php-variables-article.pdf
  • Matchu
    Matchu about 14 years
    At least, not for precision. But it seems fairly clear that foreach() is consistently slower, regardless of just how slow.
  • TravisO
    TravisO about 14 years
    I'm pretty sure most people ran this on their local PC's install. And while splitting hairs in performance is a waste of time, we're talking about a 3x-5x speed improvement, and in some scenarios that's a big deal.
  • Leif
    Leif over 12 years
    You are using a start index of 100000000000000000000000 in array_fill(). This number is too big for php and is silently adjusted to 0. Why do you do this?