PHP 7 performance improvements (1/5): Packed 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. First episode: Packed arrays saving up to 72% in compile and run time.

By Julien Pauli, on Nov 14, 2016

The following starts our blog series on PHP 7 performance improvements, detailed by Julien Pauli, PHP Contributor and Release Manager.

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

Read episode 3: Encapsed strings optimization

Read episode 4: References mismatch

Read episode 5: Immutable arrays

Introduction to PHP7

PHP is a software written in the C language. The PHP codebase, about 800K lines of code, has been massively rewritten for PHP 7.

This blog series will show you what changed inside the Zend engine between PHP 5 and PHP 7 and will detail how you, as a developer, may effectively use the new internal optimizations. We are taking PHP 5.6 as a comparison basis. Often, it is just a matter of how things are written and presented to the engine. Performance must be taken care of when critical code is written. By changing some little things, you can make the engine perform much faster, often without losing other aspects such as readability or debugging control. We’ll prove our statements using Blackfire.

Migrating to PHP 7 to benefit from performance improvements must be done in two steps:

  1. migrate the code base without changing it (or just turn it into PHP 7 compatible code) and enjoy the free performance boost;
  2. use the tips from this post to understand which part of the PHP virtual machine code changed since version 5, and how you can use with them to boost your code performance even more.

Packed arrays

Packed arrays is the first great PHP 7 optimization. Packed arrays consume less memory and are a bit faster in many operations than traditional arrays. A packed array must have the following characteristics:

  • The keys are only integers;
  • The keys are inserted into the array in growing only mode.

Example 1 :

$a = ['foo', 'bar', 'baz'];

Example 2 :

$a = [12 => 'baz', 42 => 'bar', 67 => [] ];

Such arrays are internally very optimized in PHP 7. Obviously, you won’t feel any difference for a 3-slot- array.

For example, in the Symfony framework, such arrays exist like in the class map generator, which generates code like this:

array (
  0 => 'Symfony\\Bundle\\FrameworkBundle\\EventListener\\SessionListener',
  1 => 'Symfony\\Component\\HttpFoundation\\Session\\Storage\\NativeSessionStorage',
  2 => 'Symfony\\Component\\HttpFoundation\\Session\\Storage\\PhpBridgeSessionStorage',
  3 => 'Symfony\\Component\\HttpFoundation\\Session\\Storage\\Handler\\NativeFileSessionHandler',
  4 => 'Symfony\\Component\\HttpFoundation\\Session\\Storage\\Proxy\\AbstractProxy',
  5 => 'Symfony\\Component\\HttpFoundation\\Session\\Storage\\Proxy\\SessionHandlerProxy',
  6 => 'Symfony\\Component\\HttpFoundation\\Session\\Session',
  /* ... */

If we analyze this code using Blackfire, we get this result when comparing the same code running on PHP 5 and PHP 7:

packed-arrays

As we can see on this comparison view, the total time to compile and run this array declaration has decreased by about 72%. Such an array becomes like a noop on PHP 7, whereas in PHP 5 it both took some time to compile, and then some time to load at runtime.

Pushing the concept further, let’s now use a bigger array with, say, 10.000 slots :

for ($i=0; $i<10000; $i++) {
	$a[] = $i;
}

Here is the same comparison:

packed-arrays-loop

CPU time has been divided by about 10, and memory has decreased from about 3 Mb to half a megabyte.
The PHP development team has worked a lot on optimizing arrays, as they are the main compound structure PHP supports (the second one being the object, which is implemented internally on the same model).

The memory footprint of PHP 7 arrays is really much lower than in PHP 5. And if you use packed arrays; then you’ll save even more memory.

So don’t forget:

  • if you need a list : don’t use any string key (as it would prevent the packed array optimization from firing in);
  • if you use lists with only integer based keys, try to arrange them in the natural ascending order (if not, you’ll once again prevent packed array optimization).

Such lists may be used in several parts of your application, like for just… listing some things (like the class map in Symfony) or when fetching results from a database , in ordered fashion and with an integer as column. This is frequent in web applications (like a $pdo->query("SELECT * FROM table LIMIT 10000")->fetchAll(PDO::FETCH_NUM)).

Next week: ints/floats are free in PHP 7

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.