PHP: The cost of a million objects on function calls

Especially, if you are a start-up, all it takes is a fine combination of:

  • a bad coder (with an arrogant attitude of “code first + optimize later”)
  • and 1000 simultaneous requests

to bring each of your servers to its “knees”.

On part 1, I’ve mentioned the cost of a million items when we are using arrays in PHP.

In this part, I want to highlight related features when we are passing such arrays to functions.

  • version 1: array of arrays: passed by value
  • version 2: array of arrays: passed by reference
  • version 3a: array of objects: passed by value and reassigned objects in function
  • version 3b: array of objects: passed by value without reassigned objects
  • version 4: array of objects: passed by reference

Let’s see the first version of the code:


$row) {
$row['a'] = strrev($row['a']);
$array[$i] = $row;
}

return $array;
}

$arr = [];

$L = 1000000;
$S = 'abcdefghijklmnopqrstuvwxyz';
mt_srand();
for($i = 1; $i <= $L; $i++) { $row = [ 'a' => str_shuffle($S),//string
'b' => mt_rand(0, $L) / mt_rand(1, $L),//float
'c' => mt_rand(-1000, 1000),//int
];
$arr[] = $row;
}

echo '1 item 0 -> a:' . ($arr[0]['a']) . PHP_EOL;
$arr2 = myProcess($arr);
echo '1 item 0 -> a:' . ($arr[0]['a']) . PHP_EOL;
echo '2 item 0 -> a:' . ($arr2[0]['a']) . PHP_EOL;

$t1 = microtime(true);
$mem = memory_get_usage(true);

echo 'Array of ' . number_format($L) . ' arrays.' . PHP_EOL;
echo 'Time taken: ' . number_format($t1 - $t0, 3) . 'ms.' . PHP_EOL;
echo 'Memory usage: ' . number_format($mem / 1024, 1) . 'KB.' . PHP_EOL;
?>

I’ve got the best results when I used the version 4:


$row) {
$row->a = strrev($row->a);
//$array[$i] = $row; //no need because objects in arrays have references
}

//return $array; //no need to return, because we are modifying the array given by reference
}

class myClass
{
public $a;
public $b;
public $c;
function __construct($a, $b, $c)
{
$this->a = $a;
$this->b = $b;
$this->c = $c;
}
}

$arr = [];

$L = 1000000;
$S = 'abcdefghijklmnopqrstuvwxyz';
mt_srand();
for($i = 1; $i <= $L; $i++) { $row = new myClass( str_shuffle($S),//string mt_rand(0, $L) / mt_rand(1, $L),//float mt_rand(-1000, 1000) ); $arr[] = $row; } echo '1 object 0 -> a:' . ($arr[0]->a) . PHP_EOL;
myProcess($arr);
echo '1 object 0 -> a:' . ($arr[0]->a) . PHP_EOL;

$t1 = microtime(true);
$mem = memory_get_usage(true);

echo 'Array of ' . number_format($L) . ' custom objects.' . PHP_EOL;
echo 'Time taken: ' . number_format($t1 - $t0, 3) . 'ms.' . PHP_EOL;
echo 'Memory usage: ' . number_format($mem / 1024, 1) . 'KB.' . PHP_EOL;
?>

You can see all the files on my github project.

The surprising cases for me were version 3a and version 3b. Simply, I didn’t expect a difference of 50MB because I thought they are effectively doing the same job.

And here is the chart to compare them:

php-memory-usage-chart-on-function-calls

Note: This time, I didn’t use the logarithmic scale.

Author: murat

Leave a Reply