Performance implications of Closures in PHP

A new feature for an elegant syntax. But performance...

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.