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.
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’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.
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.