READ LOOP: foreach() vs. while(list()=each()) | ||
---|---|---|
What is the best way to loop a hash array? Given is a Hash array with 100 elements, 24byte key and 10k data per entry I've chosen the large data amount to try out what happens if I reference the data with the &-ref-operator (to avoid copying). But to my surprise the loops are never faster! In tests 5 and 6 are even 10x - 30x slower !! The larger the data entrys are the slower the tests 5 and 6 get! Copying seams always faster then using the &-ref-operator. Way ??? Let me know at bs_php@users.sourceforge.net | ||
+ 100 % | 1: foreach($aHash as $val); | Total time: 0[ms] |
+ 1241 % | 2: while(list(,$val) = each($aHash)); | Total time: 1[ms] |
+ 4798 % | 3: foreach($aHash as $key=>$val); | Total time: 5[ms] |
+ 2563 % | 4: while(list($key,$val)= each($aHash)); | Total time: 3[ms] |
+ 2066 % | 5: foreach($aHash as $key=>$val) $tmp[] = &$aHash[$key]; | Total time: 2[ms] |
+ 2099 % | 6: while(list($key) = each($aHash)) $tmp[]=&$aHash[$key]; | Total time: 2[ms] |
+ 612 % | 7: Get key-/ value-array: foreach($aHash as $key[]=>$val[]); | Total time: 1[ms] |
+ 381 % | 8: Get key-/ value-array: array_keys() / array_values() | Total time: 0[ms] |
+ 554 % | 9: STRANGE: This is the fasetest code when using the the &-ref-operator (to avoid copying) $key = array_keys($aHash); $size = sizeOf($key); for ($i=0; $i<$size; $i++) $tmp[] = &$aHash[$key[$i]]; | Total time: 1[ms] |
Conclusion: It must have something to do with PHP4 variable ref-count So you can safely use foreach and only use the &-ref-operator when realy needed OR (according to the link above) when passing objects to functions. (Thanx to Wayne for his help) |
Test: MODIFY LOOP: foreach() vs. while(list()=each()) | ||
---|---|---|
While the above test only reads and copies the data the question arised what would happen if I modify each value of the hash above. Again I an unexpected result. Even if I reduce the data size to 100 byte p. e. it ends up that Nr.3 is 1.5 - 2x faster. | ||
+ 372 % | 1: foreach($aHash as $key=>$val) $aHash[$key] .= "a"; | Total time: 2[ms] |
+ 174 % | 2: while(list($key) = each($aHash)) $aHash[$key] .= "a"; | Total time: 1[ms] |
+ 100 % | 3: STRANGE: This is the fasetest code : $key = array_keys($aHash); $size = sizeOf($key); for ($i=0; $i<$size; $i++) $aHash[$key[$i]] .= "a"; | Total time: 1[ms] |
Conclusion: Use foreach unless the hash is lage AND has lage data elements. In that case use variation Nr.3 . |
Test: For-loop test | ||
---|---|---|
Is it worth the effort to calculate the length of the loop in advance? E.g. "for ($i=0; $i<$size; $i++)" instead of "for ($i=0; $i<sizeOf($x); $i++)" | ||
+ 100 % | 1: With pre calc | Total time: 1[ms] |
+ 1537 % | 2: Without pre calc | Total time: 11[ms] |
Conclusion: The test above speeks for it self. Always calculate the length of the loop in advance! |
Test: Using the &-ref-operator as so called "alias" | ||
---|---|---|
Is a good idea to use the &-ref-operator to substitute (or alias) a complex mutidim-array? . Call 1'000x E.g. $person = &$aHach["country"]["zip"]["streat"]["number"]["name"] | ||
+ 107 % | 1: NO Aliasing. Using: $aSingleDimArray[$i] | Total time: 1[ms] |
+ 100 % | 2: Aliasing. Using: $alias = &$aSingleDimArray[$i] | Total time: 1[ms] |
+ 178 % | 3: NO Aliasing. Using: $aMultiDimArray[$i]["aaaaa"]["aaaaaaaaaa"] | Total time: 1[ms] |
+ 122 % | 4: Aliasing. Using: $alias = &$aMultiDimArray[$i]["aaaaa"]["aaaaaaaaaa"] | Total time: 1[ms] |
+ 268 % | 5: NO Aliasing. Using: veryMultiDimArray[$i]["a"]["aa"]["aaa"]["aaaa"]["aaaaa"] | Total time: 2[ms] |
+ 152 % | 6: Aliasing. Using: $alias = &$veryMultiDimArray[$i]["a"]["aa"]["aaa"]["aaaa"]["aaaaa"] | Total time: 1[ms] |
Conclusion: It seams to be ok to use aliases. It also makes the code more readabel. But I was expecting to get a lager performance gain; especially with very multdimetional arrays. |
Test: $obj = new SomeClass() vs. $obj =& new SomeClass() using the =&-ref-operator | ||
---|---|---|
Is a good idea to use the =&-ref-operator when creating a new object? Call 1'000x | ||
+ 100 % | 1: $obj = new SomeClass() | Total time: 1[ms] |
+ 114 % | 2: $obj =& new SomeClass() | Total time: 1[ms] |
+ 249 % | 3: $obj =& $someClass->f(); | Total time: 2[ms] |
+ 137 % | 4: $obj = $someClass->f(); | Total time: 1[ms] |
Conclusion: There seams to be no difference in performance. |
Test: double (") vs. single (') quotes | ||
---|---|---|
Is a there a difference in using double (") and single (') quotes for strings. Call 1'000x | ||
+ 113 % | 1: single (') quotes. Just an empty string: $tmp[] = ''; | Total time: 0[ms] |
+ 103 % | 2: double (") quotes. Just an empty string: $tmp[] = ""; | Total time: 0[ms] |
+ 100 % | 3: single (') quotes. 20 bytes Text : $tmp[] = 'aaaaaaaaaaaaaaaaaaaa'; | Total time: 0[ms] |
+ 102 % | 4: double (") quotes. 20 bytes Text : $tmp[] = "aaaaaaaaaaaaaaaaaaaa"; | Total time: 0[ms] |
+ 104 % | 5: single (') quotes. 20 bytes Text and 3x a $ : $tmp[] = 'aa $ aaaa $ aaaa $ a'; | Total time: 0[ms] |
+ 100 % | 6: double (") quotes. 20 bytes Text and 3x a $ : $tmp[] = "aa $ aaaa $ aaaa $ a"; | Total time: 0[ms] |
+ 104 % | 7: double (") quotes. 20 bytes Text and 3x a \$ : $tmp[] = "aa \$ aaaa \$ aaaa \$ a"; | Total time: 0[ms] |
Conclusion: Single and double quoted strings behave almost the same with one exception: Don't use the a lonely ($) in double quoted string unless you want to reference a PHP-var; or use (\$). |
Test: isSet() vs. empty() vs. is_array() | ||
---|---|---|
What is the performance of isSet() and empty(). Call 2'000x | ||
+ 127 % | 1: isSet() with var that was set | Total time: 0[ms] |
+ 119 % | 2: empty() with var that was set | Total time: 0[ms] |
+ 106 % | 3: isSet() with var that was *not* set | Total time: 0[ms] |
+ 102 % | 4: empty() with var that was *not* set | Total time: 0[ms] |
+ 119 % | 5: isSet() with array-var that was set | Total time: 0[ms] |
+ 104 % | 6: empty() with array-var that was set | Total time: 0[ms] |
+ 100 % | 7: isSet() with array-var that was *not* set | Total time: 0[ms] |
+ 104 % | 8: empty() with array-var that was *not* set | Total time: 0[ms] |
+ 177 % | 9: is_array() of an array | Total time: 1[ms] |
+ 176 % | 10: is_array() of a string | Total time: 1[ms] |
+ 446 % | 11: is_array() of a non set value | Total time: 2[ms] |
+ 124 % | 12: isSet() AND is_array() of a non set value | Total time: 0[ms] |
Conclusion: isSet() and empty() are identical. Interesting that a is_array() on a unset val is 3x slower. So alway check if val is set at all befor using type-checking. E.g. if (isSet($foo) AND is_array($foo)) |
Test: switch/case vs. if/elseif | ||
---|---|---|
Is a there a difference between switch and if elseif. Call 1'000x | ||
+ 129 % | 1: if and elseif (using ==) | Total time: 0[ms] |
+ 100 % | 2: if and elseif (using ===) | Total time: 0[ms] |
+ 128 % | 3: case | Total time: 0[ms] |
Conclusion: Using a switch/case or if/elseif is almost the same. Note that the test is unsing === and is slitly faster then using ==. |
http://www.php.lt/benchmark/phpbench.php
http://www.phpbench.com