PHP Optimization – Where to start
“Our web app is slow,” complained the boss.
“Well, I hear that using commas instead of periods with echo() makes PHP faster,” replied the code monkey.
“Well, let’s try optimizing that,” said the boss.
“Well, it’s still slow after that change. I guess we’ve reached the limits of PHP. Time to switch to Java.”
If you run a website that becomes successful, at some point, you’re going to run into performance issues. Heck, you may only need a single visitor with some scripts to bring it to a crawl. There are a few different approaches often used for optimizing PHP, but really only one right one. First there’s the old school way, dump in a bunch of var_dumps of script execution time and hope to find a slow spot. Not very efficient.
Then there’s what I like to call the cluster-bomb optimizing approach. These are those lists of 40 things to do to optimize PHP and the like. I call this the cluster-bomb approach because you’re just throwing a bunch of tiny optimizations all over the place hoping that you hit one that’s causing your performance issues. A lot of these recommendations will leave you with a trashed collection of unmaintainable code. A cluster-f*** of code so to speak.
Very rarely will the majority of these recommendations actually make an impact to a real-world performance issue. I know this because I regularly fix real-world performance issues, and I rarely follow most of the optimization “tips” in those lists.
And then there’s the profiling method. This I think is the only right way to begin any sort of optimization process, in PHP or any other language. For the uninitiated, a profiler is a process that will sit with your script handler and generate a detailed log of exactly what happened during a script run, how long a subprocess took, etc. You can then take this log and get a visual representation of your scripts performance. From there, it’s really easy to see where the performance bottlenecks are in the system, and give you a good idea of where the best effort could be put in optimization.
For PHP optimization and profiling, the tools that I like to use are Xdebug and KCacheGrind. Xdebug is the profiler, and KCacheGrind is the graphical reader of the profiler. First, install Xdebug on your PHP server. You’ll need to be able to generate a profiler log. You can do this by either enabling it in your core php.ini file, activating it from within your script, or, my preference, turning on the xdebug.profiler_enable_trigger setting in your php.ini so you just have to add XDEBUG_PROFILE=1 as a parameter in your URL or POST parameters, and you can generate the profiler files easily on demand.
Once you’ve generated the profiler log, open it up in KCacheGrind. This is a very powerful tool that I won’t be covering very in depth, but it does make finding problem spots in the code very simple. Using the Callee Map tab on the right side of the screen, you will see a visual representation of how long the calls in your script were for each run. The larger the box, the more time relative to the other process was spent in that process. By clicking on a box, you can see a stack trace of where that specific box is.
The visual profile of a complex OO script may look something like this
The largest block there represents 17% of the run time of that script. If I wanted to improve performance of this script, I may want to look there. Or maybe this tells me that I need to optimize system performance by looking at something other than script internals. Or maybe I need to look at ways to reduce the number of calls in the script run. Each situation has its own unique requirements, and there is often more than one good approach to solving the problem.
If you have a slow performing script with a bottleneck, you may see something more like this.
The two largest boxes there represent 80% of the total script run time. This is definitely where the script bottleneck is, and performance efforts would be best directed. Directing any effort anywhere else wouldn’t have any significant impact as it represents <20% of the script run. In this particular case, the two boxes were a couple of sloppy SQL queries (something, btw, which isn’t even on the 40 ways to optimize PHP list). But by cleaning those up, I was able to easily do a 70% performance improvement to the script run time.
Now, a code bottleneck is certainly not going to be the cause of every problem, but it will be found with the majority of performance issue you’ll come across. And each bottleneck needs to be treated differently, more to come on that later. But by using the right tools and taking the few minutes to analyze your script, you’ll quickly gain excellent insight into how to best direct your optimization efforts.
Using these tools to find where to optimize, I was able to reduce script run time by 70% in about 20 minutes. Let’s see you do that by swapping out your double quotes for single quotes (#28).