PHP - Performance tuning

From LXF Wiki

Table of contents

Practical PHP Programming

(Original version written by Paul Hudson for Linux Format magazine issue 44.)


Optimisation is the blackest of black arts in programming, but when you don't want to go to the fuss of writing your own C extension, how else can you make your PHP code run faster? We overclock our PHP scripts...


For the past three tutorials we've been covering the devilishly hard topic of writing your own PHP extension. Yes, it can yield huge performance returns for your site, but on the other hand they're hard to make, fussy to maintain, and, after all, this is a tutorial about writing PHP, right?

Performance, particularly on busy sites, can be critical - after all, if you can speed up your code by 10%, that decreases your hardware load by 10%, saving you the need to upgrade. There are a number of ways you can improve the performance of your scripts, and we'll be covering as many as have space for. We'll also be dispelling various myths about optimisation, and hopefully by the end of this mini-series you'll be confidently about to re-write fundamentally flawed algorithms, tune implementations of good algorithms, make your MySQL queries fly, and more.

Before we begin, I'd like to make it quite clear that optimisation is the process of /improving/ performance of your code, whether to use less space or run faster - it's usually a trade-off. Optimised code isn't necessarily "perfect code", it's just better than unoptimised code.

Furthermore, there is rarely if ever such a thing as "too fast". In my spare time, I've been working on my own pet project: a gigantic online PHP strategy game. Actions occur at round end, which is triggered by a cron job every ten minutes. With a thousand dummy users in the system, round end takes over seven seconds, during which database writes are locked so that people can't make changes. In the past I've spent hours and hours just to cut one part of that round end from 0.002 seconds to 0.001 seconds per player - it might not sound like a lot, but as far as I'm concerned every single second counts. If you've tried out half of the recommendations here and find you've reduced the run-time for a script from four seconds down to one second, don't stop there - go for the fastest code you can get.


The easiest optimisation

There is one simple way you can double the speed of your server, and that is to install IonCube's PHP Accelerator - it's fast, free, and works perfectly on Linux. If you don't mind paying money and want another 20% extra speed boost, Zend's Performance Suite is the way to go - it's better-supported than PHPA, works with newer versions better, and has the edge on speed to boot. If you have a small/non-existent budget, PHPA is your best choice. If you've got a little money and really do want to push your PHP code to the max, Zend's Performance Suite will be a purchase you won't regret.

If you want to read more about PHP accelerators, check LXF34 where we did a round-up. Since that article, PHPA hasn't changed versions, but Zend Accelerator got a big upgrade and became Zend Performance Suite - it now includes various other enhancements and speed ups that should increase the lead of ZPS by a substantial margin.


Very basic optimisation

There are a lot of simple tweaks you can make to your PHP coding style that will produce small benefits, so we'll be starting there. Firstly, take advantage of the fact that PHP allows you to post-increment ($i++) and pre-increment (++$i). The meaning is the same as long as you're not writing anything like "$j = $i++;", however pre-incrementing is almost 10% faster, which means that you should switch from post- to pre-incrementing when you have the opportunity, especially in tight loops.

Secondly, run tests to see whether your scripts can benefit from a switch to using references - very often you'll find that assigning by reference and returning by reference can speed up the handling of complex objects by quite a margin, as well as also lowering your memory usage.

Thirdly, always set your PHP error level to the most verbose level, E_ALL. All too often people don't realise that PHP is outputting various complaints about variables not being set, etc, which you can just do away with entirely by cleaning up your code. While you're editing your php.ini file, it would also help to disable all the extensions you don't use - they're just chewing up memory otherwise.

That's the easy stuff out of the way - you should be able to do all the above with no bother. Onto more complicated matters, though...


Compress your output

HTML is a very wordy format - there's a lot of duplication in the form of HTML tags, but also in the main body of text. Furthermore, by default PHP will send text to Apache as soon as it's ready, which results in less efficient transfer of data.

The solution is to enable output buffering, and to use gzip compression for the buffers. Output buffering, if you were unaware, makes PHP store up its data into one big chunk, then send it to Apache all at once. Because all the data is kept together and sent all at once, PHP is able to compress it using gzip compression, which will generally reduce the content to around 33% of its original size (that is, 1MB will become 300KB).

Not all clients support receiving compressed content (every browser made in the last five years will), and to handle that PHP will only compress data if the client can support it - this means you can enable compression, and not have to worry about old clients because PHP won't send them compressed data.

To enable output buffering for all your scripts, open up your php.ini file and set output_buffering to 1 and output_handler to "ob_gzhandler" (without the quotes). You'll find those values already set in your php.ini already, so just change the existing values. You should check your phpinfo() output to make sure output buffering is enabled correctly.


Don't use CGI

If you have the choice of using PHP as an Apache module or as a CGI, plump for the Apache module each and every time. PHP running as a module is about five times faster than PHP running as a CGI, because it no longer has to perform lots of startup code whenever a script is executed - the PHP parser and modules are loaded when Apache is started, and kept in memory waiting for more scripts.

One other important reason to use the Apache module in place of the CGI executable is that PHP add-ons like Zend Performance Suite and PHPA don't work with either CGI or the CLI SAPIs, because they cannot cache across processes.


Debug your code

One problem with PHP is that, by default, if it encounters non-fatal errors messages it will just output them along with the rest of its output, which means that very often you don't notice the errors. While this might not seem like such a bad thing - after all the errors are non-fatal, right?

In the world of programming, one rule is fairly constant: code will run quickly until it has to handle errors. That is, errors in your code are likely to chew up five, ten, or even twenty times the resources that they should - you'll have noticed that Apache/PHP tends to shoot to the top of "top" and/or thrash your hard drive whenever it encounters a series of script problems.

You should thoroughly check the output your pages produce in order to make sure PHP isn't emitting errors behind your back. Alternatively, make sure error logging is turned on in your php.ini file, and check it regularly.


CROSSHEAD

Cache your pages

PHP speed boosters like Zend Performance Suite and PHP Accelerator work by caching your PHP code before it's executed - all well and good, but what if the PHP processing is taking longer than is acceptable?

Disable unused modules to save memory.


Single quotes are faster!

Did you know that using single quotes for your strings rather than double quotes can yield a substantial speed boost? If you did, I'm sorry to disappoint - this is one of the most commonly repeated performance myths about PHP. Whether you use double quotes or single quotes is pretty much beside the point - yes, there is a performance boost to use single quotes, but it's much less than 0.01%, and it's generally just not worth the extra hassle. Many people use double quotes for everything, and that's fine - use whatever you feel most comfortable using, because it won't affect the speed of your script.