OK, so you’ve built your site. Now what? Focus on performance. #wcbos

Here at the BDN, we’re all a bit relieved to finally have completed the relaunch of bangordailynews.com. And one of our tasks now is to clean up all the mistakes we’ve made and all the sloppy code we’ve written while rushing toward the finish line.

A great talk by Frederick Townes, CTO of Mashable.com and the man behind the must-use W3 Total Cache plugin, gave a great talk at WordCamp Boston this weekend about performance enhancements, and I thought it’d be good to repeat some of his points and add a few of my own.

It should be noted I call out a few plugins directly in this post. I don’t mean to shame or punish them, just provide real-life examples.

Reduce Heap

One of the big points of Townes’ talk was reducing heap — essentially the compiled size of your code. The less code you pull into memory obviously the faster things will execute,

A few easy things to do — keep a close eye on when things can be loaded conditionally. All your plugins should contain a separate file for anything admin-related that should only be required conditionally. Whenever possible use require instead of require-once — the performance difference can be dramatic.

If you can, break it down even more. If you have a library you use with a function, require it inside the function instead of outside so it’s only loaded when needed. One note on this: Apparently files required inside functions are not included in your opcode cache, so be mindful when doing this. For us, though, we have a library that we use when loading videos — a PHP class for the Blip.tv API. Because we don’t use our videoplayer on every page and, moreso, we often don’t even need to use the API because we’re working out of the cache, it makes a lot of sense to only load it conditionally. Another example is the excellent Edit Flow plugin, which loads many files on the frontend even though it’s only used in the admin. A simple modification made sure files are only required if we’re working in the admin.

In terms of require_once, I wonder if it’d be quicker, although maybe not as simple, to put files you might try to require more than once in an array and then check the array before requiring it. For example:

[php]global $required_files;
if( !empty( $required_files[ $file ] ) ) {
require( $file );
$required_files[ $file ] = true;
}[/php]

Count your queries

The other day, I printed off all the queries on a few pages of our site and went through them line by line. You can see your queries by using the excellent Debug Bar. We found many instances where queries were being run when they don’t have to.

The first way to make sure you aren’t running rogue queries is to make everything conditional, as I mentioned above, and make sure you’re only running the plugin on pages that need it.

Make sure to pay attention to the autoload values in add_option and update_option. Update_option will always set autoload to yes for some weird reason, and it’s the default in add_option. If you use the meta on every page, set autoload to yes. Else, please set it to no. We learned this out by accidentally using update_option to add a few thousand rows to the db and discovered they were all being autoloaded on every page. I would love to see an autoload flag on update_option.

If possible, group related options into an array to reduce rows in the db. WordPress will automagically serialize and deserialize it for you — it’s just that smaht.

Another thing we discovered was that the Disqus plugin doesn’t stop WordPress from querying for comments even though they’re never displayed via WordPress. The first step is to allow W3TC to cache the comments. The next step is to remove those queries altogether. Mark Jaquith wrote a simple script to do just that.

Think about how your plugin will scale up. When we tested the Twitter Blackbird Pie plugin on our staging site (you do use a staging site, don’t you?) we noticed it was running thousands of queries on every page load — essentially grabbing all our users and then running a separate query to grab the meta for each. Please think about the impact of your plugin on sites bigger than a very small blog.

Don’t worry about options

Allowing users to set options can add a lot of overhead, so consider the consequences carefully. I’m a big proponent, especially when developing for a specific site, of compact, targeted plugins that do one thing very well. Whatever you do, never, ever, do this. The very cool upPrev plugin loads the entire WordPress environment into a JS file just to populate a few options. There are other ways of doing this, such as a few inline JS variables, or passing the variables to the JS file as GET vars, or simply making a few decisions for the users and making them hack the plugin if they want to change something.

Cache, Cache, Cache

As I mentioned before, W3 Total Cache is by far the best WordPress caching plugin out there. But it won’t protect you from yourself. Any time you make an HTTP call or have a really expensive query (such as the Twitter Blackbird Pie instance I talked about), be sure to throw it into the transient cache, which is a persistent version of the object cache. Use the object cache for everything else — pretty much every time you are querying many posts.

Debug

Develop locally, with WP_DEBUG on. Log to a file (setting WP_DEBUG_LOG to true) but don’t turn off error display (set WP_DEBUG_DISPLAY to true). If you can, unit test.

Make sure to check that your object cache is working regularly by turning on W3TC’s debug features. We have a few HTML comments on our site that allow us to easily keep an eye on whether things are coming out of the object cache or straight from the DB.

It never ends

These are just a few things you can do to increase performance. At the BDN we’re reviewing every plugin and theme file and seeing where we can cut out the cruft. For a fairly large site, every KB of memory used and every millisecond of processing time and every query counts. It’s a neverending pursuit, but it has to be always on your mind.

Epilogue: Max Cutler asked for a bit of benchmarking data. A crude benchmark of simply measuring the average amount of memory each page uses indicates our memory usage dropped by about 3,624 KB on average, or a little more than 12 percent of total 28,559 KB per page load, just with a few conditional requires. We’ve got lots more to do, but already those are some significant numbers.

Leave a Reply

Your email address will not be published. Required fields are marked *