This weekend I got a little fed up with how slow WordPress seems to be on Geekfun and PetProject, my wife’s blog and set out trying to figure out why.
First off, I went looking on The Google for what other people had to say about optimizing WordPress. Much of what I found is a few years old, and applied pretty specifically to older versions of WordPress. Just now, I found a post written just today on the subject of optimizing not just WordPress, but WordPress 2.7, which was only released a few days ago; unfortunately, pretty much all of the advice covered things I already knew about. One post I found was almost two years old, but the advice it gave was general enough to be relevant today. Unfortunately, it wasn’t all that relevant to me, since it covered things like tuning mySQL, and using a PHP opcode cache, neither of which I can do on Pair.com’s shared hosting. In the end, the biggest benefit from this research was learning some of the things I’m unable to do to make WordPress faster.
Armed with that reality check, I started focusing on figuring out what, exactly, was making WordPress seem slow on our sites. I started out by measuring just how slow things were. I turned to the Firebug plugin for Firefox for help there. Firebug does a lot of useful things, but I was most interested in its ability to chart out how long it takes to load each page component. I didn’t write down the specifics because I already knew the answer was too damn long.
What I did note is that it was taking a second or more before the browser even started receiving the beginning of the page HTML, and then it was taking another few seconds for all the HTML to arrive. Once enough HTML arrived, the browser would request the CSS files, which would show up, eventually. Once this happened, the browser could start rendering the page and fill things in as images and whatnot showed up. It takes something on the order of 4-5 seconds for a new visitor to actually see the web page. Too damn long.
First thing I noticed is that pretty much none of the HTML, CSS or Javascript on my blog was being transmitted in compressed form. I thought this odd, because compression can easily shrink file sizes 2-4x or more, which really helps with transmission time. I knew this, and I knew that I’d turned on gzip compression in WordPress a year or so ago, and I’d been quite happy with the results, so it was doubly odd that it didn’t seem to be coming into play. I went looking for the WordPress option to turn it back on, but turned up nothing. Googling on this one was pretty useless. I found a lot of stuff about turning on gzip compression in earlier versions of wordpress. Finally, I found something about how they’d removed it in 2.5 because it created vaguely described problems. The advice was just to turn it on at the Apache webserver level. Excellent advice, but Pair.com isn’t running mod_gzip or mod_deflate. Grrrrr.
I ended up finding a few plugins that could help me. First I looked at the gzip output, but for some reason, I didn’t even try installing it, I think because I was hoping to find something that would compress my CSS and Javascript files too. The WordPress version of PHP Speedy seemed like it would be ideal. It will compress dynamic pages, and it will compress and cache static javascript and css. In addition it can do other tricks to combine css and javascript into a smaller number of files to improve performance further. Actually, its these file combining tricks that are PHP Speedy’s raison d’ etre, where as the straightforward stuff like gzipping stuff before sending it was optional. This mattered, because some things seemed a little busted with my pages when PHP Speedy was running. It’s a cool idea though, I’ll have to check back. Script Compressor will compress both CSS and Javascript, but it looked like it might require me to modify template files, and even without modifying template files, it broke my pages. I then turned to WP JS & WP CSS, wich optimize, gzip and cache their respective file types, but, unfortunately, they appear to require template modification, something I’m hoping to avoid, since it will make it more difficult to upgrade my theme.
I have a feeling I’m going to come back to JS and CSS compression before too long, but for now, I’m taking advantage of having the HTML itself compressed. Right now, I’m using the GZip output plugin to compress the HTML on this blog, and taking advantage of the compression feature of WordPress Super Cache on my wife’s.
WordPress Super Cache comes up a lot when you go looking for info on optimizing WordPress. Gzipping pages is really just secondary to its main function, which is caching static versions of wordpress pages and then serving them up without loading most of WordPress, or even involving PHP at all. It is pretty elegant in doing what it does, but it doesn’t sit quite right with me. First off, it does nothing for the css and js files, I still have to find a solution for that. More importantly though, it strikes me as wrong that I have to rely on static pages just to provide a trickle of users with something approaching reasonable performance. This is not to slight WP Super Cache, its main focus is less about simple performance, and more about being able to service high loads effectively. So, I have a principled objection, but I’ve activated it on PetProjectBlog and its helped out a lot. Normally it takes WordPress somewhere shy of a half a second to a second to produce a page. With WP Super Cache, once that page has been produced once, subsequent requsts are filled in a tenth of a second or less.
WP Tuner provides a detailed view inside of WordPress. Most (if not all) WordPress themes record the total execution time and number of queries required to produce a page in a comment in the footer of the page. From this, I learned that it was taking somewhere between a half-second and a second to generate a page, which already represents a significant % of the response time I’d like to see. WP Tuner helped me understand where that time was being spent. It shows the time taken by 12 or so major parts of the WordPress page creation process, and how much of that time was spent waiting for the database. It also shows what’s using the database at a finer grained level, and can show, in detail, which queries were run.
The first thing I learned is that something like 50% of the time spent by WordPress just goes to starting up (which it has to do for each request). By sticking some timing statements into the WordPress startup code I was able to see that a big part of that goes to starting up the 12 or so plugins I have active, with about half of that spent starting just a few plugins. The amount of time spent in startup bothers me, but I couldn’t figure out what I could do to reduce it significantly. Even giving up the functionality provided by the most startup-intensive plugins would only shave ~12% or so off the overall request time.
Less than 5% of the startup time and only about 10% of the entire request processing time was spent doing database queries, which I was glad to see. I’d been worried that my hosts database servers weren’t carrying their weight and I’d spent some time trying to understand what the state of WordPress DB caching was since this is another area that has evolved a lot over the last couple of years.
I think that brings me to where I am now.
- Both blogs are benefitting from gzip compression of the HTML to shave the better part of a second off the time it takes to receive the all the HTML.
- PetProject Blog has shaved 300-900ms from the time it takes to even send the first part of a page by using WP-SuperCache.
- I still need a solution for compressing javascript and css files. I think I’ll probably end up with a small shell script that runs through the plugin and theme directories and gzips all the appropriate files, then I’ll put in some mod_rewrite rules to serve the compressed versions to compatible browsers. Ideally, I’d like a PHP script that produces the .htaccess rules and compresses assets the first time they are requested without having to modify any of my themes, but that would just be gravy.
- I’d like to find a way to halve startup time without giving up functionality. I think this will probably require that I find hosting that includes the use of an PHP opcode cache.
- Alternate hosting may help by getting me on a faster machine. Right now, Pair.com has me on an AthlonXP 2800+ with 2GB of RAM. That’s not a bad machine for a lot of things, even though it must be 5 years old, but it ain’t great for a shared webserver that hosts some dynamic apps. It only has one core, and that core is probably, at best, half as fast is one of the cores in the CoreDuo machines they have. When it comes down to it though, I don’t have a good idea of how much a faster CPU will help. My guess is that random disk IO is a big part of the startup cost, and the CPU isn’t going to help there. If I have time, I’ll have to try setting things up on one of my home machines and see how things break down. Short of finding a new host though, I can probably get Pair to move me to a newer machine. I’ve been a customer for a decade or so.
- Beyond this, I could look to pushing static assets, including css, javascript, and at least some of my images off to a different IP (to get around limits browsers place on multiple requests from a single server), or taking advantage of Amazon’s new CloudFront pay as you go Content Delivery Network which would push things out to servers geographically closer to users.
Update: I tried running WordPress on a machine at home with the same basic set of plugins. Per-request startup times were still on the order of 250-300ms, even though the machine was completely unloaded, had plenty of RAM free, and has a faster processor than the machine my site is hosted on at Pair, which makes me think that I/O is the big issue.
Next I installed the APC, which among other things, caches the parsed application code in memory the first time its loaded. This made a huge difference. Startup times dropped 10x to about 25-30ms, which had the effect of cutting the total request processing time by half. Much much better. I also noted that the per requrest memory consumption also dropped significantly, from 11MB to about 1.1MB. Unfortunately Pair.com doesn’t include APC, probably because it can take some tweaking to get it to work stabley with some PHP apps. Also, it may have security issues in shared environments. It looks like I’m going to have to try alternative hosting if I want to improve WordPress performance further.
Since yesterday we have one another great caching plugin: DB Cache
Are you talking about this?
I think this does something similar by creating a file based backend for the built in wordpress caching framework. It sounds like this file-based mechanism was built-in to wordpress 2.1 and removed in 2.5 because a lot of people had complaints from their shared hosting providers that it was causing excessive IO.
Assuming they are similar, it definitely makes a difference on my web host, but the query time was only 10% of so of total request processing time before, so eliminating it doesn’t have a big impact on overall responsiveness. What’s really needed is something that can accelerate the startup time, particularly the loading of plugins.
Hi,
I don’t know what version of PHP Speed WP, but the version I’m running shaved 17% off load times, compresses the CSS and JS, and automatically moves the CSS to load first, and JS last. It has reduced the db calls. I blogged about it here – http://itjuggler.ollis.id.au/2009/02/16/wordpress-on-speedy-wp/
Thanks for the tip. I’ll need to give it another look, though from my understanding of how it works, I don’t understand why it would cut DB calls.
I switched to a new webhost that uses an opcode cache and that has made a huge difference.