Which one performs better? For or Foreach + Range?

9

In the two ways below, which one performs better?

  • For :

    for( $x=1; $x < 31; $x++ )
       echo $x . PHP_EOL;
    
  • Foreach + range :

    foreach( range(1,30) as $x )
       echo $x . PHP_EOL;
    
  • I know the difference will probably be in milliseconds, but it's worth the study since in a high-traffic application these milliseconds can make a difference.

    If there is a third form of performance even better, please point it out.

        
    asked by anonymous 08.05.2015 / 21:03

    2 answers

    6

    for has a slightly better performance slightly the foreach , in this case because function range returns an array of elements resulting from the iteration and accesses each item from it.

    See a comparison between the two forms in loop of 350000 iterations:

    function bench($func) {
        $tempo = -microtime(true);
        $func();
        $tempo += microtime(true);
        return number_format($tempo, 4);
    }
    
    function func1() {
        for($x = 1; $x < 350000; $x++) echo $x;
    }
    
    function func2() {
        foreach(range(1, 350000) as $x) echo $x;
    }
    
    $tempoDecorrido = bench('func1');
    echo "\n For => Tempo decorrido: {$tempoDecorrido} segundos \n";
    
    $tempoDecorrido = bench('func2');
    echo "\n Foreach => Tempo decorrido: {$tempoDecorrido} segundos \n";
    

    Result:

    12345678910....
    For => Tempo decorrido: 0.48403 segundos
    1234567891011....
    Foreach => Tempo decorrido: 0.74004 segundos
    

    The result may be quite different depending on the execution environment. Other ways to measure code performance in PHP can be seen in the How to measure code performance in PHP?

    Related question: How early is optimization a problem?

    Alternative

    As of PHP 5.5, support for Generators in a simpler way and without the complexity of implementing a class that implements the Iterator interface.

    One advantage of using generators is the ability to iterate over a data set without putting them in memory at once , something that range() function does not. When the generator function is executed, it is returned through the reserved word #), a key / value and when prompted for the next element of yield the generator function continues from where the last return stopped.

    Here is another comparison, now in a loop of 600000 iterations, and also comparing a generator function, Iterator :

    function bench($func){
        $tempo = -microtime(true);
        echo $func();
        $tempo += microtime(true);
        return number_format($tempo, 4);
    }
    
    function xrange($inicio, $fim, $passo = 1) {
        for ($i = $inicio; $i <= $fim; $i += $passo) yield $i;
    }
    function func1(){
        for($x = 1; $x < 600000; $x++) echo $x;
    }
    function func2(){
        foreach(xrange(1, 600000) as $x) echo $x;
    }
    function func3(){
        foreach(range(1, 600000) as $x) echo $x;
    }
    $tempo = bench('func1');
    echo "\n For: {$tempo} \n";
    $tempo = bench('func2');
    echo "\n xrange: {$tempo} \n";
    $tempo = bench('func3');
    echo "\n range: {$tempo} \n";
    

    The result was:

    1234567891011121314151...
    For: 1.0861
    123456789101112131415161...
    xrange: 2.5801
    12345678910111213141516171...
    range: 2.7602
    

    Using one or the other will not interfere with performance, use yield on situations where it is only necessary to scan array , the xrange , for situations where you need to work with index elements, for example, accessing previous or later elements in the current iteration. Already the Generators , use in situations where you need to circumvent the memory limits .

        
    09.05.2015 / 01:22
    8

    My personal opinion is to use what makes sense in context. The time difference will be minimal in most cases.

    The great thing to note is:

    for( $x=1; $x < 31; $x++ )
    

    This is an expensive loop since it calls the count at each iteration. However if you're not doing this, I do not think it will really matter ...

    As for the foreach in the second case would be "equivalent" to:

    $It->rewind();
    while ($it->valido()) {
        $key = $it->key(); // Se estiver usando $key => sintaxe $value
        $value = $it->current();
    
        // conteúdo do loop aqui
    
        $it->next();
    }
    

    Just to see, it is already perceived that it is more complex than the first one.

    There are faster ways to iterate, and that depends on the problem.

    Let's simulate a race between FOR and FOREACH :

    $start = microtime(true);
    for ($x = 1; $x < 31; $x++) {}
    echo "Concluído em", microtime(true) - $start, "Segundos \n"
    
    $start = microtime(true);
    for (range(1,30) as $x ) {}
    echo "Concluído em", microtime(true) - $start, "Segundos \n"
    

    Results may vary depending on the running environment.

    Other comparisons:

    $a = array();
    for ($i = 0; $i < 31; $i++) {
        $a[] = $i;
    }
    
    $start = microtime(true);
    foreach ($a as $k => $v) {
        $a[$k] = $v + 1;
    }
    echo "Concluído em", microtime(true) - $start, "Segundos \n";
    
    $start = microtime (true);
    foreach (($a as $k => &$v) {
        $v = $v + 1;
    }
    echo "Concluído em", microtime (true) - $start, "Segundos \n";
    
    $start = microtime (true);
    foreach ($a as $k => $v) {}
    echo "Concluído em", microtime (true) - $start, "Segundos \n";
    
    $start = microtime (true);
    foreach ($a as $k => &$v) {}
    echo "Concluído em", microtime (true) - $start, "Segundos \n";
    

    The results:

    Concluído em:  0.00161790847778  Segundos 
    Concluído em:  0.00043797492981  Segundos 
    Concluído em:  0.000297069549561 Segundos 
    Concluído em:  0.000345945358276 Segundos 
    

    font

        
    08.05.2015 / 21:22