Using Closures as callbacks within loops
Monday, December 20th 2010, 10:30pm
Topics: Other, PHP
Tags: Closure, Callback, Loop, Benchmark, Test, Case
Comments: 2
Permalink -
Tinylink
In jQuery (and other Javascript frameworks) it is quite common to use closures (I refer to them as callback functions) to loop over arrays or objects. Even though it's a slow process and is much more efficient to use the built-in for loop, it got me thinking. Why not try and use the new Closure class in PHP 5.3 and see how well it performs within a loop? Suffice to say, I got some really really interesting results. Before I get into the details, here is the test script I wrote (the Benchmark class is merely a class I have written in the past).
I didn't get too in depth with my test cases and simply used Firefox and page refresh to get my results. I am running PHP 5.3.1 on a Windows 7 XAMPP installation with Apache and no caching. For benchmarking I was using microtime(true) and memory_get_usage().
I began testing with 4 different cases, each of which that changed the size of the $data array. I started with 1000 iterations, then 5000, then 10000 and lastly 100000. I would comment out the foreach/loop sections and run them one at a time (of course), and ran each test about 5 times to gather an average. Here are the results.
The first thing you will notice is the time it took to interpret the page. On average using a closure as a callback within a loop will take 2-3x longer to process. However, the interesting thing is that the memory usage is around 40% smaller (using more allocated memory) while using a closure than doing a foreach, yet the max allocated is nearly identical. I knew what the outcome would be before I even started it -- Javascript closures are the same way. Regardless it was a fun experiment and if anyone knows more about this, please shed some light on this topic for the rest of us!
But in closing I can sadly say, that no, you should not be using a closure for looping, just stick to the old fashion tried and true foreach or for loop.
<?php $data = range(0, 1000); $clean = array(); function loop($array, Closure $closure) { if (!empty($array)) { foreach ($array as $key => $value) { $closure($key, $value); } } } Benchmark::start('loop'); foreach ($data as $key => $value) { $clean[$key] = $value; } loop($data, function($key, $value) { $clean[$key] = $value; }); Benchmark::stop('loop'); echo Benchmark::display('loop'); ?>
I didn't get too in depth with my test cases and simply used Firefox and page refresh to get my results. I am running PHP 5.3.1 on a Windows 7 XAMPP installation with Apache and no caching. For benchmarking I was using microtime(true) and memory_get_usage().
I began testing with 4 different cases, each of which that changed the size of the $data array. I started with 1000 iterations, then 5000, then 10000 and lastly 100000. I would comment out the foreach/loop sections and run them one at a time (of course), and ran each test about 5 times to gather an average. Here are the results.
foreach: 1000 Time: 0.0010 / Memory: 137128 (Max: 689160) 5000 Time: 0.0052 / Memory: 706488 (Max: 1258528) 10000 Time: 0.0097 / Memory: 1412048 (Max: 1964120) 100000 Time: 0.0545 / Memory: 13849568 (Max: 14401656) closure: 1000 Time: 0.0027 / Memory: 84984 (Max: 688832) 5000 Time: 0.0144 / Memory: 433672 (Max: 1258192) 10000 Time: 0.0267 / Memory: 866448 (Max: 1963744) 100000 Time: 0.1223 / Memory: 8525216 (Max: 14401256)
The first thing you will notice is the time it took to interpret the page. On average using a closure as a callback within a loop will take 2-3x longer to process. However, the interesting thing is that the memory usage is around 40% smaller (using more allocated memory) while using a closure than doing a foreach, yet the max allocated is nearly identical. I knew what the outcome would be before I even started it -- Javascript closures are the same way. Regardless it was a fun experiment and if anyone knows more about this, please shed some light on this topic for the rest of us!
But in closing I can sadly say, that no, you should not be using a closure for looping, just stick to the old fashion tried and true foreach or for loop.
2 Comments
hermanradtke.com
Dec 20th 2010, 23:20
$sum = array_reduce($set, function ($a, $b) { return $a + $b; });The above is more meaningful than a foreach loop of:
$sum = 0; foreach ($set as $v) { $sum += $v; }Obviously the above example is trivial and we see clearly understand both code samples. However, the use of the array_reduce function implies that I am reducing the array into a single value. Even if I have a 6 dimensional array with a callback function 100 lines long, I know exactly what to expect as a result.
As for performance trade-offs: I worry very little about writing the most optimized PHP code. If the database isn't the bottleneck it is usually some O(2^n) loop someone wrote that is going to be slow in any language.