Using WP-CLI wp profile to Diagnose WordPress Performance Issues

Daniel really pulled out all the stops when he created the wp profile command – kind of like a New Relic alternative (see this great Kinsta post about using New Relic) it can help pinpoint which components are slowing your WordPress site down. Originally available from runcommand as a premium package, it is now free on github.

If you are looking for a host that allows you to install wp profile check out Kinsta and Cloudways.

I use wp profile regularly to diagnose performance issues for my clients on Codeable.

Using WP-CLI wp profile to Diagnose WordPress Performance Issues

This will show you how to install wp profile and use it effectively to identify performance bottlenecks on your site. I recommend testing wp profile on staging servers so there are no other variables affecting load times.

Install wp-profile package

Fire off this command to install wp profile.

wp package install wp-cli/profile-command

You should see that the process was successful and the wp profile command is now available

Installing package wp-cli/profile-command (dev-master)
Updating /root/.wp-cli/packages/composer.json to require the package...
Using Composer to install the package...
---
Loading composer repositories with package information
Updating dependencies
Resolving dependencies through SAT
Dependency resolution completed in 0.220 seconds
Analyzed 4579 packages to resolve dependencies
Analyzed 190261 rules to resolve dependencies
Package operations: 0 installs, 0 updates, 0 removals
Generating autoload files
---
Success: Package installed.

If you see this error ‘Killed‘ when trying to install wp profile it can be fixed by using a swap file and/or updating php.ini for cli to have memory_limit = 256M

Installing package wp-cli/profile-command (dev-master)
Updating /root/.wp-cli/packages/composer.json to require the package...
Using Composer to install the package...
---
Loading composer repositories with package information
Updating dependencies
Killed

This creates a 1 GB swap file. You will need root access to create a swap file.

swapoff -a
dd if=/dev/zero of=/swapfile bs=1M count=1024

Will see this output

1024+0 records in
1024+0 records out
1073741824 bytes (1.1 GB, 1.0 GiB) copied, 2.16717 s, 495 MB/s

Change the permissions of the swap file and turn it into swap

chmod 600 /swapfile
mkswap /swapfile
swapon /swapfile

Then re-run the wp profile installation command

wp package install wp-cli/profile-command

Now you should see the successful output

Updating /root/.wp-cli/packages/composer.json to require the package...
Using Composer to install the package...
---
Loading composer repositories with package information
Updating dependencies
Resolving dependencies through SAT
Dependency resolution completed in 0.120 seconds
Analyzed 3696 packages to resolve dependencies
Analyzed 101022 rules to resolve dependencies
Package operations: 1 install, 0 updates, 0 removals
Installs: wp-cli/profile-command:dev-master ef44df5
 - Installing wp-cli/profile-command (dev-master ef44df5)
Writing lock file
Generating autoload files
---
Success: Package installed.

Upgrade wp profile

You can upgrade to the latest profile package version with this command

wp package update --allow-root

Using wp profile Command

Enter WP directory (or use --path)

wp profile stage command

This command shows the stages of loading WordPress.

wp profile stage --allow-root

You will get this very thorough table. Your main focus should generally be the time column.

In general you want your cache_ratio to be high like greater than 70%

Generally you want cache_hits to be greater than cache_misses

Your query_time should be low as well

+------------+---------+------------+-------------+-------------+------------+--------------+-----------+------------+--------------+---------------+
| stage      | time    | query_time | query_count | cache_ratio | cache_hits | cache_misses | hook_time | hook_count | request_time | request_count |
+------------+---------+------------+-------------+-------------+------------+--------------+-----------+------------+--------------+---------------+
| bootstrap  | 0.8193s | 0.004s     | 28          | 92.7%       | 559        | 44           | 0.1279s   | 2972       | 0s           | 0             |
| main_query | 0.0136s | 0.0039s    | 7           | 87.25%      | 130        | 19           | 0.0042s   | 219        | 0s           | 0             |
| template   | 0.2741s | 0.011s     | 30          | 96.94%      | 2442       | 77           | 0.2334s   | 7030       | 0s           | 0             |
+------------+---------+------------+-------------+-------------+------------+--------------+-----------+------------+--------------+---------------+
| total (3)  | 1.107s  | 0.0189s    | 65          | 92.3%       | 3131       | 140          | 0.3654s   | 10221      | 0s           | 0             |
+------------+---------+------------+-------------+-------------+------------+--------------+-----------+------------+--------------+---------------+

Can drill down into each stage, here we drill down into the bootstrap stage

wp profile stage bootstrap --allow-root

You will then see the hooks used by that stage

