Day: February 5, 2010

Development

Comparing PHP array_shift to array_pop

I noticed a note in the PHP documentation about speed differences between array_shift() (pulling the first element off the array) and array_reverse() followed by array_pop() (resulting in the same data, but got to by pulling the last element off the array).

Since I was working on some code to convert URL pieces to program arguments (like turning /admin/users/1/edit into section=admin, module=users, id=1, action=edit – stuff we tend to do every day) I thought I would take a look at the speed differences since I have always used array_shift() for this (after turning the string into an array via explode()).

My initial tests showed that array_shift was much faster than array_reverse followed by array_pop, and I wondered why someone would say that in the first place. But then I thought about it for a bit. When using array_shift the entire remaining array has to be re-indexed every call. For a very short array (like the one I was using) this is negligible. When you start looking at much larger arrays, however, this overhead adds up quickly.

To find out roughly where the break-even point on these two methods lie I whipped up a quick script to run with arrays sized from 10^1 values up to 10^5 values. What I found is that at less than 100 values you are not really gaining much (if anything) by using array_reverse and array_pop versus array_shift. Once you get to the 1000 value array size, however, the differences really add up (as you can see in the logarithmic scaling of the chart below).

shift_vs_pop.jpg

The code I used to generate the numbers (which are shown in the chart as averages over 3 runs, rounded to the nearest millionth of a second) is:

<?php
$counts = array(10,100,1000,10000,100000);
foreach ($counts as $len)
{
	$m2 = $m1 = array();
	$x = 1;
	while ($x <= $len)
	{
		$m2[] = $m1[] = $x;
		$x++;
	}
	echo "Timing with array_shift() for $len items\n";
	echo "000000";
	$s1 = microtime(true);
	while (!empty($m1))
	{
		$tmp = array_shift($m1);
		if ($tmp % 10 == 0)
		{
			echo chr(8),chr(8),chr(8),chr(8),chr(8),chr(8);
			echo str_pad(''.$tmp,6,'0',STR_PAD_LEFT);
		}
	}
	$s2 = microtime(true);
	echo "\nTook ",$s2 - $s1," seconds\n";
	
	echo "Timing with array_reverse and array_pop() for $len items\n";
	$s1 = microtime(true);
	$m2 = array_reverse($m2);
	while (!empty($m2))
	{
		$tmp = array_pop($m2);
		if ($tmp % 10 == 0)
		{
			echo chr(8),chr(8),chr(8),chr(8),chr(8),chr(8);
			echo str_pad(''.$tmp,6,'0',STR_PAD_LEFT);
		}
	}
	$s2 = microtime(true);
	echo "\nTook ",$s2 - $s1," seconds\n";
	echo "\n";
}
?>