Essential Macro Optimizations to Improve PHP Performance

This blog post describes four macro-optimizations for PHP applications that are essential to consider before investigating other possible optimizations.

I often find myself discussing small micro-optimizations for 1-2% of speed gain with other developers, even though these macro optimizations haven’t been dealt with first. While it’s fun chasing after small micro optimizations, often it is debatable if the developer time is well spent. After all, I/O is the more important bottleneck in almost every application.

Micro-optimizations are not useless, as SQLite recently showed – investing months of work and applying hundreds of different changes can improve the performance significantly.

But it’s much more efficient to fix the big issues first. The changes I present in this post can be done quickly and their gains can be massive.

1. Upgrade PHP to a recent version

Are you still using older versions of PHP? You are missing out on a lot of improvements that have been made to the Zend Engine. Every minor version in the PHP 5 lifecycle had some improvements compared to the previous versions.

Rasmus is touring conferences in the last months with his talk Speeding Up the Web with PHP 7 showing numbers for the improvements in different applications and frameworks. Lorna has a post on her blog showing improvements in every new PHP version using the Zend Bench script.

Granted, updating to new PHP versions is not always easy if you are working with legacy code. But if your application is suffering from performance problems then updating is something you should look into.

With PHP7 getting released in November 2015, we will see another performance increase of roughly 100% across all kinds of applications. That means, not having a strategy to upgrade to PHP7 in the next year (maybe two years) is careless from a performance perspective.

Another reason to upgrade to recent PHP versions is the reduced memory consumption. PHP 5.6 uses around 50% less memory than 5.3 and you can handle many more requests at the same time without exhausting server memory.

2. Use accelerator such as APC or OPCache

PHP runs your code in two steps:

  1. The script will be transformed from PHP code into opcodes by the PHP compiler.
  2. This intermediate opcache format is then executed by the PHP virtual machine.

Accelerators can cache the first step of this process until the php file changes. Depending on the kind of application you run and your PHP version, using an accelerator can double the performance and then some.

PHP 5.5 already includes Opcache, so there is another reason why you should upgrade to newer versions. On earlier versions you should use APC (5.3) or install Opcache from PECL (5.4).

If you are running on shared hosting then it is possible that your hoster disabled an opcode cache for security reasons. If you are technically savvy then you should move your code to more modern hosters that give you more control and are still very affordable. We are big fans of both Digitalocean and Syseleven for hosting.

3. Close Session for Writes

Careful with sessions in PHP: By default only one request can execute synchronously per session at the same time. Other requests for the same session will wait inside the session_start() call until the previous requests are finished. This problem becomes visible to the user when executing a lot of ajax requests on the same page, loading perfectly one after another and not in parallel.

Session locks exist, because otherwise lost-updates or corruption could happen when two requests write to the same session at the same time.

Contrary to popular believe the Memcache Session handler does not fix the locking problem anymore. Up to version 2.0 it had no locking, but this was implemented in version 3 which is now part of all major Linux distributions. The memcached extension uses locking as well.

What you need to do to fix this problem is explicitly close the session for writes. The PHP function session_write_close(); releases the lock and requests with the same session can be executed concurrently.

Only keep the session open in requests where you are writing to the session, such as the login page. In general you should avoid writing too much data to the session, so that you can close it very early in the majority of requests.

In a Tideways Timeline Trace you can identify this problem when there is a large duration for the session_start() function, in this case over 400ms:

Session Blocking in Tideways TimelineSession Blocking in Tideways Timeline

4. Don’t run XDebug in Production

The XDebug extension should not be used in production, it is a development extension. Just by installing it, your PHP code will run slower – sometimes several magnitudes.

That doesn’t make XDebug an extension non-grata, it just isn’t built for production as the author Derick Rethans mentions himself. If you absolutely need to use a debugger in production, then activate the extension only for a short amount of time on a single server.

If you need a Profiler for production look into our Tideways PHP extension. You can use it with our UI for Timeline- and Callgraph-Profiler or one of the many open-source UIs like XHGui.

Benjamin Benjamin 08.07.2015