Profiling PHP performance with XHProf

In software engineering, profiling is the process of analyzing performance attributes of an application during runtime. A profiler is a tool that monitors a program and captures information like how often functions and methods are called, how long each takes to execute, how much memory is consumed during various processes, etc.

Introduction to XHProf and XHGui

XHProf is a hierarchical profiler for PHPPHP PHP (recursive acronym for PHP: Hypertext Preprocessor) is a widely-used open source general-purpose scripting language that is especially suited for web development and can be embedded into HTML. http://php.net/manual/en/intro-whatis.php., designed to be lightweight so it could be used to collect performance data in production environments. This design makes it well suited for measuring the performance of an application with minimal observability costs, i.e., side effects that skew the performance metrics it collects. XHProf was originally released by Facebook in 2009. It has been forked several times since, in order to add support for PHP 7+ and PHP 8+. The current version of XHProf included in the PECL repository is located at https://github.com/longxinH/xhprof and is what we recommend throughout the rest of this tutorial.

XHGui is a graphical user interface for displaying and interpreting profiling data collected by an XHProf compatible profiler. It stores profiling data in either a MongoDB or PDO database. The graphical user interface allows you to compare the memory and time consumption of separate profiling runs or review an individual run to explore performance statistics about each function run during an individual request, including the following:

  • Call frequency – the number of times a function was called during execution
  • Self wall time – the amount of time spent executing the function, excluding calls to child functions.
  • Inclusive wall time – the amount of time spent executing the function including child functions
  • Self memory usage – the total memory consumption of the function excluding child functions
  • Inclusive memory usage – the total memory consumption of the function including child functions
The screen for viewing data from a specific request in XHGui

Top ↑

Installation instructions

The following sections describe common  instructions for setting up a working WordPress environment with XHProf and XHGui.

Top ↑

Using the WordPress Development docker environment

Configuring a WordPress development environment with XHProf is particularly useful when you want to profile changes to WordPress coreCore Core is the set of software required to run WordPress. The Core Development Team builds WordPress. in a specific patch or PR. In order to make it easier to set up XHProf and XHGui in the docker environment that is included in the WordPress development repo, the performance team maintains a set of instructions with drop in files that you can use to set up and configure the WordPress development environment for profiling WordPress core. Once you have an environment already installed and set up, you can follow these instructions to add XHProf and XHGUI:

  1. Copy the docker-compose.override.yml and xhprof.php  files to the root of your wordpress-develop directory
  2. Include the xhprof.php file in your wp-config.php file before WordPress is loaded. Ex:
/** Set up XHProf. */
require_once __DIR__ . '/xhprof.php';

/** Sets up WordPress vars and included files. */
require_once ABSPATH . 'wp-settings.php';
  1. Start environment, using npm run env:start
  2. Visit to http://localhost:8142/ to see XHGui

Top ↑

Using the wp-env environment

If you want to profile specific versions of WordPress or profile a theme, pluginPlugin A plugin is a piece of software containing a group of functions that can be added to a WordPress website. They can extend functionality or add new features to your WordPress websites. WordPress plugins are written in the PHP programming language and integrate seamlessly with WordPress. These can be free in the WordPress.org Plugin Directory https://wordpress.org/plugins/ or can be cost-based plugin from a third-party, or entire project using the @wordpress/env (i.e. wp-env) development environment, the WordPress Performance team has an experimental PR for the GutenbergGutenberg The Gutenberg project is the new Editor Interface for WordPress. The editor improves the process and experience of creating new content, making writing rich content much simpler. It uses ‘blocks’ to add richness rather than shortcodes, custom HTML etc. https://wordpress.org/gutenberg/ repo (where wp-env is developed) available that allows wp-env to be started with XHProf automatically configured by running wp env start --xhprof. Follow these instructions to setup and configure XHProf and XHGui in this environment.

Top ↑

1 – Installing wp-env with XHProf support

Install a copy of the wp-env package including XHProf support. The following is an example of setting up a fork that we’ve modified to support our XHProf configuration, but this process may become unnecessary if XHProf support is added officially to the wp-env package (see relevant PR).

# Clone the repo.
git clone git@github.com:joemcgill/gutenberg.git
cd gutenberg
git checkout experiment/add-xhprof-support

# Navigate to the package directory, install dependencies and symlink the package.
cd packages/env
npm install && npm link

Top ↑

2 – Installing XHGui

Follow the Install from Docker instructions to download and set up the XHGui application for reading profiler data.

Top ↑

3 – Starting up a custom environment for profiling.

To make profiling easier, we’ll use a mu-plugin that implements the php-profiler library as an interface between XHProf and XHGui. This plugin includes a custom .wp-env.json configuration as an example of how to automatically map the plugin to your mu-plugins directory.

# Clone the plugin repo and install dependencies.
git clone git@github.com:joemcgill/wp-profiler.git
cd wp-profiler
composer install

# Start the environment with XHProf support.
wp-env start --xhprof

At this point you should have a local WordPress environment running at http://localhost:8888/ and XHGui running at http://localhost:8142/.

