Elliott C. Back: Internet & Technology

Optimizing WordPress Performance & Speed

Posted in Blogging,Code,Performance,Wordpress by Elliott Back on July 27th, 2006.

We start with a fresh copy of WordPress 2.03, fresh from the WordPress download center. Our goal is to figure out what parts are slow and can be improved. We’re not interested in sacrificing features or existing code for speed, like Lightpress has done. Rather, we’d like to identify the worst performers and improve them, if possible, in the default install.

Our microscope will be XDebug, a PHP performance tool that we’ve installed as a zend extension on our server. It’s a C module that keeps track of the time spent running PHP code, so we can grab and analyse its output to determine what WordPress is doing under the hood.

Our data set will be all the posts, comments, and pages from this blog. Currently, that is 1405 posts with 6977 comments and 4 pages. This should provide ample material for testing. The database will be optimized after importing.

Initial profiling

Loading the main WordPress page produces the following traces:

wordpress-trace-01.jpg

10,000 calls to preg_replace, 6,410 to str_replace, and 2,465 to strstr.

wordpress-trace-02.jpg

wptexturize is the heaviest function at 24.4% of its own code, 5.5% on mysql, and 4.5% on apply_filters. Template loading, and php compilation (require_once) are together 30-40% of the loading, as well.

First optimizations

The first thing I notice is that index.php sets a config variable and just includes another file. Why not just move that define() into wp-blog-header.php? It’ll save a function call, and not a lot of time, but it’s cleaner. Also, the ABSPATH define should be done before any other files are included, and thus copied from wp-config.php to index.php to save a few more function calls. There’s a line that sets the timer with an extra assignment statement.

Nitpicking’s not going to get me anywhere. Let’s take a look at wptexturize. We note that we can replace some preg_replace calls with str_replace, because only static strings are replaced, so we add the [‘\’s’,’’s’] to the cockneyreplace array hack. We apply this process to all static strings. We can also convert the strings from slower dynamic double quotes to faster static single quotes, and put the dynamic section into another array, like the static section.

These simple operations reduce the time spent inside wptexturize from 24% to 16%, and the time from 600ms to 200ms. We’ve also reduced the number of preg_replace calls dramatically, from 10,439 to 3,289 and total time from 74ms to 36ms. We’ve gone from 54ms of 6,410 str_replace calls to 29ms of 1,405 calls. Why is this? By calling each function only once we save a lot of extra PHP operations and just hand off a bunch of data to fast, underlying C functions. Interestingly, using array_walk/array_map is not faster than a plain loop.

Here’s the new wptexturize function:

<?php

function wptexturize($text) {
    $next = true;
    $output = '';
    $curl = '';
    $textarr = preg_split('/(<.*>)/Us', $text, -1, PREG_SPLIT_DELIM_CAPTURE);
    $stop = count($textarr);
    
    for($i = 0; $i < $stop; $i++){
        $curl = $textarr[$i];
        
      if (isset($curl{0}) && '<' != $curl{0} && $next) { // If it's not a tag
          // static strings
          $static_characters = array('—', ' — ', '–', 'xn--', '…', '“', '\'tain\'t', '\'twere', '\'twas', '\'tis', '\'twill', '\'til', '\'bout', '\'nuff', '\'round', '\'cause', '\'s', '\'\”, ' ™');
          $static_replacements = array('—', ' — ', '–', 'xn–', '…', '“', '’tain’t', '’twere', '’twas', '’tis', '’twill', '’til', '’bout', '’nuff', '’round', '’cause', '’s', '”', ' ™');
          $curl = str_replace($static_characters, $static_replacements, $curl);
  
          // regular expressions
          $dynamic_characters = array('/\'(\d\d(?:’|\')?s)/', '/(\s|\A|”)\'/', '/(\d+)”/', '/(\d+)\'/', '/(\S)\'([^\'\s])/', '/(\s|\A)”(?!\s)/', '/”(\s|\S|\Z)/', '/\'([\s.]|\Z)/', '/(\d+)x(\d+)/');
          $dynamic_replacements = array('’$1','$1‘', '$1″', '$1′', '$1’$2', '$1“$2', '”$1', '’$1', '$1×$2');    
          $curl = preg_replace($dynamic_characters, $dynamic_replacements, $curl);
      } elseif (strstr($curl, '<code') || strstr($curl, '<pre') || strstr($curl, '<kbd' || strstr($curl, '<style') || strstr($curl, '<script'))) {
          // strstr is fast
          $next = false;
      } else {
          $next = true;
      }
          
      $curl = preg_replace('/&([^#])(?![a-zA-Z1-4]{1,8};)/', '&$1', $curl);
      $output .= $curl;
    }
    
    return $output;
}
?>

Update: As of change #4511 in 11/21/06 Ryan Boren merged this into WordPress core code. So you now have it!

What’s next

A new look at the numbers shows the next biggest culprit is apply_filters. Even on a default installation, it’s slow, taking 106ms of its own time, and 438ms total. Unfortunately, there doesn’t look to be an easy way to optimize it. Other slow spots, like get_settings and list_cats are likewise difficult to immediately improve. An easy way to increase performance would be to rewrite the plugins architecture and improve the filters mechanism, but that would mean an API change.

Some good news

It’s not all bad though; here’s how the change to wp-texturize performs, tested on PHP 5 windows on a 1.8 GHz machine, with 10 large entries on the homepage:

ab -n 100 -c 1 localhost/test/
Document Length: 190017 bytes
Requests per second: 0.78 [#/sec] (mean)
Time per request: 1278.438 [ms] (mean)

Now, here’s the default install of WordPress 2.0.3:

ab -n 100 -c 1 localhost/test/
Document Length: 189786 bytes
Requests per second: 0.77 [#/sec] (mean)
Time per request: 1297.344 [ms] (mean)

Conclusion

While the new wptexturize function performs well on a server with an opcode cache, the performance increase is lost among other considerations on an “out of the box” configuration. Also, a quick glance at the WordPress core code shows no easy patches to increase performance by large orders, just design ideas that could be gradually improved over time.

This entry was posted on Thursday, July 27th, 2006 at 3:43 pm and is tagged with . You can follow any responses to this entry through the RSS 2.0 feed. You can leave a response, or trackback.

12 Responses to “Optimizing WordPress Performance & Speed”

  1. Dorel Tanase says:

    I think my site whit Page Speed is OK, but I em not shure ! I hawe 90-91% Please tell my it is ok ?
    Best regards ! Dorel

  2. Steve says:

    SEO is very good to get best rank in search engine but need to spend long time to get best result. Patient also is important thing and must do more study about the SEO optimizing. If you are really master on SEO its like you can easy success on what ever blog platform you create beside the wordpress

  3. Elliott Back says:

    Actually, they did as of late 2006. You can view the changeset on the WordPress bug tracker.

  4. hm2k says:

    Then, why don’t they include this in the core code?

  5. CDN Guy says:

    Outsourcing static content delivery (including JavaScripts, Style sheets etc) to low cost CDN’s is always a good way to make your wordpress blog perform faster and provide better user experience.

  6. […] Optimasi bisa juga dilakukan ke script WordPress, misalnya dengan memperbaiki function call. Ini juga cukup menarik dilakukan, meskipun tidak se-ngefek pakai WP-Cache atau APC. Teknik ini tertulis lengkap di: elliottback.com/wp/archives/2006/07/27/optimizing-wordpress-performance-speed/ […]

  7. […] Optimizing WordPress Performance and Speed […]

  8. technabob says:

    well… I decided to drop in your updated wptexturize as a test, and it SEEMS like my overall blog performance is much better than it was previously. I know you were saying that performance gains would likely not be noticeable, but from everything I can tell, it really helped with some sluggishness I was experiencing here.

  9. […] While investigating I found this link regarding wordpress performance. […]

  10. erik says:

    (while i don’t know/dont want to learn much about lightpress’s core)
    what results would lightpress’s 2.0 beta offer in such a test? i have no idea what amount of work this test took you, but maybe you want to provide the same infos on lightpress – so that i could compare.

Leave a Reply