+--------------------------+----------------+---------+------------+-------------+-------------+------------+--------------+--------------+---------------+
| hook                     | callback_count | time    | query_time | query_count | cache_ratio | cache_hits | cache_misses | request_time | request_count |
+--------------------------+----------------+---------+------------+-------------+-------------+------------+--------------+--------------+---------------+
| muplugins_loaded:before  |                | 0.3076s | 0.0015s    | 4           | 76.47%      | 39         | 12           | 0s           | 0             |
| muplugins_loaded         | 2              | 0.0002s | 0s         | 0           | 100%        | 2          | 0            | 0s           | 0             |
| plugins_loaded:before    |                | 0.1897s | 0.003s     | 12          | 93.28%      | 222        | 16           | 0s           | 0             |
| plugins_loaded           | 33             | 0.0354s | 0s         | 0           | 100%        | 44         | 0            | 0s           | 0             |
| setup_theme:before       |                | 0.0003s | 0s         | 0           | 100%        | 4          | 0            | 0s           | 0             |
| setup_theme              | 1              | 0s      | 0s         | 0           |             | 0          | 0            | 0s           | 0             |
| after_setup_theme:before |                | 0.0149s | 0s         | 0           | 100%        | 40         | 0            | 0s           | 0             |
| after_setup_theme        | 20             | 0.0068s | 0s         | 0           | 100%        | 20         | 0            | 0s           | 0             |
| init:before              |                | 0s      | 0s         | 0           |             | 0          | 0            | 0s           | 0             |
| init                     | 55             | 0.0261s | 0.0009s    | 12          | 92.08%      | 186        | 16           | 0s           | 0             |
| wp_loaded:before         |                | 0.0001s | 0s         | 0           | 100%        | 2          | 0            | 0s           | 0             |
| wp_loaded                | 8              | 0.0001s | 0s         | 0           |             | 0          | 0            | 0s           | 0             |
| wp_loaded:after          |                | 0.0265s | 0s         | 0           |             | 0          | 0            | 0s           | 0             |
+--------------------------+----------------+---------+------------+-------------+-------------+------------+--------------+--------------+---------------+
| total (13)               | 119            | 0.6078s | 0.0054s    | 28          | 95.76%      | 559        | 44           | 0s           | 0             |
+--------------------------+----------------+---------+------------+-------------+-------------+------------+--------------+--------------+---------------+

I usually prefer to use the –-all flag for the stage command

wp profile stage --all --allow-root

This will show all of the hooks used by each stage

+--------------------------+----------------+---------+------------+-------------+-------------+------------+--------------+--------------+---------------+
| hook                     | callback_count | time    | query_time | query_count | cache_ratio | cache_hits | cache_misses | request_time | request_count |
+--------------------------+----------------+---------+------------+-------------+-------------+------------+--------------+--------------+---------------+
| muplugins_loaded:before  |                | 0.178s  | 0.0009s    | 1           | 25%         | 1          | 3            | 0s           | 0             |
| muplugins_loaded         | 2              | 0.0003s | 0s         | 0           | 50%         | 1          | 1            | 0s           | 0             |
| plugins_loaded:before    |                | 0.2855s | 0.0041s    | 19          | 83.95%      | 136        | 26           | 0s           | 0             |
| plugins_loaded           | 34             | 0.2442s | 0.0009s    | 3           | 98.31%      | 116        | 2            | 0s           | 0             |
| setup_theme:before       |                | 0.0005s | 0s         | 0           | 100%        | 4          | 0            | 0s           | 0             |
| setup_theme              | 1              | 0s      | 0s         | 0           |             | 0          | 0            | 0s           | 0             |
| after_setup_theme:before |                | 0.2653s | 0.0011s    | 3           | 99.68%      | 615        | 2            | 0s           | 0             |
| after_setup_theme        | 17             | 0.0241s | 0.0002s    | 1           | 98.8%       | 82         | 1            | 0s           | 0             |
| init:before              |                | 0s      | 0s         | 0           |             | 0          | 0            | 0s           | 0             |
| init                     | 82             | 0.261s  | 0.0016s    | 9           | 98.74%      | 703        | 9            | 0s           | 0             |
| wp_loaded:before         |                | 0s      | 0s         | 0           |             | 0          | 0            | 0s           | 0             |
| wp_loaded                | 7              | 0.0012s | 0s         | 0           | 100%        | 6          | 0            | 0s           | 0             |
| parse_request:before     |                | 0.0192s | 0s         | 0           | 100%        | 14         | 0            | 0s           | 0             |
| parse_request            | 2              | 0s      | 0s         | 0           |             | 0          | 0            | 0s           | 0             |
| send_headers:before      |                | 0.0001s | 0s         | 0           | 100%        | 4          | 0            | 0s           | 0             |
| send_headers             | 0              | 0s      | 0s         | 0           |             | 0          | 0            | 0s           | 0             |
| pre_get_posts:before     |                | 0.0003s | 0s         | 0           | 100%        | 10         | 0            | 0s           | 0             |
| pre_get_posts            | 8              | 0s      | 0s         | 0           |             | 0          | 0            | 0s           | 0             |
| the_posts:before         |                | 0s      | 0s         | 0           |             | 0          | 0            | 0s           | 0             |
| the_posts                | 2              | 0s      | 0s         | 0           |             | 0          | 0            | 0s           | 0             |
| wp:before                |                | 0s      | 0s         | 0           |             | 0          | 0            | 0s           | 0             |
| wp                       | 10             | 0.068s  | 0.0148s    | 30          | 97.3%       | 756        | 21           | 0s           | 0             |
| template_redirect:before |                | 0.0005s | 0s         | 0           |             | 0          | 0            | 0s           | 0             |
| template_redirect        | 20             | 0.0098s | 0.0005s    | 1           | 99.06%      | 105        | 1            | 0s           | 0             |
| template_include:before  |                | 0.0004s | 0s         | 0           | 100%        | 9          | 0            | 0s           | 0             |
| template_include         | 1              | 0s      | 0s         | 0           |             | 0          | 0            | 0s           | 0             |
| wp_head:before           |                | 0.0028s | 0.0003s    | 2           | 85.71%      | 18         | 3            | 0s           | 0             |
| wp_head                  | 42             | 0.0887s | 0.004s     | 9           | 97.85%      | 682        | 15           | 0s           | 0             |
| loop_start:before        |                | 0s      | 0s         | 0           |             | 0          | 0            | 0s           | 0             |
| wp_footer                | 14             | 0.0068s | 0s         | 0           | 100%        | 61         | 0            | 0s           | 0             |
| wp_footer:after          |                | 0.0002s | 0s         | 0           |             | 0          | 0            | 0s           | 0             |
+--------------------------+----------------+---------+------------+-------------+-------------+------------+--------------+--------------+---------------+
| total (31)               | 242            | 1.4571s | 0.0284s    | 78          | 90.8%       | 3323       | 84           | 0s           | 0             |
+--------------------------+----------------+---------+------------+-------------+-------------+------------+--------------+--------------+---------------+

You can also use the --spotlight flag to filter out zero-like values for easier reading

wp profile stage --all --spotlight --allow-root

Much cleaner now 🙂

+--------------------------+----------------+---------+------------+-------------+-------------+------------+--------------+--------------+---------------+
| hook                     | callback_count | time    | query_time | query_count | cache_ratio | cache_hits | cache_misses | request_time | request_count |
+--------------------------+----------------+---------+------------+-------------+-------------+------------+--------------+--------------+---------------+
| muplugins_loaded:before  |                | 0.2969s | 0.0016s    | 1           | 25%         | 1          | 3            | 0s           | 0             |
| muplugins_loaded         | 2              | 0.0002s | 0s         | 0           | 50%         | 1          | 1            | 0s           | 0             |
| plugins_loaded:before    |                | 0.2374s | 0.0021s    | 19          | 83.95%      | 136        | 26           | 0s           | 0             |
| plugins_loaded           | 34             | 0.2094s | 0.0004s    | 3           | 98.31%      | 116        | 2            | 0s           | 0             |
| after_setup_theme:before |                | 0.1991s | 0.0006s    | 3           | 99.68%      | 615        | 2            | 0s           | 0             |
| after_setup_theme        | 17             | 0.0299s | 0.0002s    | 1           | 98.8%       | 82         | 1            | 0s           | 0             |
| init                     | 82             | 0.3435s | 0.001s     | 9           | 98.74%      | 703        | 9            | 0s           | 0             |
| parse_request:before     |                | 0.0274s | 0s         | 0           | 100%        | 14         | 0            | 0s           | 0             |
| wp                       | 10             | 0.0747s | 0.0078s    | 30          | 97.3%       | 756        | 21           | 0s           | 0             |
| template_redirect        | 20             | 0.0102s | 0.0002s    | 1           | 99.06%      | 105        | 1            | 0s           | 0             |
| wp_head:before           |                | 0.0039s | 0.0004s    | 2           | 85.71%      | 18         | 3            | 0s           | 0             |
| wp_head                  | 42             | 0.0884s | 0.0021s    | 9           | 97.85%      | 682        | 15           | 0s           | 0             |
+--------------------------+----------------+---------+------------+-------------+-------------+------------+--------------+--------------+---------------+
| total (12)               | 207            | 1.5211s | 0.0163s    | 78          | 86.2%       | 3229       | 84           | 0s           | 0             |
+--------------------------+----------------+---------+------------+-------------+-------------+------------+--------------+--------------+---------------+