Top ↑

Manual installation instructions

For other environments, you’ll need to manually install and configure XHProf and XHGui. To do so, see the following instructions:

Top ↑

Installing and configuring XHProf

The GithubGitHub GitHub is a website that offers online implementation of git repositories that can easily be shared, copied and modified by other developers. Public repositories are free to host, private repositories require a paid subscription. GitHub introduced the concept of the ‘pull request’ where code changes done in branches by contributors can be reviewed and discussed before being merged be the repository owner. https://github.com/ repo describes installing XHProf from source. You can also install the module from the PECL library in compatible environments by running pecl install xhprof and then following the configuration instructions from the repo.

Top ↑

Installing XHGui

  1. Installing with docker (recommended)
  2. Installing from source

Top ↑

Integrating XHProf with WordPress

The easiest way to integrate XHProf with a WordPress installation is to use the perftools/php-profiler library in your project and follow the instructions to create a custom profiler and include that in your WordPress installation by requiring it in your wp-config.php file or via a custom mu-plugin (example). This library will automatically start the XHProfiler and send collected data to the database for XHGui installed previously. For more advanced configuration options, see this documentation.

Top ↑

Capturing profiling data

Once you have XHProf installed and configured for your testing environment, you are ready to begin collecting profiling data for any request to the WordPress application. 

The most basic way to do this is to visit the URLURL A specific web address of a website or web page on the Internet, such as a website’s URL www.wordpress.org for the request you want to profile in your web browser (e.g., the homepage, an admin page, a REST APIREST API The REST API is an acronym for the RESTful Application Program Interface (API) that uses HTTP requests to GET, PUT, POST and DELETE data. It is how the front end of an application (think “phone app” or “website”) can communicate with the data store (think “database” or “file system”) https://developer.wordpress.org/rest-api/. request, etc.). XHProf will collect performance statistics for that request as it is being executed. To ensure you are getting accurate results, it is best to measure the same request multiple times to eliminate variance that can occur on a single request. This can be done by reloading the same page in your browser multiple times, or by using a CLICLI Command Line Interface. Terminal (Bash) in Mac, Command Prompt in Windows, or WP-CLI for WordPress. tool that generates multiple requests at a time. To learn more, read the best practices for performance measurement.

Top ↑

Reviewing profiling data

Once you’ve collected profiling data, you can use XHGui to explore the data and make observations about how different parts of the application are performing. If you followed the instructions above, XHGui should be running at http://localhost:8142/.

When you first open XHGui, you should see a list of the most recent profiling runs, starting with the most recent one. From this page, you can click on a specific URL to see only runs collected at that location, or order the list of runs by a performance metric like wall time or memory usage.

A screenshot of the XHGui page showing a list of recent XHProf runs

When you click the time of one of the runs, you will see a detailed view of all the data collected during that specific run. This view conveniently includes the list of top 5 functions in terms of most time spent running and most memory used. You will also see a sortable table including data for every function called during the execution of that run.

A screenshot of the XHGui page showing a detailed view of a single XHProf run

Clicking on the name of any function in the list will give you even more information about that specific function, including what parent functions were responsible for calling that function, and what function were called by the function itself.

A screenshot of the XHGui page showing function details collected during a single XHProf run

Top ↑

Additional tips and features

Top ↑

Making multiple requests via CLI

Using a CLI like apacheApache Apache is the most widely used web server software. Developed and maintained by Apache Software Foundation. Apache is an Open Source software available for free. bench (ab) can make taking multiple requests at once much easier. For example, the make 20 requests to the homepage, you could run this:

ab -n 20 http://localhost:8889/

Top ↑

Using query parameters to identify groups of requests

To make filtering multiple related runs easier, consider adding specific query parameters to the end of the URL that you are testing. For example, you could add the ticket number for associated with the code you want to profile before it is committed:

ab -n 20 http://localhost:8889/\?ticket\=50000

With custom URL params in place, you can then filterFilter Filters are one of the two types of Hooks https://codex.wordpress.org/Plugin_API/Hooks. They provide a way for functions to modify data of other functions. They are the counterpart to Actions. Unlike Actions, filters are meant to work in an isolated manner, and should never have side effects such as affecting global variables and output. the list of requests in XHGui to only URLs that match that pattern by clicking on the URL in the main list of runs.

Top ↑

Ignore PHP built in functions

By default, XHProf will collect data about all functions being called during an application run, including built in PHP functions. However, sometimes it’s beneficial to filter these out so you only review the functions that are defined by your application. 

To do so, you can pass the ProfilingFlags::NO_BUILTINS flag if you are using the PHP Profiler library to connect XHProf with XHGui. Ex:

$profiler = new Profiler(
	array(
	'save.handler.upload' => array(
		'url' => /* path to XHGui's importer */
	),
	'profiler.flags'      => array(
			ProfilingFlags::CPU,
			ProfilingFlags::MEMORY,
			ProfilingFlags::NO_BUILTINS,
		),
	)
);

Props @joemcgill @spacedmonkey @flixos90 for contributing to this article.

Last updated: