Printing press characters in their boxes

I have been working with a code base that uses a lot of arrays as hashes recently. This is a style that I am confortable with, as I do enjoy PHP arrays : flexible, powerful, versatile. Who don’t like them? Yet, I keep hearing that classes are more memory efficient than arrays.

Yet, I still have in mind an article from Larry Garfield, which recommended to avoid associative arrays at all cost. In PHP 7.2, he tested various methods to compare associative arrays, stdClass, anonymous classes and named classes.

I thought it would be time to make an update of these finding. And it was very interesting indeed.

Classes are more memory efficient than arrays

First test, I created as many arrays as possible in a 2Mb PHP CLI script. This is done with the following script :

<?php

$b = [];
for($i = 0; $i < 100000; ++$i) {
    $b[] = ['a' => rand(0, 1),
            'b' => rand(0, 1),
            ];
    print "$i)\n";
}

?>

This is run with php -d memory_limit=2097152 arrays.php, and it will run until exhaustion. The print line will display the number of times where the structure could be created, until the memory is exhausted.

Here are the results

Number of elements in 2Mb memory: classes are more memory efficient than arrays

  • stdclass : 3516
  • assoc : 3931
  • anonymous class : 14427
  • stdclass extended : 14427
  • named class : 14428

First, memory consumption drops significantly from an array or a sdtclass to an actual class : there is a ratio of 4.10 between the two structures. This is quite a significant gain of memory!

Then, there are two groups : the associative arrays, and stdclass, which are even worse. The rest is made of classes, anonymous or not, and extended stdclass. Interestingly, anonymous classes are just as efficient as a named class. It seems that the name of the class doesn’t weight a lot.

Across PHP versions

For reference, here are the results of the test above, check across several PHP version :

Number of associative array in 2Mb memory per PHP version since PHP 5.6

Associative array :

  • 8.3 : 3931
  • 8.2 : 3931
  • 8.1 : 3770
  • 8.0 : 3770
  • 7.4 : 3769
  • 7.3 : 3769
  • 7.2 : 3769
  • 7.1 : 3856
  • 7.0 : 3857
  • 5.6 : 3185

As you can see, arrays are more and more efficient with PHP versions.

Number of objects of a named class in 2Mb memory, per PHP versions since PHP 5.6

Named class :

  • 8.3 : 14428
  • 8.2 : 14428
  • 8.1 : 8191
  • 8.0 : 8191
  • 7.4 : 8191
  • 7.3 : 8191
  • 7.2 : 8191
  • 7.1 : 8191
  • 7.0 : 8191
  • 5.6 : 4094

Classes are quite stable in terms of ressources. The uptick with PHP 8.2 is due to the drop of dynamic properties in 8.2. That has quite a dramatic effect.

Processing

Processing those structures may actually be quite different than simple storage. So, I ran another test. To compare array to objects, one actually need to access the values. So, I created this simple script :

<?php

const S  = 10000;
const T  = 10000;

$a = [
'a' =>  rand(0, 10),
'b' =>  rand(0, 10),
'c' =>  rand(0, 10),
];

$start = microtime(true);
$c = 0;
for ($i = 0; $i < S; $i++)
{
    $c = 0;
    for($j = 0; $j < T; ++$j) {
        $c += $a['a'] + $a['b'] + $a['c'];
    }
}
$duration = microtime(true) - $start;
echo number_format($duration * 1000, 2)." ms".PHP_EOL;

?>

The interesting part is the addition of all the elements of the array : it is using the object syntax for the other cases. microtime() is used instead of hrtime() for compatibility reasons.

The three extractions are summed a million times. That should show be sufficient to spot a difference. Here, the smaller number is the better.

Processing time of extended stdclass, anonymous classes, named classes, stdclass and associative arrays, in ms.

  • stdclass extended : 2494 ms
  • anonymous class : 2507 ms
  • named class : 2524 ms
  • stdclass : 2682 ms
  • assoc : 2904 ms

In the end, those differences are not significant. While associative arrays are a bit slower, this is over a million extractions.

Conclusion

Classes are more memory efficient than arrays. Memory usage is a lot improved when using classes, anonymous or not. That means, less resources to run PHP, and overall, better performances, as less allocation/deallocation happen.

Performances are very similar, and not significantly different one from another. It is possible to note a improvment for classes, but over a million times, it is a quite a micro optimisation.

Given the possibilities of organisation that classes offer, or the local usage of anonymous classes, there are less and less reasons to use arrays as associative structures.

The main reason would be the conversion cost : databases, JSON, etc. all provide associative arrays as result structures. Sqlite3 support arrays and associative arrays, but not object results. On the other hand, PDO, postgresql, MySQL offer resp. fetchObject(), pgfetchobjet(), mysqlifetchobject () where a class may be configured. This speeds up the conversion, and delivers immediately an object.

May be it is time to consider that classes are providing a good memory usage.