PHP - Sorting Objects - Performance Considerations

PHP has some great functions for sorting arrays, however, they should be used with care.

If you have an array of complex objects and plan to sort only on one value, you may see significant performance gains if you flatten the array into a single dimension and use the simpler library functions. The test run posted here shows a 10x improvement.

This is a very simple demonstration script that creates an array of 100 objects with a random value and timestamp. The array is sorted in ascending and descending order.

Sample output

Sorting an array of objects
------------------------------
uAsortBiDir ASC
Elapsed 0.001910924911499ms
uAsortUniDir ASC
Elapsed 0.0020918846130371ms
uAsortBiDir DESC
Elapsed 0.0018858909606934ms
uAsortUniDir DESC
Elapsed 0.0021059513092041ms

Sorting a flattened array
------------------------------
asort
Elapsed 0.00023484230041504ms
arsort (Reverse)
Elapsed 0.00020194053649902ms

<?php

// Arbitrary constants
define ('LIMIT',100);
define ('MIN', 0);
define ('MAX', 100);

// An array of objects 
$arr = array();
for ($i = 0; $i < LIMIT; $i++) {
	$arr[] = new Item(rand(MIN, MAX), microtime(true));
}

echo 'Sorting an array of objects'.PHP_EOL;
echo str_repeat('-',30).PHP_EOL;
$sorter = new Sorter;
echo 'uAsortBiDir ASC'.PHP_EOL;
$start = microtime(true);
uasort($arr, array($sorter, 'uAsortBiDir'));
$end= microtime(true);
echo 'Elapsed '.($end - $start).'ms'.PHP_EOL;

shuffle($arr);

echo 'uAsortUniDir ASC'.PHP_EOL;
$start = microtime(true);
uasort($arr, array($sorter, 'uAsortUniDir'));
$end= microtime(true);
echo 'Elapsed '.($end - $start).'ms'.PHP_EOL;

shuffle($arr);

$sorter = new Sorter(Sorter::DESC);
echo 'uAsortBiDir DESC'.PHP_EOL;
$start = microtime(true);
uasort($arr, array($sorter, 'uAsortBiDir'));
$end= microtime(true);
echo 'Elapsed '.($end - $start).'ms'.PHP_EOL;

shuffle($arr);

echo 'uAsortUniDir DESC'.PHP_EOL;
$start = microtime(true);
uasort($arr, array($sorter, 'uAsortUniDir'));
$arr = array_reverse($arr);
$end= microtime(true);
echo 'Elapsed '.($end - $start).'ms'.PHP_EOL;

shuffle($arr);

echo PHP_EOL;

echo 'Sorting a flattened array'.PHP_EOL;
echo str_repeat('-',30).PHP_EOL;
echo 'asort'.PHP_EOL;
$start = microtime(true);
$flat = array();
foreach ($arr as $index => $item) {
	$flat[$index] = $item->value;
}
asort($flat);
$new = array();
foreach ($flat as $index => $item) {
	$new[$index] = $arr[$index];
}
$end= microtime(true);
echo 'Elapsed '.($end - $start).'ms'.PHP_EOL;

shuffle($arr);

echo 'arsort (Reverse)'.PHP_EOL;
$start = microtime(true);
$flat = array();
foreach ($arr as $index => $item) {
	$flat[$index] = $item->value;
}
arsort($flat);
$new = array();
foreach ($flat as $index => $item) {
	$new[$index] = $arr[$index];
}
$end= microtime(true);
echo 'Elapsed '.($end - $start).'ms'.PHP_EOL;

Class Item {
	public $data = array();
	
	function __construct($value, $timestamp) {
		$this->data['value'] = $value;
		$this->data['timestamp'] = $timestamp;
	}

	function __get($property) {
		return $this->data[$property];
	}
}

Class Sorter {

	const ASC = 'asc';
	const DESC = 'desc';

	protected $dir;

	function __construct($dir = self::ASC) {
		$this->dir = $dir;
	}

	function uAsortBiDir($a, $b) {
		$cmp = ($a->value < $b->value);
		if ($this->dir === self::DESC) $cmp != $cmp;
		return $cmp;
	}

	function uAsortUniDir($a, $b) {
		$cmp = ($a->value < $b->value);
		return $cmp;
	}
}