You can use the hook command to drill down into specific hooks.

wp profile hook Command

Here we dig into the wp hook

wp profile hook wp --allow-root

Here are all the functions being called in the wp hook for this particular site running Avada

+----------------------------------+------------------------------------+---------+------------+-------------+-------------+------------+--------------+--------------+---------------+
| callback                         | location                           | time    | query_time | query_count | cache_ratio | cache_hits | cache_misses | request_time | request_count |
+----------------------------------+------------------------------------+---------+------------+-------------+-------------+------------+--------------+--------------+---------------+
| a3_lazy_load_instance()          | a3-lazy-load/classes/class-a3-lazy | 0.0002s | 0s         | 0           |             | 0          | 0            | 0s           | 0             |
|                                  | -load.php:540                      |         |            |             |             |            |              |              |               |
| tve_leads_query_group()          | thrive-leads/inc/hooks.php:185     | 0.0614s | 0.021s     | 30          | 93.1%       | 216        | 16           | 0s           | 0             |
| tve_leads_one_click_signup()     | thrive-leads/inc/hooks.php:2614    | 0s      | 0s         | 0           |             | 0          | 0            | 0s           | 0             |
| tve_wp_action()                  | thrive-visual-editor/plugin-core.p | 0.0006s | 0s         | 0           | 100%        | 2          | 0            | 0s           | 0             |
|                                  | hp:204                             |         |            |             |             |            |              |              |               |
| TCB_Editor->clean_inner_frame()  | thrive-visual-editor/inc/classes/c | 0s      | 0s         | 0           |             | 0          | 0            | 0s           | 0             |
|                                  | lass-tcb-editor.php:223            |         |            |             |             |            |              |              |               |
| Avada_Init->set_theme_version()  | Avada/includes/class-avada-init.ph | 0.0017s | 0s         | 0           | 64.29%      | 9          | 5            | 0s           | 0             |
|                                  | p:101                              |         |            |             |             |            |              |              |               |
| Avada_Dynamic_CSS->set_mode()    | Avada/includes/class-avada-dynamic | 0.0003s | 0s         | 0           | 100%        | 4          | 0            | 0s           | 0             |
|                                  | -css.php:62                        |         |            |             |             |            |              |              |               |
| Avada->set_page_id()             | Avada/includes/class-avada.php:239 | 0.0004s | 0s         | 0           | 100%        | 8          | 0            | 0s           | 0             |
| Avada_Layout->add_sidebar()      | Avada/includes/class-avada-layout. | 0.0251s | 0s         | 0           | 100%        | 516        | 0            | 0s           | 0             |
|                                  | php:20                             |         |            |             |             |            |              |              |               |
| WPSEO_Frontend->page_redirect()  | wordpress-seo/frontend/class-front | 0s      | 0s         | 0           | 100%        | 1          | 0            | 0s           | 0             |
|                                  | end.php:1377                       |         |            |             |             |            |              |              |               |
+----------------------------------+------------------------------------+---------+------------+-------------+-------------+------------+--------------+--------------+---------------+
| total (10)                       |                                    | 0.0898s | 0.021s     | 30          | 93.91%      | 756        | 21           | 0s           | 0             |
+----------------------------------+------------------------------------+---------+------------+-------------+-------------+------------+--------------+--------------+---------------+

Similar to the wp profile stage command, you can use the --all flag for hooks as well.

wp profile hook --all --spotlight

You can even simulate the loading for a specific URL on the site by using the --url flag.

wp profile hook --all --spotlight --url=https://guides.wp-bullet.com --allow-root

Looks like only my theme GeneratePress, Yoast and WP Theme Optimizer are being loaded.

