Closures bring concise and elegant solutions to a wide array of coding problems. A common use is in array operations; array_map(), array_filter() and similar functions all can take advantage of Closure expressiveness and elegance.
Before PHP 5.3 the same functionality could be implemented with callback functions or “manually” iterating over the arrays. I’m currently developing an application that manipulates large arrays and soon started speculating about which is the fastest approach.
I built a large linear array composed of associative arrays:
$a = array();
for ($i = 0; $i < 20000; $i++) {
$a[] = array(
'name' => 'John',
'city' => 'New York',
'age' => $i % 200 == 0 ? 21 : 28
);
}
I then tried to extract all associative arrays where age = 21:
// Explicit iteration
$filtered = array();
foreach ($a as $item) {
if ($item['age'] == 21) $filtered[] = $item;
}
// Closure
$filtered = array_filter($a, function ($item) {
return ($item['age'] == 21);
});
// Callback
$filtered = array_filter($a, 'agefilter');
function agefilter($item)
{
return ($item['age'] == 21);
}
By far, the Closure approach is the most elegant and compact; it is also understandable, at least when you get used to Closures.
The explicit iteration is more easily understood, even by less experienced programmers. It lacks compactness and elegance.
The callback approach is definitely the worst in terms of elegance and readability. The callback function could be distant from its usage, or worse in a different file. Moreover, you need to pass a string representing the name of the function to array_filter() as there was no other method in old versions of PHP create a reference to a function.
The hard numbers
To test the speed of each solution, I performed 50 iterations of each and picked the average from all executions. Results taken on my dev machine are interesting (all times in seconds):
Method | time (sec) |
---|---|
iteration: | 0.037838 |
closure: | 0.053041 |
callback: | 0.056753 |
The PC is running Mandriva 2010.2/32bit with PHP 5.3.6. I repeated the same test on various systems and got consistent results.
System | PHP version | iteration | closure | callback |
---|---|---|---|---|
lamp | PHP 5.3.6 with Suhosin-Patch (cli) (built: Apr 8 2011 06:12:58) | 0.037838 | 0.053041 | 0.056753 |
affvm | PHP 5.3.6 (cli) (built: May 16 2011 19:18:00) | 0.019283 | 0.023893 | 0.026602 |
gf1 | PHP 5.3.3 (cli) (built: Jul 22 2010 16:41:20) | 0.036564 | 0.040266 | 0.045073 |
WeBIp | PHP 5.3.5 (cli) (built: Jan 22 2011 10:11:01) | 0.012046 | 0.017699 | 0.020483 |
apollo | PHP 5.3.2 (cli) (built: Jun 25 2011 08:12:19) | 0.018686 | 0.024761 | 0.026844 |
Luckily, the callback approach is the slowest one, so I’m not afraid of not using it. Speed penalties of Closure with respect to iteration range from 10% to roughly 50%. Certainly, 5-10msec penalty can be negligible for a single HTML page generation. Nevertheless, 10-50% is definitely a valuable time saving on large traffic sites or when processing large amounts of data.
My guess is the performance penalty for Closure and callback comes from function call. In both solutions a function is called 20000 times, whereas the iterative approach doesn’t use any function.
The harder numbers
As mentioned earlier, lamp is my dev box, running Mandriva 2010.2/32bit. It scored as the worst of all systems tested. The funny thing is that affvm is a virtual machine running on top of lamp and is almost twice as fast.
OS? CentOS 5.6/32bit with PHP from Remi Collet.