Powered By Blogger

lunes, 21 de mayo de 2012

Advanced CodeIgniter Profiling With XHProf

There are a number of ways to profile and monitor the performance of PHP code in a development environment, but once it’s been let loose on a production server its extremely difficult to know what’s going on when your app is getting accessed by your users.

In March 2009, Facebook released XHProf, a function level profiler that allows you to identify bottlenecks in your application. And it runs on a production server with little overhead.

XHProf will provide you with a hierarchical profile of the functions your application calls, allowing you to follow the chain of events that lead up to resource intensive code running. You’ll be able to see exactly how much wall time (the actual time elapsed), CPU time and memory your application uses.

This tutorial will help you get XHProf installed on your server, and integrated with your CodeIgniter application via hooks so that you can start profiling your application, in a production environment, straight away. Everything you see here was written for Ubuntu 10.04 – the latest LTS release at the time of writing.


Installation

Installation of XHProf can be done via PECL – that said, I’ve never been able to get the PECL version installed on Ubuntu easily so its easier to install from source. Start off by downloading the latest revision from the XHProf GitHub account.

          wget https://github.com/facebook/xhprof/tarball/master -O xhprof.tar.gz  

Extract the tarball and switch into the extracted folder – this will change depending on the latest revision available from GitHub.

          tar -xvf xhprof.tar.gz          cd facebook-xhprof-bc8ef04/extension  

Then install as normal.

          phpize          ./configure          make          sudo make install  

Finally, we need to tell PHP to load the extension. Create a config file for it – I do this for cleanliness but you can always drop it at the end of your main php.ini.

          sudo nano /etc/php5/conf.d/xhprof.ini  

And paste in the following:

          extension=xhprof.so          xhprof.output_dir="/var/tmp/xhprof"  

This tells XHProf to use the directory at /var/tmp/xhprof to save its run data.

You can check whether XHProf is installed correctly by entering php -m at the command line and checking that the module is available. Remember to restart Apache so that it gets picked up in your PHP web applications as well.

If you want to render the callgraph images you’ll also need the graphviz package. This can be obtained from apt.

          sudo apt-get install graphviz  

Integration With CodeIgniter

XHProf can be used on an ad-hoc basis to evaluate small pieces of code, but it’s at its most useful when you let it profile the full page. First of all, you’ll need to move the XHProf code into your web applications root so that it has access to the appropriate classes.

          sudo mv ~/facebook-xhprof-bc8ef04  /var/www/xhprof  

CodeIgniter has an excellent mechanism for injecting custom code into the execution of a page called ‘Hooks’. This is what we’ll use to integrate XHProf with your application. Enable hooks in your application/config/config.php file.

          $config['enable_hooks'] = true;  

Then specify your hooks in application/config/hooks.php

          $hook['pre_controller'] = array(            'class'  => 'XHProf',            'function' => 'XHProf_Start',            'filename' => 'xhprof.php',            'filepath' => 'hooks',            'params' => array()          );            $hook['post_controller'] = array(                  'class'  => 'XHProf',                  'function' => 'XHProf_End',                  'filename' => 'xhprof.php',                  'filepath' => 'hooks',                  'params' => array()          );  

Then create the hook that will load XHProf into your application at application/hooks/xhprof.php. This class will provide the necessary minimum to get XHProf collecting data from your application.

          class XHProf {                    private $XHProfPath = 'xhprof/';                  private $applicationName = 'my_application';                  private $sampleSize = 1;                  private static $enabled = false;                    public function XHProf_Start() {                          if (mt_rand(1, $this->sampleSize) == 1) {                                  include_once $this->XHProfPath . 'xhprof_lib/utils/xhprof_lib.php';                                  include_once $this->XHProfPath . 'xhprof_lib/utils/xhprof_runs.php';                                  xhprof_enable(XHPROF_FLAGS_NO_BUILTINS);                                    self::$enabled = true;                          }                  }                    public function XHProf_End() {                          if (self::$enabled) {                                  $XHProfData = xhprof_disable();                                    $XHProfRuns = new XHProfRuns_Default();                                  $XHProfRuns->save_run($XHProfData, $this->applicationName);                          }                  }            }  