+----------------------------------------------+-----------------------------------------------------+---------+------------+-------------+-------------+------------+--------------+--------------+---------------+
| callback                                     | location                                            | time    | query_time | query_count | cache_ratio | cache_hits | cache_misses | request_time | request_count |
+----------------------------------------------+-----------------------------------------------------+---------+------------+-------------+-------------+------------+--------------+--------------+---------------+
| wpseo_init()                                 | wordpress-seo/wp-seo-main.php:252                   | 0.0136s | 0s         | 0           | 100%        | 34         | 0            | 0s           | 0             |
| wpto_Public->wpto_remove_yoast_information() | wp-theme-optimizer/public/class-wpto-public.php:187 | 0.0151s | 0s         | 0           | 100%        | 18         | 0            | 0s           | 0             |
| wp_enqueue_scripts()                         | wp-includes/script-loader.php:1294                  | 0.015s  | 0s         | 0           | 100%        | 348        | 0            | 0s           | 0             |
| generate_construct_header()                  | generatepress/inc/template-tags.php:514             | 0.0041s | 0.0002s    | 2           | 100%        | 93         | 0            | 0s           | 0             |
| generate_add_navigation_after_header()       | generatepress/inc/navigation.php:23                 | 0.0132s | 0.0003s    | 3           | 100%        | 367        | 0            | 0s           | 0             |
| wp_trim_excerpt()                            | wp-includes/formatting.php:3289                     | 0.0186s | 0s         | 0           | 100%        | 16         | 0            | 0s           | 0             |
| generate_construct_sidebars()                | generatepress/functions.php:434                     | 0.0129s | 0.0001s    | 1           | 100%        | 178        | 0            | 0s           | 0             |
+----------------------------------------------+-----------------------------------------------------+---------+------------+-------------+-------------+------------+--------------+--------------+---------------+
| total (7)                                    |                                                     | 0.0927s | 0.0006s    | 6           | 100%        | 1054       | 0            | 0s           | 0             |
+----------------------------------------------+-----------------------------------------------------+---------+------------+-------------+-------------+------------+--------------+--------------+---------------+

Let’s compare to my contact page

wp profile hook --all --spotlight --url=https://guides.wp-bullet.com/contact/ --allow-root

Now we can see Pretty Link is running, TablePress and Contact Form 7

+----------------------------------------+-------------------------------------------------------+---------+------------+-------------+-------------+------------+--------------+--------------+---------------+
| callback                               | location                                              | time    | query_time | query_count | cache_ratio | cache_hits | cache_misses | request_time | request_count |
+----------------------------------------+-------------------------------------------------------+---------+------------+-------------+-------------+------------+--------------+--------------+---------------+
| PrliAppController->redirect()          | pretty-link/app/controllers/PrliAppController.php:295 | 0.0012s | 0.0003s    | 1           | 100%        | 2          | 0            | 0s           | 0             |
| wpcf7()                                | contact-form-7/settings.php:88                        | 0.012s  | 0s         | 0           |             | 0          | 0            | 0s           | 0             |
| wpseo_init()                           | wordpress-seo/wp-seo-main.php:252                     | 0.023s  | 0s         | 0           | 100%        | 34         | 0            | 0s           | 0             |
| PrliAppController->install()           | pretty-link/app/controllers/PrliAppController.php:253 | 0.014s  | 0s         | 0           | 100%        | 2          | 0            | 0s           | 0             |
| TablePress::run()                      | tablepress/classes/class-tablepress.php:101           | 0.0107s | 0s         | 0           | 100%        | 4          | 0            | 0s           | 0             |
| wp_enqueue_scripts()                   | wp-includes/script-loader.php:1294                    | 0.0247s | 0.0002s    | 1           | 100%        | 369        | 0            | 0s           | 0             |
| generate_construct_header()            | generatepress/inc/template-tags.php:514               | 0.0067s | 0.0003s    | 2           | 100%        | 97         | 0            | 0s           | 0             |
| generate_add_navigation_after_header() | generatepress/inc/navigation.php:23                   | 0.0219s | 0.0004s    | 3           | 100%        | 370        | 0            | 0s           | 0             |
| generate_construct_sidebars()          | generatepress/functions.php:434                       | 0.0167s | 0.0002s    | 1           | 100%        | 178        | 0            | 0s           | 0             |
+----------------------------------------+-------------------------------------------------------+---------+------------+-------------+-------------+------------+--------------+--------------+---------------+
| total (9)                              |                                                       | 0.1309s | 0.0014s    | 8           | 100%        | 1056       | 0            | 0s           | 0             |
+----------------------------------------+-------------------------------------------------------+---------+------------+-------------+-------------+------------+--------------+--------------+---------------+

Now you can diagnose issues by running the wp profile commands before and after deactivating plugins

wp plugin deactivate plugin-name

Compare the wp profile results before and after, happy WordPress performance troubleshooting :).

If you need help diagnosing the performance issues of your site get in touch with me via Codeable.

Sources

WP-CLI Profile Command – Github
WP-CLI Package Update – WP.org
WP Profile Command – EasyEngine
Identify WordPress Slowness – Daniel Bachhuber