PHP 7 performance improvements (5/5): Immutable arrays

Julien Pauli, PHP contributor and release manager, details what changed between PHP 5 and PHP 7, and how to migrate and make effective use of the language optimizations. All statements are documented with specific examples and Blackfire profiles. Fifth and last episode: Immutable arrays using 1,000 times less memory.

By Julien Pauli, on Dec 12, 2016

 The following is the last episode of our blog series on PHP 7 performance improvements, detailed by Julien Pauli, PHP Contributor and Release Manager.

Read episode 1: Packed arrays

Read episode 2: ints/floats are free in PHP 7

Read episode 3: Encapsed strings optimization

Read episode 4: References mismatch

Immutable arrays are a concept added to PHP 7, and part of the OPCache extension, which I assume you are using. An immutable array is an array full of immutable elements, which are compile-time known and fully resolved values: strings, integers, floats, or arrays containing those. In a word: no dynamic elements, no variables, like in this example:

$ar = [ 'foo', 42, 'bar', [1, 2, 3], 9.87 ];

Such arrays have been optimized in PHP 7. Under PHP 5, those are just plain arrays, there is no difference between an array containing dynamic members (such as $vars), and a static compile-time resolved array. In PHP 7, immutable arrays are not copied nor duplicated as soon as they stay read only.

Here is such an example:

for ($i = 0; $i < 1000; $i++) {
$var[] = [
  0 => 'Symfony\\Bundle\\FrameworkBundle\\EventListener\\SessionListener',
  1 => 'Symfony\\Component\\HttpFoundation\\Session\\Storage\\NativeSessionStorage',
  /* ... go to many immutable items here */
  ];
}

The above code creates the same big array 1,000 times (truncated, think of it as being a hundred or thousand slots array).

In PHP 5, this array is duplicated in memory 1,000 times. A big array could weight at several hundreds of Kilobytes or even Megabytes, which in this code will be duplicated 1,000 times. Such a waste of memory.

In PHP 7, OPCache flags those arrays as being immutable: the array is created only once, and shares its memory pointer everywhere it is supposed to be used, ending in huge savings of overall memory, especially when dealing with big configuration arrays, like the one shown in the example (which is taken from the Symfony 3 framework).

Let’s see the performance impact:

immutable-arrays

Here again, the gain between PHP 5 and PHP 7 is huge. PHP 5 needs to create 1,000 times the same array, resulting in 27 Mb of memory. In PHP 7 with OPCache, we are using 36 Kb of memory, something like 1,000 times less!

If you can make use of an immutable array, use it, it would be a shame to break immutability by writing code like this:

$a = 'value';

/* Don't do that */
$ar = ['foo', 'bar', 42, $a];

/* But prefer that : */
$ar = ['foo', 'bar', 42, 'value'];

Other considerations

So far, we’ve described some PHP internal tricks that explain why PHP 7 performs better than PHP 5. However, those are micro-optimizations for most workloads. You need huge amount of data or tight loops in your code to really benefit from those optimizations. These situations mostly occur when running PHP workers in the background instead of handling HTTP requests.

We’ve shown that in these cases, you can greatly improve resource consumption (CPU and memory) just by writing your code differently.

But as always, don’t trust your gut feelings about possible performance optimizations. Don’t blindly patch your code, use a profiler like Blackfire to confirm your hypothesis and check that your changes actually improve code performance.

Happy PHP 7’ing,

Julien Pauli

Julien is a web&system architect. He's been using PHP for more than a decade together with frameworks such as Symfony. He is now working at SensioLabs and is a PHP contributor and PHP 5.5/5.6 release manager. He tries to make PHP and its ecosystem better and more efficient.