There’s a few thing to point out in this code sample.

  • The $XHProfPath variable should point to the directory you installed XHProf in. In our example, we put it in the root of the web application, but you might be storing it somewhere central and symlinking it to multiple applications.
  • The $applicationName variable lets you specify the name of the application using XHProf. This could be especially important in an environment where you’re running multiple applications on the same server.
  • The $sampleSize variable lets you specify a factor for how often XHProf profiles a run. In a production environment where you’re receiving thousands of hits, it’s probably not worth storing every single run. You can increase this variable to get a random sample of runs. Changing it to 10, for example, will give you a report from one in every 10 visits to your application.

The xhprof_enable() function can accept any combination of 3 constants.

  • XHPROF_FLAGS_NO_BUILTINS – skip all internal php functions. Adding this means you’ll only see time spent in functions that you have written (or are part of CodeIgniter)
  • XHPROF_FLAGS_CPU – add additional CPU profiling information
  • XHPROF_FLAGS_MEMORY – add additional memory profiling information

Combine them using +. eg. xhprof_enable(XHPROF_FLAGS_NO_BUILTINS + XHPROF_FLAGS_MEMORY);

After running through your application a couple of times, point your browser at the XHProf application directory http://localhost/xhprof/xhprof_html/index.php – adjusting for the address of your development server – and you’ll see a list of your last application runs with the newest at the top. Select one of the runs to see it’s profiling information.


Evaluating the Results

Storing all this profiling information is all very well and good but knowing how to evaluate it is the key to optimising your application.

The ‘Overall Summary’ is a good place to start – it’ll show you:

  • Total inclusive wall time – how long (in microseconds) it took for you page to be generated
  • Total memory use – the total memory used by this run of your application
  • Total peak memory use – the maximum amount of memory that was used by your application
  • Number of function calls – the number of functions that were called by your application
Run summary

These number should give you an overall base to start from and provide headline numbers to compare against when you start optimisation.

Below the summary is the detailed run report The bottlenecks in your application should be fairly obvious – they’re the function calls at the top of the report.

Wall report

The most resource intensive things you’re likely to come across will be those that make calls to external services or your database. By default, the results are sorted by ‘inclusive wall time’. This shows the functions that took the longest to run by the cumulative total of them and the functions they call. Ordering by ‘exclusive wall time’ allows you to see the specific functions that are using the most amount of resource.

Clicking a function allows you to drill down into more detail about that function and the functions it called. From there, it’ll be easier to see exactly what your long running function was doing and spot any issues.

When debugging your CodeIgniter application, one of the best places to start is the controller being called for your page – Welcome::index in the example. From there you can easily see the breakdown of where the controller spent most of it’s time.

Controller summary

Callgraph

XHProf can generate a call graph view that provides a visual clue to the main path of your application. The main bottlenecks are highlighted in red to show where most your resources are being spent.

Callgraph

Comparing Runs

XHProf provides a mechanism for comparing multiple runs – this will allow you to make a number of optimisations and then see how they’ve affected your application.

http://localhost/xhprof/xhprof_html/index.php?run1=run_id_1>&run2=run_id_2&source=applicationName

Note: run_id_1 and run_id_2 should be the ids from some of your previous runs and applicationName should be the name you specified when setting up the hook earlier.

This will provide the same information you see in a regular report but provide percentage statistics on any changes since the first run, allowing you to easily see if your optimisations are moving your performance in the right direction.

Aggregating Runs

Sometimes a single run might not be enough to evaluate exactly what’s going on. Pages my work slightly differently depending on the query string or user input or there might be differences in caching mechanisms.

Aggregating a number of runs allows you to combine a number of runs and receive an average of the resources consumed.

http://localhost/xhprof/xhprof_html/index.php?run=run_id,run_id,run_id&source=applicationName

Note: run_id should be the ids from some of your previous runs and applicationName should be the name you specified when setting up the hook earlier.


Summary

You should now have XHProf up and running in your CodeIgniter application and be able to see exactly where your code is spending most of it’s time. Optimisation can be a difficult task. Sometimes it’s not as simple as reimplementing a function, or adding a layer of caching. Thinking about why you’re doing something and what the exact effect that is having on your application is just as important.

So let me know if you have any questions in the comments below. Thank you so much for reading!



No hay comentarios: