“Smiley” Peformance Monitor plugin for WordPress
As there have been
I previously investigated the memory use of WordPress 2.0.2. I shall build upon that work to introduce execution timestamps and database queries into the performance graph. I shall then directly compare the performance of 2.0.3 and 2.0.4 on the same dataset – my blog, of course!
Review
My previous work resulted in each execution of WordPress generating and storing a graph of program memory usage. The performance issues reported in WordPress 2.0.4 seem to be related to excessive database queries so I want to probe how many are executed and when. WordPress already tracks this information, so gathering it is easy.
Preparation
I begin afresh with WordPress 2.0.3 and an extraction of the previous memory graph source into a new file named performance-probe.php, as follows:
function performance_probe( $tag ) { global $wpdb; global $performance_graph; $performance_graph[] = array( $tag, memory_get_usage(), timer_stop(), $wpdb->num_queries ) ; } function performance_probe_dump_graph() { performance_probe( $_SERVER['REQUEST_URI'] ); $filename = $basename = ABSPATH . '/wp-content/perfgraph-'.date('Ymd-His'); while( file_exists( $filename ) and ++$seq < 100 ) $filename = $basename . '-' . $seq; global $performance_graph; file_put_contents($filename, gzcompress(serialize($performance_graph)) ); } register_shutdown_function( 'performance_probe_dump_graph' );
This file is included at the beginning of the WordPress index.php. The probe is invasive in nature, requiring a modification, albeit simple, to the WordPress core, in wp-includes/functions.php, at the top of function do_action().
function do_action($tag, $arg = '') { if(function_exists('performance_probe')) performance_probe($tag);
Finally I import a data dump of my live blog, on which I shall generate the graphs for 2.0.3, upgrade to 2.0.4 and regenerate the graphs.
Ready
The performance testing blog is here.
Taking a Dump
The performance reading dumps to follow were produced using this PHP script.
<?php $graph = unserialize(gzuncompress(file_get_contents($_SERVER['argv'][1]))); foreach( $graph as $index => $reading ) { $tag=$reading[0]; $memory=$reading[1]; $timestamp=number_format($reading[2],3); $queries = $reading[3]; $delta = $memory - $delta; echo "$index,$tag,$memory,$delta,$timestamp,$queries\\n"; $delta = $memory; } ?>
Libertus says:
WordPress 2.0.3 Performance
Non-widget Theme
My first tests with the default, non-widgetized theme, are as follows:
1,init,3453264,69144,0.100,2
2,set_current_user,3463160,9896,0.102,4
3,pre_get_posts,3450648,-12512,0.105,4
4,template_redirect,3589808,139160,0.136,8
5,wp_head,3603096,13288,0.139,8
6,loop_start,3594464,-8632,0.142,11
7,loop_end,3618680,24216,0.214,11
8,wp_meta,3754464,135784,0.269,19
9,wp_footer,3746200,-8264,0.270,19
10,/~paul/wp-perftest/,3739408,-6792,0.270,19
1,init,3453264,69144,0.099,2
2,set_current_user,3463160,9896,0.101,4
3,pre_get_posts,3450856,-12304,0.106,4
4,template_redirect,3589960,139104,0.112,8
5,wp_head,3603248,13288,0.116,8
6,loop_start,3594616,-8632,0.123,11
7,loop_end,3618752,24136,0.191,11
8,wp_meta,3754536,135784,0.244,19
9,wp_footer,3746272,-8264,0.245,19
10,/~paul/wp-perftest/,3739480,-6792,0.245,19
1,init,3453264,69144,0.098,2
2,set_current_user,3463160,9896,0.100,4
3,pre_get_posts,3450648,-12512,0.103,4
4,template_redirect,3589808,139160,0.109,8
5,wp_head,3603096,13288,0.113,8
6,loop_start,3594464,-8632,0.115,11
7,loop_end,3618680,24216,0.187,11
8,wp_meta,3754464,135784,0.240,19
9,wp_footer,3746200,-8264,0.242,19
10,/~paul/wp-perftest/,3739408,-6792,0.242,19
The test homepage uses 19 database queries and renders in roughly 0.25 seconds.
Add Widgets
I use the “Recent Comments” and “Recent Posts” widgets, both of which require database queries, so test with these activated is important.
1,init,3719480,84240,0.126,2
2,set_current_user,3719304,-176,0.128,4
3,widgets_init,3737760,18456,0.182,4
4,pre_get_posts,3719808,-17952,0.187,4
5,template_redirect,3854496,134688,0.221,8
6,wp_head,3868008,13512,0.225,8
7,loop_start,3859376,-8632,0.229,11
8,loop_end,3883384,24008,0.316,11
9,wp_meta,3896848,13464,0.322,12
10,parse_query,3910552,13704,0.327,14
11,pre_get_posts,3910968,416,0.327,14
12,loop_start,3954648,43680,0.331,17
13,loop_end,3954672,24,0.349,17
14,wp_footer,4076424,121752,0.408,24
15,/~paul/wp-perftest/,4069184,-7240,0.408,24
1,init,3719656,84328,0.105,2
2,set_current_user,3719480,-176,0.107,4
3,widgets_init,3737936,18456,0.147,4
4,pre_get_posts,3719728,-18208,0.151,4
5,template_redirect,3854520,134792,0.157,8
6,wp_head,3868032,13512,0.161,8
7,loop_start,3859400,-8632,0.163,11
8,loop_end,3883488,24088,0.237,11
9,wp_meta,3896952,13464,0.240,12
10,parse_query,3910656,13704,0.244,14
11,pre_get_posts,3911072,416,0.244,14
12,loop_start,3954752,43680,0.250,17
13,loop_end,3954776,24,0.268,17
14,wp_footer,4076528,121752,0.315,24
15,/~paul/wp-perftest/,4069288,-7240,0.315,24
1,init,3719656,84328,0.107,2
2,set_current_user,3719480,-176,0.110,4
3,widgets_init,3737936,18456,0.146,4
4,pre_get_posts,3719728,-18208,0.154,4
5,template_redirect,3854520,134792,0.160,8
6,wp_head,3868032,13512,0.164,8
7,loop_start,3859400,-8632,0.167,11
8,loop_end,3883488,24088,0.234,11
9,wp_meta,3896952,13464,0.237,12
10,parse_query,3910656,13704,0.241,14
11,pre_get_posts,3911072,416,0.241,14
12,loop_start,3954752,43680,0.246,17
13,loop_end,3954776,24,0.265,17
14,wp_footer,4076528,121752,0.313,24
15,/~paul/wp-perftest/,4069288,-7240,0.313,24
As expected, Widgets on the homepage cause it to use more queries and load slower, but not by any excessive amount – 5 more queries and 0.06 seconds longer.
August 6, 2006, 12:06 pmLibertus says:
Upgrade to WordPress 2.0.4
Copy the files, which disables the performance probes. There’s no need to run the upgrade script (the database schema has not changed), and I’m not asked to, but I do anyway.
August 6, 2006, 12:11 pmLibertus says:
WordPress 2.0.4 Performance
First, I reappy the performance probe change to
index.phpandwp-includes/functions.php. Next, I view the test site. There is no perceptible performance difference, but I notice the widget changes need to be reapplied to the theme.That done, and a few more reloads, there is still no perceptible performance difference. Now, I can’t say I’m able to notice differences when performance is already in the sub-second range, so what to the numbers say?
1,sanitize_comment_cookies,3715736,4880,0.107,2
2,init,3781672,65936,0.111,2
3,set_current_user,3791112,9440,0.113,4
4,widgets_init,3799880,8768,0.121,4
5,pre_get_posts,3787704,-12176,0.125,4
6,template_redirect,3930560,142856,0.160,8
7,wp_head,3942688,12128,0.163,8
8,loop_start,3936176,-6512,0.165,11
9,loop_end,3959992,23816,0.237,11
10,wp_meta,3972048,12056,0.242,12
11,parse_query,3986528,14480,0.246,14
12,pre_get_posts,3986944,416,0.247,14
13,loop_start,4030936,43992,0.258,17
14,loop_end,4030872,-64,0.273,17
15,wp_footer,4152736,121864,0.322,24
16,/~paul/wp-perftest/,4145304,-7432,0.322,24
1,sanitize_comment_cookies,3715736,4880,0.108,2
2,init,3781672,65936,0.112,2
3,set_current_user,3791112,9440,0.114,4
4,widgets_init,3799880,8768,0.122,4
5,pre_get_posts,3787704,-12176,0.126,4
6,template_redirect,3930560,142856,0.137,8
7,wp_head,3942688,12128,0.140,8
8,loop_start,3936176,-6512,0.142,11
9,loop_end,3959992,23816,0.214,11
10,wp_meta,3972048,12056,0.216,12
11,parse_query,3986528,14480,0.225,14
12,pre_get_posts,3986944,416,0.225,14
13,loop_start,4030936,43992,0.231,17
14,loop_end,4030872,-64,0.245,17
15,wp_footer,4152736,121864,0.293,24
16,/~paul/wp-perftest/,4145304,-7432,0.293,24
1,sanitize_comment_cookies,3715736,4880,0.107,2
2,init,3781672,65936,0.111,2
3,set_current_user,3791112,9440,0.113,4
4,widgets_init,3799880,8768,0.126,4
5,pre_get_posts,3787704,-12176,0.129,4
6,template_redirect,3930560,142856,0.136,8
7,wp_head,3942688,12128,0.139,8
8,loop_start,3936176,-6512,0.142,11
9,loop_end,3959992,23816,0.220,11
10,wp_meta,3972048,12056,0.223,12
11,parse_query,3986528,14480,0.227,14
12,pre_get_posts,3986944,416,0.227,14
13,loop_start,4030936,43992,0.232,17
14,loop_end,4030872,-64,0.246,17
15,wp_footer,4152736,121864,0.298,24
16,/~paul/wp-perftest/,4145304,-7432,0.298,24
Nothing. The homepage hasn’t slowed down. So, move on to a specific use case.
August 6, 2006, 12:14 pmLibertus says:
Robert Deaton’s Use Case
On thewp-hackers mailing list , 5th August 2006 at 23:00, Robert Deaton described a particular use case in which slow-down occurs.
Analysis
The homepage isn’t cited as being slow. This sounds like a single-page view with a sidebar, accessed by permalink.
Checking my test data, I don’t have a decent number of pages (11), so I create 10 more. I do have fancy permalinks enabled and it’s easy to make the Widget sidebar with page listing appear in the single-page view.
First Test
I run the homepage then switch to a single page from the sidebar, which I reload a couple of times, after a bit of wrangling with
.htaccessto get the permalinks working. The results are interesting.Homepage
1,sanitize_comment_cookies,3732064,4960,0.139,2
2,init,3798032,65968,0.147,2
3,set_current_user,3807472,9440,0.149,4
4,widgets_init,3816240,8768,0.159,4
5,pre_get_posts,3825864,9624,0.165,4
6,template_redirect,3968440,142576,0.207,8
7,wp_head,3980568,12128,0.210,8
8,loop_start,3974056,-6512,0.215,11
9,loop_end,3996240,22184,0.292,11
10,wp_meta,4008296,12056,0.297,12
11,parse_query,4022776,14480,0.302,14
12,pre_get_posts,4023192,416,0.302,14
13,loop_start,4067184,43992,0.307,17
14,loop_end,4067120,-64,0.321,17
15,wp_footer,4215728,148608,0.383,24
16,/~paul/wp-perftest/,4208296,-7432,0.383,24
Single Page
1,sanitize_comment_cookies,3732856,5384,0.104,2
2,init,3798768,65912,0.108,2
3,set_current_user,3808208,9440,0.111,4
4,widgets_init,3816976,8768,0.119,4
5,parse_query,3819184,2208,0.123,4
6,pre_get_posts,3819632,448,0.123,4
7,template_redirect,4013528,193896,0.135,15
8,wp_head,4025792,12264,0.144,15
9,loop_start,4019304,-6488,0.146,18
10,loop_end,4028888,9584,0.194,18
11,wp_meta,4040152,11264,0.196,18
12,parse_query,4065184,25032,0.201,21
13,pre_get_posts,4065600,416,0.201,21
14,loop_start,4105600,40000,0.207,24
15,loop_end,4105704,104,0.220,24
16,wp_footer,4254632,148928,0.280,31
17,/~paul/wp-perftest/zeitgeist/,4249616,-5016,0.280,31
Observations
The number of queries for the page view is 7 more than for the homepage, for no obvious reason, which deserves further investigation. The page load time is not affected.
Investigation
The extra 7 queries occur between action
pre_get_postsand actiontemplate_redirect. Why does a single page view need so many more queries than the homepage? At a guess, I’d say security (there’s an “Edit this page” link), but I can’t account for 7 queries with that alone. Time to look in the code, down theis_single(),is_page()and!is_home()paths, starting fromwp-includes/classes.php, functionWP_Query::get_posts().This yields a block of code, conditional on query parameter
pagename, containing a call toget_all_page_ids()and a loop callingget_page(). This loop appears to be the means by which the ID of the required single page is found. I can prove this by choosing a different page, perhaps one of the test pages I added, and see how many queries are performed, betting that it’s no longer 7 more than the homepage.1,sanitize_comment_cookies,3732736,5312,0.102,2
2,init,3798672,65936,0.106,2
3,set_current_user,3808112,9440,0.108,4
4,widgets_init,3816880,8768,0.117,4
5,parse_query,3814704,-2176,0.119,4
6,pre_get_posts,3815248,544,0.119,4
7,template_redirect,4068240,252992,0.151,30
8,wp_head,4078784,10544,0.154,30
9,loop_start,4071592,-7192,0.156,33
10,loop_end,4071640,48,0.157,33
11,wp_meta,4083384,11744,0.159,33
12,parse_query,4116720,33336,0.165,36
13,pre_get_posts,4117136,416,0.165,36
14,loop_start,4158400,41264,0.199,39
15,loop_end,4158504,104,0.213,39
16,wp_footer,4303872,145368,0.277,46
17,/~paul/wp-perftest/performance-test-rd-usecase-page-7/performance-test-rd-usecase-page-8/performance-test-rd-usecase-page-9/performance-test-rd-usecase-page-10/,4298856,-5016,0.277,46
I think this illustrates the problem nicely. The linear page ID search algorithm is rather inefficient.
August 6, 2006, 12:29 pmLibertus says:
Upgrade Problem or Not?
This probably isn’t the upgrade problem, but it is certainly a problem one might encounter upon upgrade to a version that contains this inefficient search code. Using theWordPress Trac I can browse the source looking back through the versions to find out in which the offending code was introduced.
No, this isn’t an upgrade problem at all. It’s just a problem my blog already has.
August 6, 2006, 2:06 pmLibertus says:
The “Smiley” Performance Monitor Plugin for WordPress
Just for fun. Whilst activated, it puts a couple of smilies at the top-left of the page to indicate how hard WordPress had to work to make the page.
“Smiley” Peformance Monitor plugin for WordPress
August 6, 2006, 5:05 pmLibertus says:
Performance Probe Plugin
Thanks to MarkJaquith on #wordpress for this freaky idea, which works! With a different one-line change to
do_action(), the performance probe becomes a plugin.I also experimented with a non-invasive plugin using
August 12, 2006, 12:21 pmadd_action( 'all', 'performance_probe' );but this gets called for all filters too and so generates too much information to be useful.