One of the key questions we encountered in the last section was, why should we use SplFixedArray instead of PHP arrays? We are now ready to explore the answer. We came across the concept that PHP arrays are actually not arrays rather than hash maps. Let us run a small example code in PHP 5.x version to see the memory usage of a PHP array.
Let us create an array with 100,000 unique PHP integers. As I am running a 64 bit machine, I expect each integer to take 8 bytes each. So we will have around 800,000 bytes of memory consumed for the array. Here is the code:
$startMemory = memory_get_usage();
$array = range(1,100000);
$endMemory = memory_get_usage();
echo ($endMemory - $startMemory)." bytes";
If we run this code in our command prompt, we will see an output of 14,649,040 bytes. Yes, it is correct. The memory usage is almost 18.5 times more than what we have planned for. That means, for each element in the array an overhead of 144 bytes (18 * 8 bytes) for one PHP array. Now, where does this extra 144 bytes come from and why does PHP utilize so much extra memory for each array element? Here is an explanation of the extra bytes used by a PHP array:
This diagram shows how a PHP array works internally. It stores data in a bucket to avoid collision and to accommodate more data. To manage this dynamic nature, it implements both a doubly linked list and hash table internally for array. Eventually, it costs lots of extra memory space for each individual elements in the array. Here is the breakdown of the memory consumption of each element based on the PHP array implementation code (C code):
32 bit |
64 bit |
|
zval |
16 bytes |
24 bytes |
+ cyclic GC info |
4 bytes |
8 bytes |
+ allocation header |
8 bytes |
16 bytes |
zval (value) total |
28 bytes |
48 bytes |
bucket |
36 bytes |
72 bytes |
+ allocation header |
8 bytes |
16 bytes |
+ pointer |
4 bytes |
8 bytes |
bucket (array element) total |
48 bytes |
96 bytes |
Grand total (bucket+zval) |
76 bytes |
144 bytes |
With the new PHP 7 version, there is a very big improvement in the PHP array and how it is constructed internally. As a result, the 144 bytes overhead on each element has come down to 36 bytes only. That is a big improvement and it is applicable for both 32 bit and 64 bit OS. A comparison chart, having a range of 100,000 items in an array, is shown as follows:
$array = Range(1,100000) |
32 bit |
64 bit |
PHP 5.6 or below |
7.4 MB |
14 MB |
PHP 7 |
3 MB |
4 MB |
So, in other words, PHP 7 has an improvement factor of 2.5 times for 32 bit and 3.5 times for 64 bit system for array storage. That is a really good improvement. But this was all about a PHP array, what about SplFixedArray? Let us run the same example using SplFixArray in both PHP 7 and PHP 5.x:
$items = 100000;
$startMemory = memory_get_usage();
$array = new SplFixedArray($items);
for ($i = 0; $i < $items; $i++) {
$array[$i] = $i;
}
$endMemory = memory_get_usage();
$memoryConsumed = ($endMemory - $startMemory) / (1024*1024);
$memoryConsumed = ceil($memoryConsumed);
echo "memory = {$memoryConsumed} MB ";
We have written the memory consumption functionality of a SplFixedArray here. If we just change the line $array = new SplFixedArray($items); to $array = [];, we will have the same code running as for a PHP array.
Here is a comparison of memory consumption of a PHP array and SplFixedArray for an array with 100,000 integers in a 64 bit system:
100,000 items |
Using PHP array |
SplFixedArray |
PHP 5.6 or below |
14 MB |
6 MB |
PHP 7 |
5 MB |
2 MB |
Not only in memory usage, SplFixedArray is also faster in execution compared to general PHP array operations such as accessing value, assigning value, and so on.