Press "Enter" to skip to content

How to Write the Fastest Loop

Do you know how to write the fastest loop?
I just came across an interesting question on Xiaodong Guo's blog: " The difference between $i++ and ++$i in PHP ":

Approach 1:
$begin = time();
$i = 0;
while(++$i < 10000)
{
  $j = 0;
  while(++$j < 10000)
    ;
  ;
}
$end = time();
Time : 16s
Approach 2:
$begin = time();
$i = 0;
while($i < 10000)
{
  $j = 0;
  while($j < 10000)
    ++$j;
  ++$i;
}
$end = time();
Time:13s
Approach 3:
$begin = time();
$i = 0;
while($i < 10000)
{
  $j = 0;
  while($j < 10000)
    $j++;
  $i++;
}
$end = time();
Time:15s
Approach 4:
$begin = time();
$i = 0;
while($i++ < 10000)
{
  $j = 0;
  while($j++ < 10000)
    ;
  ;
}
$end = time();
Time:13s

Heh, so why does it work out that way?
Compare approach 1 and approach 2. In PHP, what ultimately runs is OPCODE, and every opline has two operands. For operands there are generally 3 kinds of access: temporary variable, variable, and compile-time variable. Of the three, the compile-time variable is the fastest to access. During opcode execution, an extra level of reference to a variable is stored in a hash structure to speed up access.
In approach 1:

$i = 0;
while(++$i < 10000)
{
  $j = 0;
  while(++$j < 10000)
    ;
  ;
}

For ++$i we need to get its return value to compare against 10000, and that makes PHP generate a variable (IS_VAR) at compile time to hold the result of the increment. In other words, the opline's return operand is used here.
Then PHP takes this variable (IS_VAR) and compares it against 10000.
And for approach 2:

$i = 0;
while($i < 10000)
{
  $j = 0;
  while($j < 10000)
    ++$j;
  ++$i;
}

In this process, $i has already been optimized into a compile-time variable (IS_CV), and for ++$i, since we do not need to keep its return value, we just increment the compile-time variable directly..
So the speed difference between approach 1 and approach 2 comes down to this: in approach 2 we are using the compile-time variable the whole time.. and compile-time variable access is far faster than variable (IS_VAR) access
Now look at approach 3 and approach 4:

//3:
$i = 0;
while($i < 10000)
{
  $j = 0;
  while($j < 10000)
    $j++;
  $i++;
}
//4:
$i = 0;
while($i++ < 10000)
{
  $j = 0;
  while($j++ < 10000)
    ;
  ;
}

As we know, postfix increment (POST_INC) returns a copy of the original value, then increments.
In approach 4, after $i++, ZE stores the original value of $i in a temporary variable (IS_TMP_VAR). And it compares that temporary variable against 10000.
So strictly speaking, this part is a bit slower than approach 1.
Approach 3 is slow because the return value of $i++ is not used. During parsing, $j++ reduces to (rw_variable T_INC->expr_without_variable->expr, expr+';' => zend_do_free). So ZE arranges an opline to free that temporary variable....

Be First to Comment

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.