- URL: https://www.laruence.com/en/2009/04/07/670.html
- Please include attribution when republishing.
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