Which one performs better? For or Foreach+Range?

Asked

Viewed 1,839 times

9

Of the two forms below, which has a better performance?

  1. For:

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

    foreach( range(1,30) as $x )
       echo $x . PHP_EOL;
    

I know the difference will probably be in milliseconds, but it is worth the study since in an application with great traffic these milliseconds can make a difference.

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

  • 5

    Without even executing the code, I would say that the first fragment is faster, since you don’t need to create an array, or access its values.

2 answers

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 observe is:

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

This is an expensive loop, since it calls the count on every iteration. However if you’re not doing this, I don’t think it will really matter ...

As to 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, you can see that it’s more complex than the first.

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

Let’s simulate a race between the 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 execution 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 

source

6


The for performed mildly better about the foreach, in this case because the function range returns a array of elements resulting from iteration and accesses each item of this.

See a comparison between the two forms in a 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";

Upshot:

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

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

Related question: To what extent premature optimization is a problem?

Alternative

Starting with PHP 5.5, support for Generators, the idea behind the generators is that a function does not return a unique value, but rather a sequence of values instead, where each value is issued one by one. In other words, generators allow to implement Iterators in a simpler way and without the complexity of implementing a class that implements the interface Iterator.

An advantage to use generators is the possibility to iterate over a data set without putting them in memory at once, something that the function range() does not. When the function generator is executed, is returned through the reserved word yield (a kind of return special), one key/value and, when requested the next element of the Iterator, the function generator continues where the last one left off yield.

Follow another comparative, now in a loop of 600000 iterations, and also comparing a function generator, the xrange:

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 interfere little in performance, use the foreach in situations where it is only necessary sweep to array, the for, for situations where it is necessary to work with the index elements, for example, access previous or later elements in the current iteration. Already the Generators, use in situations where it is necessary to bypass memory limits.

Browser other questions tagged

You are not signed in. Login or sign up in order to post.