3 simple steps to boost performance of a PHP web application

Posted by on May 23, 2013 in PHP Programming | 6 Comments

Developers care about performance of their software. The problem is that they don’t usually have enough time for a proper optimisation. I would like to propose 3 relatively simple steps which won’t take much time and will improve a response time by at least 60%.

If you can not measure it, you can not improve it.

There are many great tools you can use for benchmarking and finding bottle necks but the tools are not everything. If you want your results to be representative you need to follow the best practice. Doing a good science is a big subject far behind scope of this post. I can recommend four simple points which used to work for me.

  • make sure there is no external traffic on a tested host
  • always run benchmarks from a different host
  • test form local network if possible
  • monitor load (top, htop or dmesg will do) during a test

Apache Benchmark

This is my favourite tool. It’s very easy to use and you can find it everywhere. I usually go for a concurrency set between 20 and 100 and a minimum 500 requests. For example:

Look at average requests per second. Higher is better.

Apache jMeter

Very rubost testing tool. If you want to know more about it read my previous post.

XDebug

Well known PHP extension for debugging and profiling. Profiling takes lots of resources so it’s better to use it on demand (trigger with XDEBUG_PROFILE=1 attached to a URL).

Profiler will save cache grinds to /tmp/ directory. You can use webbgrid to analyse it. It’s a very useful tool and it doesn’t require any configuration.

1. PHP accelerator

It has been iterated many times and I’m not very innovative here. This is the starting point. You can use APC or Zend Optimizer Plus. Bare in mind that the default configuration is not the fastest one. Both extensions will be looking into file timestamp to make sure the cache is up to date. Code doesn’t change spontaneously in a production environment so you can disable that feature. It will eliminate the slow fstat() calls but you will have to restart all web servers after every release.

2. Autoload with the Composer

Composer is designed to manage dependencies but it can also help with the performance. Even if a code is cached by an accelerator your framework can still try to call functions like “is_readable” and loop through the include_path. I had this particular problem with Zend Framework 1.12.1. You can quickly fix it by editing the composer.json file.

Classmap is an array so you can keep adding new paths. Run the composer to re-generate the autoloader.

The classmap is going to be cached by the APC so autoloading should get much faster.

3. Serialiaze heavy objects

Instantiating an object with a complex relationship might take a significant amount of time. A good example is Zend Framework’s router. The router is a collection of routes. Every route is an instance of “Zend_Controller_Router_Route”. Instantiating a route means executing route’s constructor (for every instance). Certain routes might be storing (chaining) other routes which adds to the execution time. Not to mention a configuration is usually stored in an XML or INI file which needs to be opened and parsed in the first place.

If the component is hermetic and doesn’t affect the environment you can skip the execution process and cache serialized object. Our goal is performance so cache it in RAM. When the instance is needed acquire it from a cache and unserialize it. Obviously the unserialize function doesn’t come without a cost. There are some opinions it can be sometimes slower than the normal approach. It might also vary accross different PHP versions. You can easly measure it with the mirotime function:

In the router example I got a 10% improvement just with that one change.

It’s not difficult to achieve a major performance improvement on a not optimised code. The key to success is to measure everything and apply the 80-20 rule (80% of problems are in the 20% of code). XDebug profiler will instantly highlight where are the problems. Remember to profile against your production config.

6 Comments

  1. Wojtek
    23/05/2013

    A easy way to improve your Zend 1 Routing is to (if possible) split your app in main index.php and load different, module-specific Application class with different set of routes. It also means that adding new routes will not slow down other parts of your app!

    APC is your friend – but be careful, and always benchmark your changes. Fetching objects from APC can take more then 5ms in some cases, it might be more then initialising PHP object with config array retrieved from APC.

    Also, it’s good to make sure that there are no require_once in your code, there is a lot of it in Zend 1. Even with apc.stat = 0 require once will hit disk and stat the file!

    And finally, use XDebug regularly on your production environment! :)

    Reply
    • Lukasz Kujawa
      24/05/2013

      Hello Wojtek, thank you for your comment. Breaking routes is a good idea but for this particular application hard to do. We need routes from various modules to build links with the URL helper.

      Could you please tell more about the issue with APC? 5ms is indeed very slow for pulling data form memory. Is it because of concurrency and locking or there are some other reasons?

      Reply
    • mixdev
      18/06/2013

      Won’t some overhead kick-in with XDebug on production?

      Reply
      • Lukasz Kujawa
        20/06/2013

        I guess that depends on settings. If you have profiling ON by default than you can expect trouble. If you have the remote debugging on there is a security issue. Appart fron that I wouldn’t think so but I have no data to support it so it’s only an assumption :-)

        Reply
  2. Mariusz Kujawski
    24/05/2013

    microtime(true)? :)

    Reply
    • Lukasz Kujawa
      24/05/2013

      Good point, thank you. I used to blindly copy that function for years.

      Reply

Leave a Reply