While optimizing a function in PHP, I changed
if(is_array($obj)) foreach($obj as $key=>$value { [snip] }
else if(is_object($obj)) foreach($obj as $key=>$value { [snip] }
to
if($obj == (array) $obj) foreach($obj as $key=>$value { [snip] }
else if($obj == (obj) $obj) foreach($obj as $key=>$value { [snip] }
After learning about ===
, I changed that to
if($obj === (array) $obj) foreach($obj as $key=>$value { [snip] }
else if($obj === (obj) $obj) foreach($obj as $key=>$value { [snip] }
Changing each test from is_* to casting resulted in a major speedup (>30%).
I understand that ===
is faster than ==
as no coercion has to be done, but why is casting the variable so much faster than calling any of the is_*-functions?
Edit: Since everyone asked about correctness, I wrote this little test:
$foo=(object) array('bar'=>'foo');
$bar=array('bar'=>'foo');
if($foo===(array) $foo) echo '$foo is an array?';
if($bar===(object) $bar) echo '$bar is an object?';
It doesn't print any error and both variables don't get changed, so I think it's working, but I'm ready to be convinced otherwise.
Another Edit: Artefacto's program gives me the following numbers:
PHP 5.3.2-1ubuntu4.2 (64bit) on a Core i5-750 with Xdebug Elapsed (1): 0.46174287796021 / 0.28902506828308 Elapsed (2): 0.52625703811646 / 0.3072669506073 Elapsed (3): 0.57169318199158 / 0.12708187103271 Elapsed (4): 0.51496887207031 / 0.30524897575378 Speculation: Casting and comparing can be about 1.7-4 times faster.
PHP 5.3.2-1ubuntu4.2 (64bit) on a Core i5-750 without Xdebug Elapsed (1): 0.15818405151367 / 0.214271068573 Elapsed (2): 0.1531388759613 / 0.25853085517883 Elapsed (3): 0.16164898872375 / 0.074632883071899 Elapsed (4): 0.14408397674561 / 0.25812387466431 Without Xdebug, the extra function call didn't matter anymore, so every test (except 3) ran faster.
PHP 5.3.2-1ubuntu4.2 on a Pentium M 1.6GHz Elapsed (1): 0.97393798828125 / 0.9062979221344 Elapsed (2): 0.39448714256287 / 0.86932587623596 Elapsed (3): 0.44513893127441 / 0.23662400245667 Elapsed (4): 0.38685202598572 / 0.82854390144348 Speculation: Casting an array is slower, casting an object can be faster, but might not be slower.
PHP 5.2.6-1+lenny8 on a Xeon 5110 Elapsed (1): 0.273758888245 / 0.530702114105 Elapsed (2): 0.276469945908 / 0.605964899063 Elapsed (3): 0.332523107529 / 0.137730836868 Elapsed (4): 0.267735004425 / 0.556323766708 Speculation: These results are similar to Artefacto's results, I think it's PHP 5.2.
The solution: The profiler I used (Xdebug) made function calls about 3 times slower (even when not profiling), but didn't affect casting and comparing noticeably, so casting and comparing appeared to be faster, even though it just wasn't affected by the debugger/profiler.
I can't really reproduce. In fact, your strategy gives me longer times in all but one case:
<?php
class A {
private $a = 4;
private $b = 4;
private $f = 7;
}
$arr = array("a" => 4, "b" => 4, "f" => 7);
$obj = new A();
$t = microtime(true);
for ($i = 0; $i < 500000; $i++) {
is_array($obj) and die("err");
}
echo "Elapsed (1.1): " . (microtime(true) - $t);
echo "\n";
$t = microtime(true);
for ($i = 0; $i < 500000; $i++) {
($obj === (array) $obj) and die("err");
}
echo "Elapsed (1.2): " . (microtime(true) - $t);
echo "\n";
$t = microtime(true);
for ($i = 0; $i < 500000; $i++) {
is_object($arr) and die("err");
}
echo "Elapsed (2.1): " . (microtime(true) - $t);
echo "\n";
$t = microtime(true);
for ($i = 0; $i < 500000; $i++) {
($arr === (object) $arr) and die("err");
}
echo "Elapsed (2.2): " . (microtime(true) - $t);
echo "\n";
$t = microtime(true);
for ($i = 0; $i < 500000; $i++) {
is_object($obj) or die("err");
}
echo "Elapsed (3.1): " . (microtime(true) - $t);
echo "\n";
$t = microtime(true);
for ($i = 0; $i < 500000; $i++) {
($obj === (object) $obj) or die("err");
}
echo "Elapsed (3.2): " . (microtime(true) - $t);
echo "\n";
$t = microtime(true);
for ($i = 0; $i < 500000; $i++) {
is_array($arr) or die("err");
}
echo "Elapsed (4.1): " . (microtime(true) - $t);
echo "\n";
$t = microtime(true);
for ($i = 0; $i < 500000; $i++) {
($arr === (array) $arr) or die("err");
}
echo "Elapsed (4.2): " . (microtime(true) - $t);
Output:
Elapsed (1.1): 0.366055965424 Elapsed (1.2): 0.550662994385 Elapsed (2.1): 0.337422132492 Elapsed (2.2): 0.579686880112 Elapsed (3.1): 0.402997970581 Elapsed (3.2): 0.190818071365 Elapsed (4.1): 0.332742214203 Elapsed (4.2): 0.549873113632
The cast and compare was only faster for checking if something is an object. Speculation follows: perhaps because object identity checking requires only determining whether the handler tables and the object handles are the same, while checking for array identity requires, in the worst case, comparing all the values.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With