markjaquith / feedback

Ask @markjaquith anything!
42 stars 4 forks source link

Finding code performance issues on WP Sites? #19

Closed jb510 closed 10 years ago

jb510 commented 10 years ago

What tools/techniques do you use for finding and fixing code performance problems on the backend of WordPress sites?

Since that may sound a bit nebulous. The example case is a site owner comes to you saying "my site has a slowness problem" or "I've been told my site is using too many server resources" and you've already ruled out "site owner error" or anything diagnosable on the front end (ie. massive images, sliders with 100 images in them, woefully underpowered server, redirect loops, etc). If it's a backend code issue how do you go about tracking down code causing the problem (slow queries, slow functions, more esoteric things)? How do you fix those problems?

My assumption is xdebug, webgrind and/or an IDE and "it depends" but I'm really hoping for a little more detail than just the tools and insight into how you use them and/or examples of common occurrences.

markjaquith commented 10 years ago

xdebug is actually the last thing I end up using. That's for the really non-obvious stuff.

First, try out the P3 plugin: https://wordpress.org/plugins/p3-profiler/

This will give you a good idea of your problem plugins.

Next, to look at stuff happening in the theme, use the timer_stop() function at strategic locations. Right after your opening <html> tag (your pre-template time), after the header, before the posts loop, and after each post in the loop. Before and after each sidebar. Before and after the footer.

The methodology here is to find your biggest problem, tackle that, then reassess. That is, look at the "chunk" that's taking the longest time to render, and focus on that. At this point you can look to see if you can use object caching or HTML snippet caching to speed things up.

Grab the Core Control plugin: http://wordpress.org/plugins/core-control/

Use that to watch for frontend-triggered HTTP requests. This is a no no no no no. If you're holding up a page render so you can talk to some remote server, stop everything and fix this. Use a cron job. Use TLC Transients. Never ever do live HTTP requests on the front end. One way I've seen people really do this wrong is by them including things from their own server via HTTP requests instead of local file includes. That is, something like this:

<?php include( 'http://example.com/ads.html' ); ?>

Instead of:

<?php include( $_SERVER['DOCUMENT_ROOT'] . '/ads.html' ); ?>

Set MySQL to use slow query logging. Set it at 2 seconds (you can dial it down to 1 second later after dealing with the REALLY bad queries... sometimes the server has a hiccup and a query will take a second, so just to reduce the amount of stuff I'm looking at, I start at 2 seconds). Identify problem queries, figure out which expensive ones run on the front end (and under what circumstances), and either rewrite them or cache them.

Look for plugins that do significant unnecessary work on every page load. Consider dynamically loading/unloading those plugins (hook in to the active plugins array with an mu-plugin dropin) so that they only run when necessary. Depending on how the plugin is written you may also be able to just remove some of the plugin's action hooks and filters.

Consider that maybe the web host just sucks or that the server is overcrowded/underpowered.

Consider switching from Apache to Nginx and PHP-FPM (or, probably very soon, HHVM). Use an object cache backend (APC, Memcached). Turn on MySQL query caching. Use a CDN.

That was a bit disjointed, but hopefully there are some helpful nuggets up there for you!

jb510 commented 10 years ago

THANKS! Very educational.

bueltge commented 10 years ago

Small hint to find performance issues in plugins, themes, wp core optional via plugin Debug Objects https://github.com/bueltge/Debug-Objects I have a lot helper, not only performance. But you can see the problems on large queries, different views form plugins, themes.

jb510 commented 10 years ago

Thanks Frank, will check that out too.

igmoweb commented 10 years ago

I think Query Monitor is a must: https://wordpress.org/plugins/query-monitor/

BUt watch out, you won't see any more notice/warnings! They will be all inside the Query Monitor menu (it will change its colour) but at least WordPress will redirect pages without any problem (you will avoid that annoying message "headers already sent").

MarioSo commented 10 years ago

I like the idea of loading plugins only when they are needed! I added a mu-plugins drop-in which sets a filter to the 'option_active_plugins' option like so:

add_filter('option_active_plugins', 'filter_my_plugins');

function filter_my_plugins($active_plugins){
  $pluginsToRemove = array();

  //logic for polluting the $pluginsToRemove array comes here

  return array_diff($active_plugins, $pluginsToRemove);
}

So far this works pretty well. Now I have got two questions: First, is this the way to go or is there a better approach?

And second, concerning the logic for adding plugins to the $pluginsToRemove array: which options/functions/variables are available at this point?

thanks :)

markjaquith commented 10 years ago

So far this works pretty well. Now I have got two questions: First, is this the way to go or is there a better approach?

You could filter active_plugins directly, and not go low-level with an options filter.

And second, concerning the logic for adding plugins to the $pluginsToRemove array: which options/functions/variables are available at this point?

Not much. is_admin() as you noted. But obviously no queries have been made yet. Because plugins need a chance to hook in and manipulate them. So really you're going to have to parse the URL. I used regular expressions to whitelist certain URL path roots, like /store/ for WooCommerce.

phillcoxon commented 10 years ago

Just wanted to say thanks - the tip re: the Core Control plugin just helped me nail a backend HTTP requests that was massively slowing down the admin backend on a site.