Often, when someone asks how they could make their WordPress implementation faster, the first optimization target is the database.

MySQL is slow, so if we can avoid using it everything will be faster.

The second target, however, is markup optimization and sometimes is closely coupled with MySQL optimization. One way to solve this problem is with full page caching - storing the entire output of a page in some sort of highly responsive caching system (like in-memory Memcached).

The other way is to selectively store fragments of this generated output in the cache. Full page generation can be sped up when database-intensive lookups no longer need to hit the database, but use an already-rendered piece of markup.

The Secret Sauce

In WordPress, fragment caching is straight-forward. Consider the following (uncached) function for generating markup.

function generate_output() {
$output = '

';

$output .= get_header_from_db();
$output .= '';
$output .= get_title_from_db();
$output .= '
';

foreach ( get_items_from_db() as $item ) {
$output .= render_item( $item );
}

$output .= '

';

return $output;
}

This function could be used to generate a piece of markup in a widget, in a template part, or in a template tag used by a plugin. Where the code exists isn't important - the fact that the code has to make several round-trips to the database to generate content is important.

Each round trip is added latency for your visitors - if you can't consolidate queries to alleviate the issue, skipping the database entirely is your next option.

The code above could be refactored to cache a fragment of markup as follows:

function generate_output() {
// Attempt to get our markup from the cache
$output = wp_cache_get( 'my_output' );

if ( false === $output ) {
$output = '

';

$output .= get_header_from_db();
$output .= '';
$output .= get_title_from_db();
$output .= '
';

foreach ( get_items_from_db() as $item ) {
$output .= render_item( $item );
}

$output .= '

';

// Cache our markup for an hour
wp_cache_set( 'my_output', $output, 'fragment_cache', 60 * 60 );
}

return $output;
}

Notes

The code above is using WordPress' [cci]wp_cache_set()[/cci] (and its family of caching functions) based on the assumption that you have an advanced object cache available. If no such cache is installed (or if you're building code in a plugin for general release), you'll want to instead use WordPress' transients API to store your data.

The advanced object cache will still run - the code won't error. But cached data will only live for the current request, making your cached function no more performant than its uncached equivalent. Transients will store data in the database by default, but use a persistent object cache if available.